OLD | NEW |
1 #!/usr/bin/python | 1 #!/usr/bin/python |
2 """ | 2 """ |
3 Utility classes and functions to handle Virtual Machine creation using qemu. | 3 Utility classes and functions to handle Virtual Machine creation using qemu. |
4 | 4 |
5 @copyright: 2008-2009 Red Hat Inc. | 5 @copyright: 2008-2009 Red Hat Inc. |
6 """ | 6 """ |
7 | 7 |
8 import time, socket, os, logging, fcntl, re, commands, shelve, glob | 8 import time, os, logging, fcntl, re, commands, glob |
9 import kvm_utils, kvm_subprocess, kvm_monitor, rss_file_transfer | 9 import kvm_utils, kvm_subprocess, kvm_monitor |
10 from autotest_lib.client.common_lib import error | 10 from autotest_lib.client.common_lib import error |
11 from autotest_lib.client.bin import utils | 11 from autotest_lib.client.bin import utils |
12 | 12 |
13 | 13 |
| 14 class VMError(Exception): |
| 15 pass |
| 16 |
| 17 |
| 18 class VMCreateError(VMError): |
| 19 def __init__(self, cmd, status, output): |
| 20 VMError.__init__(self, cmd, status, output) |
| 21 self.cmd = cmd |
| 22 self.status = status |
| 23 self.output = output |
| 24 |
| 25 def __str__(self): |
| 26 return ("VM creation command failed: %r (status: %s, " |
| 27 "output: %r)" % (self.cmd, self.status, self.output)) |
| 28 |
| 29 |
| 30 class VMHashMismatchError(VMError): |
| 31 def __init__(self, actual, expected): |
| 32 VMError.__init__(self, actual, expected) |
| 33 self.actual_hash = actual |
| 34 self.expected_hash = expected |
| 35 |
| 36 def __str__(self): |
| 37 return ("CD image hash (%s) differs from expected one (%s)" % |
| 38 (self.actual_hash, self.expected_hash)) |
| 39 |
| 40 |
| 41 class VMImageMissingError(VMError): |
| 42 def __init__(self, filename): |
| 43 VMError.__init__(self, filename) |
| 44 self.filename = filename |
| 45 |
| 46 def __str__(self): |
| 47 return "CD image file not found: %r" % self.filename |
| 48 |
| 49 |
| 50 class VMImageCheckError(VMError): |
| 51 def __init__(self, filename): |
| 52 VMError.__init__(self, filename) |
| 53 self.filename = filename |
| 54 |
| 55 def __str__(self): |
| 56 return "Errors found on image: %r" % self.filename |
| 57 |
| 58 |
| 59 class VMBadPATypeError(VMError): |
| 60 def __init__(self, pa_type): |
| 61 VMError.__init__(self, pa_type) |
| 62 self.pa_type = pa_type |
| 63 |
| 64 def __str__(self): |
| 65 return "Unsupported PCI assignable type: %r" % self.pa_type |
| 66 |
| 67 |
| 68 class VMPAError(VMError): |
| 69 def __init__(self, pa_type): |
| 70 VMError.__init__(self, pa_type) |
| 71 self.pa_type = pa_type |
| 72 |
| 73 def __str__(self): |
| 74 return ("No PCI assignable devices could be assigned " |
| 75 "(pci_assignable=%r)" % self.pa_type) |
| 76 |
| 77 |
| 78 class VMPostCreateError(VMError): |
| 79 def __init__(self, cmd, output): |
| 80 VMError.__init__(self, cmd, output) |
| 81 self.cmd = cmd |
| 82 self.output = output |
| 83 |
| 84 |
| 85 class VMHugePageError(VMPostCreateError): |
| 86 def __str__(self): |
| 87 return ("Cannot allocate hugepage memory (command: %r, " |
| 88 "output: %r)" % (self.cmd, self.output)) |
| 89 |
| 90 |
| 91 class VMKVMInitError(VMPostCreateError): |
| 92 def __str__(self): |
| 93 return ("Cannot initialize KVM (command: %r, output: %r)" % |
| 94 (self.cmd, self.output)) |
| 95 |
| 96 |
| 97 class VMDeadError(VMError): |
| 98 def __init__(self, status, output): |
| 99 VMError.__init__(self, status, output) |
| 100 self.status = status |
| 101 self.output = output |
| 102 |
| 103 def __str__(self): |
| 104 return ("VM process is dead (status: %s, output: %r)" % |
| 105 (self.status, self.output)) |
| 106 |
| 107 |
| 108 class VMAddressError(VMError): |
| 109 pass |
| 110 |
| 111 |
| 112 class VMPortNotRedirectedError(VMAddressError): |
| 113 def __init__(self, port): |
| 114 VMAddressError.__init__(self, port) |
| 115 self.port = port |
| 116 |
| 117 def __str__(self): |
| 118 return "Port not redirected: %s" % self.port |
| 119 |
| 120 |
| 121 class VMAddressVerificationError(VMAddressError): |
| 122 def __init__(self, mac, ip): |
| 123 VMAddressError.__init__(self, mac, ip) |
| 124 self.mac = mac |
| 125 self.ip = ip |
| 126 |
| 127 def __str__(self): |
| 128 return ("Cannot verify MAC-IP address mapping using arping: " |
| 129 "%s ---> %s" % (self.mac, self.ip)) |
| 130 |
| 131 |
| 132 class VMMACAddressMissingError(VMAddressError): |
| 133 def __init__(self, nic_index): |
| 134 VMAddressError.__init__(self, nic_index) |
| 135 self.nic_index = nic_index |
| 136 |
| 137 def __str__(self): |
| 138 return "No MAC address defined for NIC #%s" % self.nic_index |
| 139 |
| 140 |
| 141 class VMIPAddressMissingError(VMAddressError): |
| 142 def __init__(self, mac): |
| 143 VMAddressError.__init__(self, mac) |
| 144 self.mac = mac |
| 145 |
| 146 def __str__(self): |
| 147 return "Cannot find IP address for MAC address %s" % self.mac |
| 148 |
| 149 |
| 150 class VMMigrateError(VMError): |
| 151 pass |
| 152 |
| 153 |
| 154 class VMMigrateTimeoutError(VMMigrateError): |
| 155 pass |
| 156 |
| 157 |
| 158 class VMMigrateCancelError(VMMigrateError): |
| 159 pass |
| 160 |
| 161 |
| 162 class VMMigrateFailedError(VMMigrateError): |
| 163 pass |
| 164 |
| 165 |
| 166 class VMMigrateStateMismatchError(VMMigrateError): |
| 167 def __init__(self, src_hash, dst_hash): |
| 168 VMMigrateError.__init__(self, src_hash, dst_hash) |
| 169 self.src_hash = src_hash |
| 170 self.dst_hash = dst_hash |
| 171 |
| 172 def __str__(self): |
| 173 return ("Mismatch of VM state before and after migration (%s != %s)" % |
| 174 (self.src_hash, self.dst_hash)) |
| 175 |
| 176 |
| 177 class VMRebootError(VMError): |
| 178 pass |
| 179 |
| 180 |
14 def get_image_filename(params, root_dir): | 181 def get_image_filename(params, root_dir): |
15 """ | 182 """ |
16 Generate an image path from params and root_dir. | 183 Generate an image path from params and root_dir. |
17 | 184 |
18 @param params: Dictionary containing the test parameters. | 185 @param params: Dictionary containing the test parameters. |
19 @param root_dir: Base directory for relative filenames. | 186 @param root_dir: Base directory for relative filenames. |
20 | 187 |
21 @note: params should contain: | 188 @note: params should contain: |
22 image_name -- the name of the image file, without extension | 189 image_name -- the name of the image file, without extension |
23 image_format -- the format of the image (qcow2, raw etc) | 190 image_format -- the format of the image (qcow2, raw etc) |
24 """ | 191 """ |
25 image_name = params.get("image_name", "image") | 192 image_name = params.get("image_name", "image") |
26 image_format = params.get("image_format", "qcow2") | 193 image_format = params.get("image_format", "qcow2") |
| 194 if params.get("image_raw_device") == "yes": |
| 195 return image_name |
27 image_filename = "%s.%s" % (image_name, image_format) | 196 image_filename = "%s.%s" % (image_name, image_format) |
28 image_filename = kvm_utils.get_path(root_dir, image_filename) | 197 image_filename = kvm_utils.get_path(root_dir, image_filename) |
29 return image_filename | 198 return image_filename |
30 | 199 |
31 | 200 |
32 def create_image(params, root_dir): | 201 def create_image(params, root_dir): |
33 """ | 202 """ |
34 Create an image using qemu_image. | 203 Create an image using qemu_image. |
35 | 204 |
36 @param params: Dictionary containing the test parameters. | 205 @param params: Dictionary containing the test parameters. |
(...skipping 11 matching lines...) Expand all Loading... |
48 | 217 |
49 format = params.get("image_format", "qcow2") | 218 format = params.get("image_format", "qcow2") |
50 qemu_img_cmd += " -f %s" % format | 219 qemu_img_cmd += " -f %s" % format |
51 | 220 |
52 image_filename = get_image_filename(params, root_dir) | 221 image_filename = get_image_filename(params, root_dir) |
53 qemu_img_cmd += " %s" % image_filename | 222 qemu_img_cmd += " %s" % image_filename |
54 | 223 |
55 size = params.get("image_size", "10G") | 224 size = params.get("image_size", "10G") |
56 qemu_img_cmd += " %s" % size | 225 qemu_img_cmd += " %s" % size |
57 | 226 |
58 try: | 227 utils.system(qemu_img_cmd) |
59 utils.system(qemu_img_cmd) | 228 logging.info("Image created in %r", image_filename) |
60 except error.CmdError, e: | |
61 logging.error("Could not create image; qemu-img command failed:\n%s", | |
62 str(e)) | |
63 return None | |
64 | |
65 if not os.path.exists(image_filename): | |
66 logging.error("Image could not be created for some reason; " | |
67 "qemu-img command:\n%s" % qemu_img_cmd) | |
68 return None | |
69 | |
70 logging.info("Image created in %s" % image_filename) | |
71 return image_filename | 229 return image_filename |
72 | 230 |
73 | 231 |
74 def remove_image(params, root_dir): | 232 def remove_image(params, root_dir): |
75 """ | 233 """ |
76 Remove an image file. | 234 Remove an image file. |
77 | 235 |
78 @param params: A dict | 236 @param params: A dict |
79 @param root_dir: Base directory for relative filenames. | 237 @param root_dir: Base directory for relative filenames. |
80 | 238 |
81 @note: params should contain: | 239 @note: params should contain: |
82 image_name -- the name of the image file, without extension | 240 image_name -- the name of the image file, without extension |
83 image_format -- the format of the image (qcow2, raw etc) | 241 image_format -- the format of the image (qcow2, raw etc) |
84 """ | 242 """ |
85 image_filename = get_image_filename(params, root_dir) | 243 image_filename = get_image_filename(params, root_dir) |
86 logging.debug("Removing image file %s..." % image_filename) | 244 logging.debug("Removing image file %s...", image_filename) |
87 if os.path.exists(image_filename): | 245 if os.path.exists(image_filename): |
88 os.unlink(image_filename) | 246 os.unlink(image_filename) |
89 else: | 247 else: |
90 logging.debug("Image file %s not found") | 248 logging.debug("Image file %s not found") |
91 | 249 |
92 | 250 |
| 251 def check_image(params, root_dir): |
| 252 """ |
| 253 Check an image using qemu-img. |
| 254 |
| 255 @param params: Dictionary containing the test parameters. |
| 256 @param root_dir: Base directory for relative filenames. |
| 257 |
| 258 @note: params should contain: |
| 259 image_name -- the name of the image file, without extension |
| 260 image_format -- the format of the image (qcow2, raw etc) |
| 261 |
| 262 @raise VMImageCheckError: In case qemu-img check fails on the image. |
| 263 """ |
| 264 image_filename = get_image_filename(params, root_dir) |
| 265 logging.debug("Checking image file %s...", image_filename) |
| 266 qemu_img_cmd = kvm_utils.get_path(root_dir, |
| 267 params.get("qemu_img_binary", "qemu-img")) |
| 268 image_is_qcow2 = params.get("image_format") == 'qcow2' |
| 269 if os.path.exists(image_filename) and image_is_qcow2: |
| 270 # Verifying if qemu-img supports 'check' |
| 271 q_result = utils.run(qemu_img_cmd, ignore_status=True) |
| 272 q_output = q_result.stdout |
| 273 check_img = True |
| 274 if not "check" in q_output: |
| 275 logging.error("qemu-img does not support 'check', " |
| 276 "skipping check...") |
| 277 check_img = False |
| 278 if not "info" in q_output: |
| 279 logging.error("qemu-img does not support 'info', " |
| 280 "skipping check...") |
| 281 check_img = False |
| 282 if check_img: |
| 283 try: |
| 284 utils.system("%s info %s" % (qemu_img_cmd, image_filename)) |
| 285 except error.CmdError: |
| 286 logging.error("Error getting info from image %s", |
| 287 image_filename) |
| 288 try: |
| 289 utils.system("%s check %s" % (qemu_img_cmd, image_filename)) |
| 290 except error.CmdError: |
| 291 raise VMImageCheckError(image_filename) |
| 292 |
| 293 else: |
| 294 if not os.path.exists(image_filename): |
| 295 logging.debug("Image file %s not found, skipping check...", |
| 296 image_filename) |
| 297 elif not image_is_qcow2: |
| 298 logging.debug("Image file %s not qcow2, skipping check...", |
| 299 image_filename) |
| 300 |
| 301 |
93 class VM: | 302 class VM: |
94 """ | 303 """ |
95 This class handles all basic VM operations. | 304 This class handles all basic VM operations. |
96 """ | 305 """ |
97 | 306 |
98 def __init__(self, name, params, root_dir, address_cache): | 307 def __init__(self, name, params, root_dir, address_cache, state=None): |
99 """ | 308 """ |
100 Initialize the object and set a few attributes. | 309 Initialize the object and set a few attributes. |
101 | 310 |
102 @param name: The name of the object | 311 @param name: The name of the object |
103 @param params: A dict containing VM params | 312 @param params: A dict containing VM params |
104 (see method make_qemu_command for a full description) | 313 (see method make_qemu_command for a full description) |
105 @param root_dir: Base directory for relative filenames | 314 @param root_dir: Base directory for relative filenames |
106 @param address_cache: A dict that maps MAC addresses to IP addresses | 315 @param address_cache: A dict that maps MAC addresses to IP addresses |
| 316 @param state: If provided, use this as self.__dict__ |
107 """ | 317 """ |
108 self.process = None | 318 if state: |
109 self.serial_console = None | 319 self.__dict__ = state |
110 self.redirs = {} | 320 else: |
111 self.vnc_port = 5900 | 321 self.process = None |
112 self.monitors = [] | 322 self.serial_console = None |
113 self.pci_assignable = None | 323 self.redirs = {} |
114 self.netdev_id = [] | 324 self.vnc_port = 5900 |
115 self.uuid = None | 325 self.monitors = [] |
| 326 self.pci_assignable = None |
| 327 self.netdev_id = [] |
| 328 self.uuid = None |
| 329 |
| 330 # Find a unique identifier for this VM |
| 331 while True: |
| 332 self.instance = (time.strftime("%Y%m%d-%H%M%S-") + |
| 333 kvm_utils.generate_random_string(4)) |
| 334 if not glob.glob("/tmp/*%s" % self.instance): |
| 335 break |
116 | 336 |
117 self.name = name | 337 self.name = name |
118 self.params = params | 338 self.params = params |
119 self.root_dir = root_dir | 339 self.root_dir = root_dir |
120 self.address_cache = address_cache | 340 self.address_cache = address_cache |
121 | 341 |
122 # Find a unique identifier for this VM | |
123 while True: | |
124 self.instance = (time.strftime("%Y%m%d-%H%M%S-") + | |
125 kvm_utils.generate_random_string(4)) | |
126 if not glob.glob("/tmp/*%s" % self.instance): | |
127 break | |
128 | 342 |
129 | 343 def clone(self, name=None, params=None, root_dir=None, address_cache=None, |
130 def clone(self, name=None, params=None, root_dir=None, address_cache=None): | 344 copy_state=False): |
131 """ | 345 """ |
132 Return a clone of the VM object with optionally modified parameters. | 346 Return a clone of the VM object with optionally modified parameters. |
133 The clone is initially not alive and needs to be started using create(). | 347 The clone is initially not alive and needs to be started using create(). |
134 Any parameters not passed to this function are copied from the source | 348 Any parameters not passed to this function are copied from the source |
135 VM. | 349 VM. |
136 | 350 |
137 @param name: Optional new VM name | 351 @param name: Optional new VM name |
138 @param params: Optional new VM creation parameters | 352 @param params: Optional new VM creation parameters |
139 @param root_dir: Optional new base directory for relative filenames | 353 @param root_dir: Optional new base directory for relative filenames |
140 @param address_cache: A dict that maps MAC addresses to IP addresses | 354 @param address_cache: A dict that maps MAC addresses to IP addresses |
| 355 @param copy_state: If True, copy the original VM's state to the clone. |
| 356 Mainly useful for make_qemu_command(). |
141 """ | 357 """ |
142 if name is None: | 358 if name is None: |
143 name = self.name | 359 name = self.name |
144 if params is None: | 360 if params is None: |
145 params = self.params.copy() | 361 params = self.params.copy() |
146 if root_dir is None: | 362 if root_dir is None: |
147 root_dir = self.root_dir | 363 root_dir = self.root_dir |
148 if address_cache is None: | 364 if address_cache is None: |
149 address_cache = self.address_cache | 365 address_cache = self.address_cache |
150 return VM(name, params, root_dir, address_cache) | 366 if copy_state: |
| 367 state = self.__dict__.copy() |
| 368 else: |
| 369 state = None |
| 370 return VM(name, params, root_dir, address_cache, state) |
151 | 371 |
152 | 372 |
153 def make_qemu_command(self, name=None, params=None, root_dir=None): | 373 def make_qemu_command(self, name=None, params=None, root_dir=None): |
154 """ | 374 """ |
155 Generate a qemu command line. All parameters are optional. If a | 375 Generate a qemu command line. All parameters are optional. If a |
156 parameter is not supplied, the corresponding value stored in the | 376 parameter is not supplied, the corresponding value stored in the |
157 class attributes is used. | 377 class attributes is used. |
158 | 378 |
159 @param name: The name of the object | 379 @param name: The name of the object |
160 @param params: A dict containing VM params | 380 @param params: A dict containing VM params |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
218 if has_option(help, "drive"): | 438 if has_option(help, "drive"): |
219 cmd = " -drive file='%s',media=cdrom" % filename | 439 cmd = " -drive file='%s',media=cdrom" % filename |
220 if index is not None: cmd += ",index=%s" % index | 440 if index is not None: cmd += ",index=%s" % index |
221 return cmd | 441 return cmd |
222 else: | 442 else: |
223 return " -cdrom '%s'" % filename | 443 return " -cdrom '%s'" % filename |
224 | 444 |
225 def add_drive(help, filename, index=None, format=None, cache=None, | 445 def add_drive(help, filename, index=None, format=None, cache=None, |
226 werror=None, serial=None, snapshot=False, boot=False): | 446 werror=None, serial=None, snapshot=False, boot=False): |
227 cmd = " -drive file='%s'" % filename | 447 cmd = " -drive file='%s'" % filename |
228 if index is not None: cmd += ",index=%s" % index | 448 if index is not None: |
229 if format: cmd += ",if=%s" % format | 449 cmd += ",index=%s" % index |
230 if cache: cmd += ",cache=%s" % cache | 450 if format: |
231 if werror: cmd += ",werror=%s" % werror | 451 cmd += ",if=%s" % format |
232 if serial: cmd += ",serial='%s'" % serial | 452 if cache: |
233 if snapshot: cmd += ",snapshot=on" | 453 cmd += ",cache=%s" % cache |
234 if boot: cmd += ",boot=on" | 454 if werror: |
| 455 cmd += ",werror=%s" % werror |
| 456 if serial: |
| 457 cmd += ",serial='%s'" % serial |
| 458 if snapshot: |
| 459 cmd += ",snapshot=on" |
| 460 if boot: |
| 461 cmd += ",boot=on" |
235 return cmd | 462 return cmd |
236 | 463 |
237 def add_nic(help, vlan, model=None, mac=None, netdev_id=None, | 464 def add_nic(help, vlan, model=None, mac=None, netdev_id=None, |
238 nic_extra_params=None): | 465 nic_extra_params=None): |
| 466 if has_option(help, "netdev"): |
| 467 netdev_vlan_str = ",netdev=%s" % netdev_id |
| 468 else: |
| 469 netdev_vlan_str = ",vlan=%d" % vlan |
239 if has_option(help, "device"): | 470 if has_option(help, "device"): |
240 if model == "virtio": | |
241 model="virtio-net-pci" | |
242 if not model: | 471 if not model: |
243 model= "rtl8139" | 472 model = "rtl8139" |
244 cmd = " -device %s" % model | 473 elif model == "virtio": |
| 474 model = "virtio-net-pci" |
| 475 cmd = " -device %s" % model + netdev_vlan_str |
245 if mac: | 476 if mac: |
246 cmd += ",mac=%s" % mac | 477 cmd += ",mac='%s'" % mac |
247 if has_option(help, "netdev"): | |
248 cmd += ",netdev=%s" % netdev_id | |
249 else: | |
250 cmd += "vlan=%d," % vlan | |
251 if nic_extra_params: | 478 if nic_extra_params: |
252 cmd += ",%s" % nic_extra_params | 479 cmd += ",%s" % nic_extra_params |
253 else: | 480 else: |
254 if has_option(help, "netdev"): | 481 cmd = " -net nic" + netdev_vlan_str |
255 cmd = " -net nic,netdev=%s" % netdev_id | |
256 else: | |
257 cmd = " -net nic,vlan=%d" % vlan | |
258 if model: | 482 if model: |
259 cmd += ",model=%s" % model | 483 cmd += ",model=%s" % model |
260 if mac: | 484 if mac: |
261 cmd += ",macaddr='%s'" % mac | 485 cmd += ",macaddr='%s'" % mac |
262 return cmd | 486 return cmd |
263 | 487 |
264 def add_net(help, vlan, mode, ifname=None, script=None, | 488 def add_net(help, vlan, mode, ifname=None, script=None, |
265 downscript=None, tftp=None, bootfile=None, hostfwd=[], | 489 downscript=None, tftp=None, bootfile=None, hostfwd=[], |
266 netdev_id=None, vhost=False): | 490 netdev_id=None, netdev_extra_params=None): |
267 if has_option(help, "netdev"): | 491 if has_option(help, "netdev"): |
268 cmd = " -netdev %s,id=%s" % (mode, netdev_id) | 492 cmd = " -netdev %s,id=%s" % (mode, netdev_id) |
269 if vhost: | 493 if netdev_extra_params: |
270 cmd +=",vhost=on" | 494 cmd += ",%s" % netdev_extra_params |
271 else: | 495 else: |
272 cmd = " -net %s,vlan=%d" % (mode, vlan) | 496 cmd = " -net %s,vlan=%d" % (mode, vlan) |
273 if mode == "tap": | 497 if mode == "tap": |
274 if ifname: cmd += ",ifname='%s'" % ifname | 498 if ifname: cmd += ",ifname='%s'" % ifname |
275 if script: cmd += ",script='%s'" % script | 499 if script: cmd += ",script='%s'" % script |
276 cmd += ",downscript='%s'" % (downscript or "no") | 500 cmd += ",downscript='%s'" % (downscript or "no") |
277 elif mode == "user": | 501 elif mode == "user": |
278 if tftp and "[,tftp=" in help: | 502 if tftp and "[,tftp=" in help: |
279 cmd += ",tftp='%s'" % tftp | 503 cmd += ",tftp='%s'" % tftp |
280 if bootfile and "[,bootfile=" in help: | 504 if bootfile and "[,bootfile=" in help: |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
340 " -device testdev,chardev=testlog" % filename) | 564 " -device testdev,chardev=testlog" % filename) |
341 | 565 |
342 def add_no_hpet(help): | 566 def add_no_hpet(help): |
343 if has_option(help, "no-hpet"): | 567 if has_option(help, "no-hpet"): |
344 return " -no-hpet" | 568 return " -no-hpet" |
345 else: | 569 else: |
346 return "" | 570 return "" |
347 | 571 |
348 # End of command line option wrappers | 572 # End of command line option wrappers |
349 | 573 |
350 if name is None: name = self.name | 574 if name is None: |
351 if params is None: params = self.params | 575 name = self.name |
352 if root_dir is None: root_dir = self.root_dir | 576 if params is None: |
| 577 params = self.params |
| 578 if root_dir is None: |
| 579 root_dir = self.root_dir |
| 580 |
| 581 # Clone this VM using the new params |
| 582 vm = self.clone(name, params, root_dir, copy_state=True) |
353 | 583 |
354 qemu_binary = kvm_utils.get_path(root_dir, params.get("qemu_binary", | 584 qemu_binary = kvm_utils.get_path(root_dir, params.get("qemu_binary", |
355 "qemu")) | 585 "qemu")) |
356 # Get the output of 'qemu -help' (log a message in case this call never | 586 # Get the output of 'qemu -help' (log a message in case this call never |
357 # returns or causes some other kind of trouble) | 587 # returns or causes some other kind of trouble) |
358 logging.debug("Getting output of 'qemu -help'") | 588 logging.debug("Getting output of 'qemu -help'") |
359 help = commands.getoutput("%s -help" % qemu_binary) | 589 help = commands.getoutput("%s -help" % qemu_binary) |
360 | 590 |
361 # Start constructing the qemu command | 591 # Start constructing the qemu command |
362 qemu_cmd = "" | 592 qemu_cmd = "" |
363 # Set the X11 display parameter if requested | 593 # Set the X11 display parameter if requested |
364 if params.get("x11_display"): | 594 if params.get("x11_display"): |
365 qemu_cmd += "DISPLAY=%s " % params.get("x11_display") | 595 qemu_cmd += "DISPLAY=%s " % params.get("x11_display") |
366 # Add the qemu binary | 596 # Add the qemu binary |
367 qemu_cmd += qemu_binary | 597 qemu_cmd += qemu_binary |
368 # Add the VM's name | 598 # Add the VM's name |
369 qemu_cmd += add_name(help, name) | 599 qemu_cmd += add_name(help, name) |
370 # Add monitors | 600 # Add monitors |
371 for monitor_name in kvm_utils.get_sub_dict_names(params, "monitors"): | 601 for monitor_name in params.objects("monitors"): |
372 monitor_params = kvm_utils.get_sub_dict(params, monitor_name) | 602 monitor_params = params.object_params(monitor_name) |
373 monitor_filename = self.get_monitor_filename(monitor_name) | 603 monitor_filename = vm.get_monitor_filename(monitor_name) |
374 if monitor_params.get("monitor_type") == "qmp": | 604 if monitor_params.get("monitor_type") == "qmp": |
375 qemu_cmd += add_qmp_monitor(help, monitor_filename) | 605 qemu_cmd += add_qmp_monitor(help, monitor_filename) |
376 else: | 606 else: |
377 qemu_cmd += add_human_monitor(help, monitor_filename) | 607 qemu_cmd += add_human_monitor(help, monitor_filename) |
378 | 608 |
379 # Add serial console redirection | 609 # Add serial console redirection |
380 qemu_cmd += add_serial(help, self.get_serial_console_filename()) | 610 qemu_cmd += add_serial(help, vm.get_serial_console_filename()) |
381 | 611 |
382 for image_name in kvm_utils.get_sub_dict_names(params, "images"): | 612 for image_name in params.objects("images"): |
383 image_params = kvm_utils.get_sub_dict(params, image_name) | 613 image_params = params.object_params(image_name) |
384 if image_params.get("boot_drive") == "no": | 614 if image_params.get("boot_drive") == "no": |
385 continue | 615 continue |
386 qemu_cmd += add_drive(help, | 616 qemu_cmd += add_drive(help, |
387 get_image_filename(image_params, root_dir), | 617 get_image_filename(image_params, root_dir), |
388 image_params.get("drive_index"), | 618 image_params.get("drive_index"), |
389 image_params.get("drive_format"), | 619 image_params.get("drive_format"), |
390 image_params.get("drive_cache"), | 620 image_params.get("drive_cache"), |
391 image_params.get("drive_werror"), | 621 image_params.get("drive_werror"), |
392 image_params.get("drive_serial"), | 622 image_params.get("drive_serial"), |
393 image_params.get("image_snapshot") == "yes", | 623 image_params.get("image_snapshot") == "yes", |
394 image_params.get("image_boot") == "yes") | 624 image_params.get("image_boot") == "yes") |
395 | 625 |
396 redirs = [] | 626 redirs = [] |
397 for redir_name in kvm_utils.get_sub_dict_names(params, "redirs"): | 627 for redir_name in params.objects("redirs"): |
398 redir_params = kvm_utils.get_sub_dict(params, redir_name) | 628 redir_params = params.object_params(redir_name) |
399 guest_port = int(redir_params.get("guest_port")) | 629 guest_port = int(redir_params.get("guest_port")) |
400 host_port = self.redirs.get(guest_port) | 630 host_port = vm.redirs.get(guest_port) |
401 redirs += [(host_port, guest_port)] | 631 redirs += [(host_port, guest_port)] |
402 | 632 |
403 vlan = 0 | 633 vlan = 0 |
404 for nic_name in kvm_utils.get_sub_dict_names(params, "nics"): | 634 for nic_name in params.objects("nics"): |
405 nic_params = kvm_utils.get_sub_dict(params, nic_name) | 635 nic_params = params.object_params(nic_name) |
| 636 try: |
| 637 netdev_id = vm.netdev_id[vlan] |
| 638 except IndexError: |
| 639 netdev_id = None |
406 # Handle the '-net nic' part | 640 # Handle the '-net nic' part |
407 mac = self.get_mac_address(vlan) | 641 mac = vm.get_mac_address(vlan) |
408 qemu_cmd += add_nic(help, vlan, nic_params.get("nic_model"), mac, | 642 qemu_cmd += add_nic(help, vlan, nic_params.get("nic_model"), mac, |
409 self.netdev_id[vlan], | 643 netdev_id, nic_params.get("nic_extra_params")) |
410 nic_params.get("nic_extra_params")) | |
411 # Handle the '-net tap' or '-net user' part | 644 # Handle the '-net tap' or '-net user' part |
412 script = nic_params.get("nic_script") | 645 script = nic_params.get("nic_script") |
413 downscript = nic_params.get("nic_downscript") | 646 downscript = nic_params.get("nic_downscript") |
414 tftp = nic_params.get("tftp") | 647 tftp = nic_params.get("tftp") |
415 if script: | 648 if script: |
416 script = kvm_utils.get_path(root_dir, script) | 649 script = kvm_utils.get_path(root_dir, script) |
417 if downscript: | 650 if downscript: |
418 downscript = kvm_utils.get_path(root_dir, downscript) | 651 downscript = kvm_utils.get_path(root_dir, downscript) |
419 if tftp: | 652 if tftp: |
420 tftp = kvm_utils.get_path(root_dir, tftp) | 653 tftp = kvm_utils.get_path(root_dir, tftp) |
421 qemu_cmd += add_net(help, vlan, nic_params.get("nic_mode", "user"), | 654 qemu_cmd += add_net(help, vlan, nic_params.get("nic_mode", "user"), |
422 self.get_ifname(vlan), | 655 vm.get_ifname(vlan), |
423 script, downscript, tftp, | 656 script, downscript, tftp, |
424 nic_params.get("bootp"), redirs, | 657 nic_params.get("bootp"), redirs, netdev_id, |
425 self.netdev_id[vlan], | 658 nic_params.get("netdev_extra_params")) |
426 nic_params.get("vhost")=="yes") | |
427 # Proceed to next NIC | 659 # Proceed to next NIC |
428 vlan += 1 | 660 vlan += 1 |
429 | 661 |
430 mem = params.get("mem") | 662 mem = params.get("mem") |
431 if mem: | 663 if mem: |
432 qemu_cmd += add_mem(help, mem) | 664 qemu_cmd += add_mem(help, mem) |
433 | 665 |
434 smp = params.get("smp") | 666 smp = params.get("smp") |
435 if smp: | 667 if smp: |
436 qemu_cmd += add_smp(help, smp) | 668 qemu_cmd += add_smp(help, smp) |
437 | 669 |
438 cdroms = kvm_utils.get_sub_dict_names(params, "cdroms") | 670 for cdrom in params.objects("cdroms"): |
439 for cdrom in cdroms: | 671 cdrom_params = params.object_params(cdrom) |
440 cdrom_params = kvm_utils.get_sub_dict(params, cdrom) | |
441 iso = cdrom_params.get("cdrom") | 672 iso = cdrom_params.get("cdrom") |
442 if iso: | 673 if iso: |
443 qemu_cmd += add_cdrom(help, kvm_utils.get_path(root_dir, iso), | 674 qemu_cmd += add_cdrom(help, kvm_utils.get_path(root_dir, iso), |
444 cdrom_params.get("drive_index")) | 675 cdrom_params.get("drive_index")) |
445 | 676 |
446 # We may want to add {floppy_otps} parameter for -fda | 677 # We may want to add {floppy_otps} parameter for -fda |
447 # {fat:floppy:}/path/. However vvfat is not usually recommended. | 678 # {fat:floppy:}/path/. However vvfat is not usually recommended. |
448 floppy = params.get("floppy") | 679 floppy = params.get("floppy") |
449 if floppy: | 680 if floppy: |
450 floppy = kvm_utils.get_path(root_dir, floppy) | 681 floppy = kvm_utils.get_path(root_dir, floppy) |
(...skipping 19 matching lines...) Expand all Loading... |
470 | 701 |
471 initrd = params.get("initrd") | 702 initrd = params.get("initrd") |
472 if initrd: | 703 if initrd: |
473 initrd = kvm_utils.get_path(root_dir, initrd) | 704 initrd = kvm_utils.get_path(root_dir, initrd) |
474 qemu_cmd += add_initrd(help, initrd) | 705 qemu_cmd += add_initrd(help, initrd) |
475 | 706 |
476 for host_port, guest_port in redirs: | 707 for host_port, guest_port in redirs: |
477 qemu_cmd += add_tcp_redir(help, host_port, guest_port) | 708 qemu_cmd += add_tcp_redir(help, host_port, guest_port) |
478 | 709 |
479 if params.get("display") == "vnc": | 710 if params.get("display") == "vnc": |
480 qemu_cmd += add_vnc(help, self.vnc_port) | 711 qemu_cmd += add_vnc(help, vm.vnc_port) |
481 elif params.get("display") == "sdl": | 712 elif params.get("display") == "sdl": |
482 qemu_cmd += add_sdl(help) | 713 qemu_cmd += add_sdl(help) |
483 elif params.get("display") == "nographic": | 714 elif params.get("display") == "nographic": |
484 qemu_cmd += add_nographic(help) | 715 qemu_cmd += add_nographic(help) |
485 | 716 |
486 if params.get("uuid") == "random": | 717 if params.get("uuid") == "random": |
487 qemu_cmd += add_uuid(help, self.uuid) | 718 qemu_cmd += add_uuid(help, vm.uuid) |
488 elif params.get("uuid"): | 719 elif params.get("uuid"): |
489 qemu_cmd += add_uuid(help, params.get("uuid")) | 720 qemu_cmd += add_uuid(help, params.get("uuid")) |
490 | 721 |
491 if params.get("testdev") == "yes": | 722 if params.get("testdev") == "yes": |
492 qemu_cmd += add_testdev(help, self.get_testlog_filename()) | 723 qemu_cmd += add_testdev(help, vm.get_testlog_filename()) |
493 | 724 |
494 if params.get("disable_hpet") == "yes": | 725 if params.get("disable_hpet") == "yes": |
495 qemu_cmd += add_no_hpet(help) | 726 qemu_cmd += add_no_hpet(help) |
496 | 727 |
497 # If the PCI assignment step went OK, add each one of the PCI assigned | 728 # If the PCI assignment step went OK, add each one of the PCI assigned |
498 # devices to the qemu command line. | 729 # devices to the qemu command line. |
499 if self.pci_assignable: | 730 if vm.pci_assignable: |
500 for pci_id in self.pa_pci_ids: | 731 for pci_id in vm.pa_pci_ids: |
501 qemu_cmd += add_pcidevice(help, pci_id) | 732 qemu_cmd += add_pcidevice(help, pci_id) |
502 | 733 |
503 extra_params = params.get("extra_params") | 734 extra_params = params.get("extra_params") |
504 if extra_params: | 735 if extra_params: |
505 qemu_cmd += " %s" % extra_params | 736 qemu_cmd += " %s" % extra_params |
506 | 737 |
507 return qemu_cmd | 738 return qemu_cmd |
508 | 739 |
509 | 740 |
| 741 @error.context_aware |
510 def create(self, name=None, params=None, root_dir=None, timeout=5.0, | 742 def create(self, name=None, params=None, root_dir=None, timeout=5.0, |
511 migration_mode=None, migration_exec_cmd=None, mac_source=None): | 743 migration_mode=None, mac_source=None): |
512 """ | 744 """ |
513 Start the VM by running a qemu command. | 745 Start the VM by running a qemu command. |
514 All parameters are optional. If name, params or root_dir are not | 746 All parameters are optional. If name, params or root_dir are not |
515 supplied, the respective values stored as class attributes are used. | 747 supplied, the respective values stored as class attributes are used. |
516 | 748 |
517 @param name: The name of the object | 749 @param name: The name of the object |
518 @param params: A dict containing VM params | 750 @param params: A dict containing VM params |
519 @param root_dir: Base directory for relative filenames | 751 @param root_dir: Base directory for relative filenames |
520 @param migration_mode: If supplied, start VM for incoming migration | 752 @param migration_mode: If supplied, start VM for incoming migration |
521 using this protocol (either 'tcp', 'unix' or 'exec') | 753 using this protocol (either 'tcp', 'unix' or 'exec') |
522 @param migration_exec_cmd: Command to embed in '-incoming "exec: ..."' | 754 @param migration_exec_cmd: Command to embed in '-incoming "exec: ..."' |
523 (e.g. 'gzip -c -d filename') if migration_mode is 'exec' | 755 (e.g. 'gzip -c -d filename') if migration_mode is 'exec' |
524 @param mac_source: A VM object from which to copy MAC addresses. If not | 756 @param mac_source: A VM object from which to copy MAC addresses. If not |
525 specified, new addresses will be generated. | 757 specified, new addresses will be generated. |
| 758 |
| 759 @raise VMCreateError: If qemu terminates unexpectedly |
| 760 @raise VMKVMInitError: If KVM initialization fails |
| 761 @raise VMHugePageError: If hugepage initialization fails |
| 762 @raise VMImageMissingError: If a CD image is missing |
| 763 @raise VMHashMismatchError: If a CD image hash has doesn't match the |
| 764 expected hash |
| 765 @raise VMBadPATypeError: If an unsupported PCI assignment type is |
| 766 requested |
| 767 @raise VMPAError: If no PCI assignable devices could be assigned |
526 """ | 768 """ |
527 self.destroy() | 769 error.context("creating '%s'" % self.name) |
| 770 self.destroy(free_mac_addresses=False) |
528 | 771 |
529 if name is not None: | 772 if name is not None: |
530 self.name = name | 773 self.name = name |
531 if params is not None: | 774 if params is not None: |
532 self.params = params | 775 self.params = params |
533 if root_dir is not None: | 776 if root_dir is not None: |
534 self.root_dir = root_dir | 777 self.root_dir = root_dir |
535 name = self.name | 778 name = self.name |
536 params = self.params | 779 params = self.params |
537 root_dir = self.root_dir | 780 root_dir = self.root_dir |
538 | 781 |
539 # Verify the md5sum of the ISO image | 782 # Verify the md5sum of the ISO images |
540 iso = params.get("cdrom") | 783 for cdrom in params.objects("cdroms"): |
541 if iso: | 784 cdrom_params = params.object_params(cdrom) |
542 iso = kvm_utils.get_path(root_dir, iso) | 785 iso = cdrom_params.get("cdrom") |
543 if not os.path.exists(iso): | 786 if iso: |
544 logging.error("ISO file not found: %s" % iso) | 787 iso = kvm_utils.get_path(root_dir, iso) |
545 return False | 788 if not os.path.exists(iso): |
546 compare = False | 789 raise VMImageMissingError(iso) |
547 if params.get("md5sum_1m"): | 790 compare = False |
548 logging.debug("Comparing expected MD5 sum with MD5 sum of " | 791 if cdrom_params.get("md5sum_1m"): |
549 "first MB of ISO file...") | 792 logging.debug("Comparing expected MD5 sum with MD5 sum of " |
550 actual_hash = utils.hash_file(iso, 1048576, method="md5") | 793 "first MB of ISO file...") |
551 expected_hash = params.get("md5sum_1m") | 794 actual_hash = utils.hash_file(iso, 1048576, method="md5") |
552 compare = True | 795 expected_hash = cdrom_params.get("md5sum_1m") |
553 elif params.get("md5sum"): | 796 compare = True |
554 logging.debug("Comparing expected MD5 sum with MD5 sum of ISO " | 797 elif cdrom_params.get("md5sum"): |
555 "file...") | 798 logging.debug("Comparing expected MD5 sum with MD5 sum of " |
556 actual_hash = utils.hash_file(iso, method="md5") | 799 "ISO file...") |
557 expected_hash = params.get("md5sum") | 800 actual_hash = utils.hash_file(iso, method="md5") |
558 compare = True | 801 expected_hash = cdrom_params.get("md5sum") |
559 elif params.get("sha1sum"): | 802 compare = True |
560 logging.debug("Comparing expected SHA1 sum with SHA1 sum of " | 803 elif cdrom_params.get("sha1sum"): |
561 "ISO file...") | 804 logging.debug("Comparing expected SHA1 sum with SHA1 sum " |
562 actual_hash = utils.hash_file(iso, method="sha1") | 805 "of ISO file...") |
563 expected_hash = params.get("sha1sum") | 806 actual_hash = utils.hash_file(iso, method="sha1") |
564 compare = True | 807 expected_hash = cdrom_params.get("sha1sum") |
565 if compare: | 808 compare = True |
566 if actual_hash == expected_hash: | 809 if compare: |
567 logging.debug("Hashes match") | 810 if actual_hash == expected_hash: |
568 else: | 811 logging.debug("Hashes match") |
569 logging.error("Actual hash differs from expected one") | 812 else: |
570 return False | 813 raise VMHashMismatchError(actual_hash, expected_hash) |
571 | 814 |
572 # Make sure the following code is not executed by more than one thread | 815 # Make sure the following code is not executed by more than one thread |
573 # at the same time | 816 # at the same time |
574 lockfile = open("/tmp/kvm-autotest-vm-create.lock", "w+") | 817 lockfile = open("/tmp/kvm-autotest-vm-create.lock", "w+") |
575 fcntl.lockf(lockfile, fcntl.LOCK_EX) | 818 fcntl.lockf(lockfile, fcntl.LOCK_EX) |
576 | 819 |
577 try: | 820 try: |
578 # Handle port redirections | 821 # Handle port redirections |
579 redir_names = kvm_utils.get_sub_dict_names(params, "redirs") | 822 redir_names = params.objects("redirs") |
580 host_ports = kvm_utils.find_free_ports(5000, 6000, len(redir_names)) | 823 host_ports = kvm_utils.find_free_ports(5000, 6000, len(redir_names)) |
581 self.redirs = {} | 824 self.redirs = {} |
582 for i in range(len(redir_names)): | 825 for i in range(len(redir_names)): |
583 redir_params = kvm_utils.get_sub_dict(params, redir_names[i]) | 826 redir_params = params.object_params(redir_names[i]) |
584 guest_port = int(redir_params.get("guest_port")) | 827 guest_port = int(redir_params.get("guest_port")) |
585 self.redirs[guest_port] = host_ports[i] | 828 self.redirs[guest_port] = host_ports[i] |
586 | 829 |
587 for nic in kvm_utils.get_sub_dict_names(params, "nics"): | 830 # Generate netdev IDs for all NICs |
| 831 self.netdev_id = [] |
| 832 for nic in params.objects("nics"): |
588 self.netdev_id.append(kvm_utils.generate_random_id()) | 833 self.netdev_id.append(kvm_utils.generate_random_id()) |
589 | 834 |
590 # Find available VNC port, if needed | 835 # Find available VNC port, if needed |
591 if params.get("display") == "vnc": | 836 if params.get("display") == "vnc": |
592 self.vnc_port = kvm_utils.find_free_port(5900, 6100) | 837 self.vnc_port = kvm_utils.find_free_port(5900, 6100) |
593 | 838 |
594 # Find random UUID if specified 'uuid = random' in config file | 839 # Find random UUID if specified 'uuid = random' in config file |
595 if params.get("uuid") == "random": | 840 if params.get("uuid") == "random": |
596 f = open("/proc/sys/kernel/random/uuid") | 841 f = open("/proc/sys/kernel/random/uuid") |
597 self.uuid = f.read().strip() | 842 self.uuid = f.read().strip() |
598 f.close() | 843 f.close() |
599 | 844 |
600 # Generate or copy MAC addresses for all NICs | 845 # Generate or copy MAC addresses for all NICs |
601 num_nics = len(kvm_utils.get_sub_dict_names(params, "nics")) | 846 num_nics = len(params.objects("nics")) |
602 for vlan in range(num_nics): | 847 for vlan in range(num_nics): |
603 mac = mac_source and mac_source.get_mac_address(vlan) | 848 nic_name = params.objects("nics")[vlan] |
604 if mac: | 849 nic_params = params.object_params(nic_name) |
| 850 if nic_params.get("nic_mac", None): |
| 851 mac = nic_params.get("nic_mac") |
605 kvm_utils.set_mac_address(self.instance, vlan, mac) | 852 kvm_utils.set_mac_address(self.instance, vlan, mac) |
606 else: | 853 else: |
607 kvm_utils.generate_mac_address(self.instance, vlan) | 854 mac = mac_source and mac_source.get_mac_address(vlan) |
| 855 if mac: |
| 856 kvm_utils.set_mac_address(self.instance, vlan, mac) |
| 857 else: |
| 858 kvm_utils.generate_mac_address(self.instance, vlan) |
608 | 859 |
609 # Assign a PCI assignable device | 860 # Assign a PCI assignable device |
610 self.pci_assignable = None | 861 self.pci_assignable = None |
611 pa_type = params.get("pci_assignable") | 862 pa_type = params.get("pci_assignable") |
612 if pa_type in ["vf", "pf", "mixed"]: | 863 if pa_type and pa_type != "no": |
613 pa_devices_requested = params.get("devices_requested") | 864 pa_devices_requested = params.get("devices_requested") |
614 | 865 |
615 # Virtual Functions (VF) assignable devices | 866 # Virtual Functions (VF) assignable devices |
616 if pa_type == "vf": | 867 if pa_type == "vf": |
617 self.pci_assignable = kvm_utils.PciAssignable( | 868 self.pci_assignable = kvm_utils.PciAssignable( |
618 type=pa_type, | 869 type=pa_type, |
619 driver=params.get("driver"), | 870 driver=params.get("driver"), |
620 driver_option=params.get("driver_option"), | 871 driver_option=params.get("driver_option"), |
621 devices_requested=pa_devices_requested) | 872 devices_requested=pa_devices_requested) |
622 # Physical NIC (PF) assignable devices | 873 # Physical NIC (PF) assignable devices |
623 elif pa_type == "pf": | 874 elif pa_type == "pf": |
624 self.pci_assignable = kvm_utils.PciAssignable( | 875 self.pci_assignable = kvm_utils.PciAssignable( |
625 type=pa_type, | 876 type=pa_type, |
626 names=params.get("device_names"), | 877 names=params.get("device_names"), |
627 devices_requested=pa_devices_requested) | 878 devices_requested=pa_devices_requested) |
628 # Working with both VF and PF | 879 # Working with both VF and PF |
629 elif pa_type == "mixed": | 880 elif pa_type == "mixed": |
630 self.pci_assignable = kvm_utils.PciAssignable( | 881 self.pci_assignable = kvm_utils.PciAssignable( |
631 type=pa_type, | 882 type=pa_type, |
632 driver=params.get("driver"), | 883 driver=params.get("driver"), |
633 driver_option=params.get("driver_option"), | 884 driver_option=params.get("driver_option"), |
634 names=params.get("device_names"), | 885 names=params.get("device_names"), |
635 devices_requested=pa_devices_requested) | 886 devices_requested=pa_devices_requested) |
| 887 else: |
| 888 raise VMBadPATypeError(pa_type) |
636 | 889 |
637 self.pa_pci_ids = self.pci_assignable.request_devs() | 890 self.pa_pci_ids = self.pci_assignable.request_devs() |
638 | 891 |
639 if self.pa_pci_ids: | 892 if self.pa_pci_ids: |
640 logging.debug("Successfuly assigned devices: %s", | 893 logging.debug("Successfuly assigned devices: %s", |
641 self.pa_pci_ids) | 894 self.pa_pci_ids) |
642 else: | 895 else: |
643 logging.error("No PCI assignable devices were assigned " | 896 raise VMPAError(pa_type) |
644 "and 'pci_assignable' is defined to %s " | |
645 "on your config file. Aborting VM creation.", | |
646 pa_type) | |
647 return False | |
648 | |
649 elif pa_type and pa_type != "no": | |
650 logging.warn("Unsupported pci_assignable type: %s", pa_type) | |
651 | 897 |
652 # Make qemu command | 898 # Make qemu command |
653 qemu_command = self.make_qemu_command() | 899 qemu_command = self.make_qemu_command() |
654 | 900 |
655 # Add migration parameters if required | 901 # Add migration parameters if required |
656 if migration_mode == "tcp": | 902 if migration_mode == "tcp": |
657 self.migration_port = kvm_utils.find_free_port(5200, 6000) | 903 self.migration_port = kvm_utils.find_free_port(5200, 6000) |
658 qemu_command += " -incoming tcp:0:%d" % self.migration_port | 904 qemu_command += " -incoming tcp:0:%d" % self.migration_port |
659 elif migration_mode == "unix": | 905 elif migration_mode == "unix": |
660 self.migration_file = "/tmp/migration-unix-%s" % self.instance | 906 self.migration_file = "/tmp/migration-unix-%s" % self.instance |
661 qemu_command += " -incoming unix:%s" % self.migration_file | 907 qemu_command += " -incoming unix:%s" % self.migration_file |
662 elif migration_mode == "exec": | 908 elif migration_mode == "exec": |
663 qemu_command += ' -incoming "exec:%s"' % migration_exec_cmd | 909 self.migration_port = kvm_utils.find_free_port(5200, 6000) |
| 910 qemu_command += (' -incoming "exec:nc -l %s"' % |
| 911 self.migration_port) |
664 | 912 |
665 logging.debug("Running qemu command:\n%s", qemu_command) | 913 logging.info("Running qemu command:\n%s", qemu_command) |
666 self.process = kvm_subprocess.run_bg(qemu_command, None, | 914 self.process = kvm_subprocess.run_bg(qemu_command, None, |
667 logging.debug, "(qemu) ") | 915 logging.info, "(qemu) ") |
668 | 916 |
669 # Make sure the process was started successfully | 917 # Make sure the process was started successfully |
670 if not self.process.is_alive(): | 918 if not self.process.is_alive(): |
671 logging.error("VM could not be created; " | 919 e = VMCreateError(qemu_command, |
672 "qemu command failed:\n%s" % qemu_command) | 920 self.process.get_status(), |
673 logging.error("Status: %s" % self.process.get_status()) | 921 self.process.get_output()) |
674 logging.error("Output:" + kvm_utils.format_str_for_message( | |
675 self.process.get_output())) | |
676 self.destroy() | 922 self.destroy() |
677 return False | 923 raise e |
678 | 924 |
679 # Establish monitor connections | 925 # Establish monitor connections |
680 self.monitors = [] | 926 self.monitors = [] |
681 for monitor_name in kvm_utils.get_sub_dict_names(params, | 927 for monitor_name in params.objects("monitors"): |
682 "monitors"): | 928 monitor_params = params.object_params(monitor_name) |
683 monitor_params = kvm_utils.get_sub_dict(params, monitor_name) | |
684 # Wait for monitor connection to succeed | 929 # Wait for monitor connection to succeed |
685 end_time = time.time() + timeout | 930 end_time = time.time() + timeout |
686 while time.time() < end_time: | 931 while time.time() < end_time: |
687 try: | 932 try: |
688 if monitor_params.get("monitor_type") == "qmp": | 933 if monitor_params.get("monitor_type") == "qmp": |
689 # Add a QMP monitor | 934 # Add a QMP monitor |
690 monitor = kvm_monitor.QMPMonitor( | 935 monitor = kvm_monitor.QMPMonitor( |
691 monitor_name, | 936 monitor_name, |
692 self.get_monitor_filename(monitor_name)) | 937 self.get_monitor_filename(monitor_name)) |
693 else: | 938 else: |
694 # Add a "human" monitor | 939 # Add a "human" monitor |
695 monitor = kvm_monitor.HumanMonitor( | 940 monitor = kvm_monitor.HumanMonitor( |
696 monitor_name, | 941 monitor_name, |
697 self.get_monitor_filename(monitor_name)) | 942 self.get_monitor_filename(monitor_name)) |
| 943 monitor.verify_responsive() |
| 944 break |
698 except kvm_monitor.MonitorError, e: | 945 except kvm_monitor.MonitorError, e: |
699 logging.warn(e) | 946 logging.warn(e) |
700 else: | 947 time.sleep(1) |
701 if monitor.is_responsive(): | |
702 break | |
703 time.sleep(1) | |
704 else: | 948 else: |
705 logging.error("Could not connect to monitor '%s'" % | |
706 monitor_name) | |
707 self.destroy() | 949 self.destroy() |
708 return False | 950 raise e |
709 # Add this monitor to the list | 951 # Add this monitor to the list |
710 self.monitors += [monitor] | 952 self.monitors += [monitor] |
711 | 953 |
712 # Get the output so far, to see if we have any problems with | 954 # Get the output so far, to see if we have any problems with |
713 # KVM modules or with hugepage setup. | 955 # KVM modules or with hugepage setup. |
714 output = self.process.get_output() | 956 output = self.process.get_output() |
715 | 957 |
716 if re.search("Could not initialize KVM", output, re.IGNORECASE): | 958 if re.search("Could not initialize KVM", output, re.IGNORECASE): |
717 logging.error("Could not initialize KVM; " | 959 e = VMKVMInitError(qemu_command, self.process.get_output()) |
718 "qemu command:\n%s" % qemu_command) | |
719 logging.error("Output:" + kvm_utils.format_str_for_message( | |
720 self.process.get_output())) | |
721 self.destroy() | 960 self.destroy() |
722 return False | 961 raise e |
723 | 962 |
724 if "alloc_mem_area" in output: | 963 if "alloc_mem_area" in output: |
725 logging.error("Could not allocate hugepage memory; " | 964 e = VMHugePageError(qemu_command, self.process.get_output()) |
726 "qemu command:\n%s" % qemu_command) | |
727 logging.error("Output:" + kvm_utils.format_str_for_message( | |
728 self.process.get_output())) | |
729 self.destroy() | 965 self.destroy() |
730 return False | 966 raise e |
731 | 967 |
732 logging.debug("VM appears to be alive with PID %s", self.get_pid()) | 968 logging.debug("VM appears to be alive with PID %s", self.get_pid()) |
733 | 969 |
734 # Establish a session with the serial console -- requires a version | 970 # Establish a session with the serial console -- requires a version |
735 # of netcat that supports -U | 971 # of netcat that supports -U |
736 self.serial_console = kvm_subprocess.kvm_shell_session( | 972 self.serial_console = kvm_subprocess.ShellSession( |
737 "nc -U %s" % self.get_serial_console_filename(), | 973 "nc -U %s" % self.get_serial_console_filename(), |
738 auto_close=False, | 974 auto_close=False, |
739 output_func=kvm_utils.log_line, | 975 output_func=kvm_utils.log_line, |
740 output_params=("serial-%s.log" % name,)) | 976 output_params=("serial-%s.log" % name,)) |
741 | 977 |
742 return True | |
743 | |
744 finally: | 978 finally: |
745 fcntl.lockf(lockfile, fcntl.LOCK_UN) | 979 fcntl.lockf(lockfile, fcntl.LOCK_UN) |
746 lockfile.close() | 980 lockfile.close() |
747 | 981 |
748 | 982 |
749 def destroy(self, gracefully=True): | 983 def destroy(self, gracefully=True, free_mac_addresses=True): |
750 """ | 984 """ |
751 Destroy the VM. | 985 Destroy the VM. |
752 | 986 |
753 If gracefully is True, first attempt to shutdown the VM with a shell | 987 If gracefully is True, first attempt to shutdown the VM with a shell |
754 command. Then, attempt to destroy the VM via the monitor with a 'quit' | 988 command. Then, attempt to destroy the VM via the monitor with a 'quit' |
755 command. If that fails, send SIGKILL to the qemu process. | 989 command. If that fails, send SIGKILL to the qemu process. |
756 | 990 |
757 @param gracefully: Whether an attempt will be made to end the VM | 991 @param gracefully: If True, an attempt will be made to end the VM |
758 using a shell command before trying to end the qemu process | 992 using a shell command before trying to end the qemu process |
759 with a 'quit' or a kill signal. | 993 with a 'quit' or a kill signal. |
| 994 @param free_mac_addresses: If True, the MAC addresses used by the VM |
| 995 will be freed. |
760 """ | 996 """ |
761 try: | 997 try: |
762 # Is it already dead? | 998 # Is it already dead? |
763 if self.is_dead(): | 999 if self.is_dead(): |
764 logging.debug("VM is already down") | |
765 return | 1000 return |
766 | 1001 |
767 logging.debug("Destroying VM with PID %s...", self.get_pid()) | 1002 logging.debug("Destroying VM with PID %s...", self.get_pid()) |
768 | 1003 |
769 if gracefully and self.params.get("shutdown_command"): | 1004 if gracefully and self.params.get("shutdown_command"): |
770 # Try to destroy with shell command | 1005 # Try to destroy with shell command |
771 logging.debug("Trying to shutdown VM with shell command...") | 1006 logging.debug("Trying to shutdown VM with shell command...") |
772 session = self.remote_login() | 1007 try: |
773 if session: | 1008 session = self.login() |
| 1009 except (kvm_utils.LoginError, VMError), e: |
| 1010 logging.debug(e) |
| 1011 else: |
774 try: | 1012 try: |
775 # Send the shutdown command | 1013 # Send the shutdown command |
776 session.sendline(self.params.get("shutdown_command")) | 1014 session.sendline(self.params.get("shutdown_command")) |
777 logging.debug("Shutdown command sent; waiting for VM " | 1015 logging.debug("Shutdown command sent; waiting for VM " |
778 "to go down...") | 1016 "to go down...") |
779 if kvm_utils.wait_for(self.is_dead, 60, 1, 1): | 1017 if kvm_utils.wait_for(self.is_dead, 60, 1, 1): |
780 logging.debug("VM is down, freeing mac address.") | 1018 logging.debug("VM is down") |
781 return | 1019 return |
782 finally: | 1020 finally: |
783 session.close() | 1021 session.close() |
784 | 1022 |
785 if self.monitor: | 1023 if self.monitor: |
786 # Try to destroy with a monitor command | 1024 # Try to destroy with a monitor command |
787 logging.debug("Trying to kill VM with monitor command...") | 1025 logging.debug("Trying to kill VM with monitor command...") |
788 try: | 1026 try: |
789 self.monitor.quit() | 1027 self.monitor.quit() |
790 except kvm_monitor.MonitorError, e: | 1028 except kvm_monitor.MonitorError, e: |
791 logging.warn(e) | 1029 logging.warn(e) |
792 else: | 1030 else: |
793 # Wait for the VM to be really dead | 1031 # Wait for the VM to be really dead |
794 if kvm_utils.wait_for(self.is_dead, 5, 0.5, 0.5): | 1032 if kvm_utils.wait_for(self.is_dead, 5, 0.5, 0.5): |
795 logging.debug("VM is down") | 1033 logging.debug("VM is down") |
796 return | 1034 return |
797 | 1035 |
798 # If the VM isn't dead yet... | 1036 # If the VM isn't dead yet... |
799 logging.debug("Cannot quit normally; sending a kill to close the " | 1037 logging.debug("Cannot quit normally; sending a kill to close the " |
800 "deal...") | 1038 "deal...") |
801 kvm_utils.kill_process_tree(self.process.get_pid(), 9) | 1039 kvm_utils.kill_process_tree(self.process.get_pid(), 9) |
802 # Wait for the VM to be really dead | 1040 # Wait for the VM to be really dead |
803 if kvm_utils.wait_for(self.is_dead, 5, 0.5, 0.5): | 1041 if kvm_utils.wait_for(self.is_dead, 5, 0.5, 0.5): |
804 logging.debug("VM is down") | 1042 logging.debug("VM is down") |
805 return | 1043 return |
806 | 1044 |
807 logging.error("Process %s is a zombie!" % self.process.get_pid()) | 1045 logging.error("Process %s is a zombie!", self.process.get_pid()) |
808 | 1046 |
809 finally: | 1047 finally: |
810 self.monitors = [] | 1048 self.monitors = [] |
811 if self.pci_assignable: | 1049 if self.pci_assignable: |
812 self.pci_assignable.release_devs() | 1050 self.pci_assignable.release_devs() |
813 if self.process: | 1051 if self.process: |
814 self.process.close() | 1052 self.process.close() |
815 if self.serial_console: | 1053 if self.serial_console: |
816 self.serial_console.close() | 1054 self.serial_console.close() |
817 for f in ([self.get_testlog_filename(), | 1055 for f in ([self.get_testlog_filename(), |
818 self.get_serial_console_filename()] + | 1056 self.get_serial_console_filename()] + |
819 self.get_monitor_filenames()): | 1057 self.get_monitor_filenames()): |
820 try: | 1058 try: |
821 os.unlink(f) | 1059 os.unlink(f) |
822 except OSError: | 1060 except OSError: |
823 pass | 1061 pass |
824 if hasattr(self, "migration_file"): | 1062 if hasattr(self, "migration_file"): |
825 try: | 1063 try: |
826 os.unlink(self.migration_file) | 1064 os.unlink(self.migration_file) |
827 except OSError: | 1065 except OSError: |
828 pass | 1066 pass |
829 num_nics = len(kvm_utils.get_sub_dict_names(self.params, "nics")) | 1067 if free_mac_addresses: |
830 for vlan in range(num_nics): | 1068 num_nics = len(self.params.objects("nics")) |
831 self.free_mac_address(vlan) | 1069 for vlan in range(num_nics): |
| 1070 self.free_mac_address(vlan) |
832 | 1071 |
833 | 1072 |
834 @property | 1073 @property |
835 def monitor(self): | 1074 def monitor(self): |
836 """ | 1075 """ |
837 Return the main monitor object, selected by the parameter main_monitor. | 1076 Return the main monitor object, selected by the parameter main_monitor. |
838 If main_monitor isn't defined, return the first monitor. | 1077 If main_monitor isn't defined, return the first monitor. |
839 If no monitors exist, or if main_monitor refers to a nonexistent | 1078 If no monitors exist, or if main_monitor refers to a nonexistent |
840 monitor, return None. | 1079 monitor, return None. |
841 """ | 1080 """ |
842 for m in self.monitors: | 1081 for m in self.monitors: |
843 if m.name == self.params.get("main_monitor"): | 1082 if m.name == self.params.get("main_monitor"): |
844 return m | 1083 return m |
845 if self.monitors and not self.params.get("main_monitor"): | 1084 if self.monitors and not self.params.get("main_monitor"): |
846 return self.monitors[0] | 1085 return self.monitors[0] |
847 | 1086 |
848 | 1087 |
| 1088 def verify_alive(self): |
| 1089 """ |
| 1090 Make sure the VM is alive and that the main monitor is responsive. |
| 1091 |
| 1092 @raise VMDeadError: If the VM is dead |
| 1093 @raise: Various monitor exceptions if the monitor is unresponsive |
| 1094 """ |
| 1095 if self.is_dead(): |
| 1096 raise VMDeadError(self.process.get_status(), |
| 1097 self.process.get_output()) |
| 1098 if self.monitors: |
| 1099 self.monitor.verify_responsive() |
| 1100 |
| 1101 |
849 def is_alive(self): | 1102 def is_alive(self): |
850 """ | 1103 """ |
851 Return True if the VM is alive and its monitor is responsive. | 1104 Return True if the VM is alive and its monitor is responsive. |
852 """ | 1105 """ |
853 # Check if the process is running | 1106 return not self.is_dead() and (not self.monitors or |
854 if self.is_dead(): | 1107 self.monitor.is_responsive()) |
855 return False | |
856 # Try sending a monitor command | |
857 return bool(self.monitor) and self.monitor.is_responsive() | |
858 | 1108 |
859 | 1109 |
860 def is_dead(self): | 1110 def is_dead(self): |
861 """ | 1111 """ |
862 Return True if the qemu process is dead. | 1112 Return True if the qemu process is dead. |
863 """ | 1113 """ |
864 return not self.process or not self.process.is_alive() | 1114 return not self.process or not self.process.is_alive() |
865 | 1115 |
866 | 1116 |
867 def get_params(self): | 1117 def get_params(self): |
(...skipping 10 matching lines...) Expand all Loading... |
878 """ | 1128 """ |
879 return "/tmp/monitor-%s-%s" % (monitor_name, self.instance) | 1129 return "/tmp/monitor-%s-%s" % (monitor_name, self.instance) |
880 | 1130 |
881 | 1131 |
882 def get_monitor_filenames(self): | 1132 def get_monitor_filenames(self): |
883 """ | 1133 """ |
884 Return a list of all monitor filenames (as specified in the VM's | 1134 Return a list of all monitor filenames (as specified in the VM's |
885 params). | 1135 params). |
886 """ | 1136 """ |
887 return [self.get_monitor_filename(m) for m in | 1137 return [self.get_monitor_filename(m) for m in |
888 kvm_utils.get_sub_dict_names(self.params, "monitors")] | 1138 self.params.objects("monitors")] |
889 | 1139 |
890 | 1140 |
891 def get_serial_console_filename(self): | 1141 def get_serial_console_filename(self): |
892 """ | 1142 """ |
893 Return the serial console filename. | 1143 Return the serial console filename. |
894 """ | 1144 """ |
895 return "/tmp/serial-%s" % self.instance | 1145 return "/tmp/serial-%s" % self.instance |
896 | 1146 |
897 | 1147 |
898 def get_testlog_filename(self): | 1148 def get_testlog_filename(self): |
899 """ | 1149 """ |
900 Return the testlog filename. | 1150 Return the testlog filename. |
901 """ | 1151 """ |
902 return "/tmp/testlog-%s" % self.instance | 1152 return "/tmp/testlog-%s" % self.instance |
903 | 1153 |
904 | 1154 |
905 def get_address(self, index=0): | 1155 def get_address(self, index=0): |
906 """ | 1156 """ |
907 Return the address of a NIC of the guest, in host space. | 1157 Return the address of a NIC of the guest, in host space. |
908 | 1158 |
909 If port redirection is used, return 'localhost' (the NIC has no IP | 1159 If port redirection is used, return 'localhost' (the NIC has no IP |
910 address of its own). Otherwise return the NIC's IP address. | 1160 address of its own). Otherwise return the NIC's IP address. |
911 | 1161 |
912 @param index: Index of the NIC whose address is requested. | 1162 @param index: Index of the NIC whose address is requested. |
| 1163 @raise VMMACAddressMissingError: If no MAC address is defined for the |
| 1164 requested NIC |
| 1165 @raise VMIPAddressMissingError: If no IP address is found for the the |
| 1166 NIC's MAC address |
| 1167 @raise VMAddressVerificationError: If the MAC-IP address mapping cannot |
| 1168 be verified (using arping) |
913 """ | 1169 """ |
914 nics = kvm_utils.get_sub_dict_names(self.params, "nics") | 1170 nics = self.params.objects("nics") |
915 nic_name = nics[index] | 1171 nic_name = nics[index] |
916 nic_params = kvm_utils.get_sub_dict(self.params, nic_name) | 1172 nic_params = self.params.object_params(nic_name) |
917 if nic_params.get("nic_mode") == "tap": | 1173 if nic_params.get("nic_mode") == "tap": |
918 mac = self.get_mac_address(index) | 1174 mac = self.get_mac_address(index).lower() |
919 if not mac: | |
920 logging.debug("MAC address unavailable") | |
921 return None | |
922 mac = mac.lower() | |
923 # Get the IP address from the cache | 1175 # Get the IP address from the cache |
924 ip = self.address_cache.get(mac) | 1176 ip = self.address_cache.get(mac) |
925 if not ip: | 1177 if not ip: |
926 logging.debug("Could not find IP address for MAC address: %s" % | 1178 raise VMIPAddressMissingError(mac) |
927 mac) | |
928 return None | |
929 # Make sure the IP address is assigned to this guest | 1179 # Make sure the IP address is assigned to this guest |
930 macs = [self.get_mac_address(i) for i in range(len(nics))] | 1180 macs = [self.get_mac_address(i) for i in range(len(nics))] |
931 if not kvm_utils.verify_ip_address_ownership(ip, macs): | 1181 if not kvm_utils.verify_ip_address_ownership(ip, macs): |
932 logging.debug("Could not verify MAC-IP address mapping: " | 1182 raise VMAddressVerificationError(mac, ip) |
933 "%s ---> %s" % (mac, ip)) | |
934 return None | |
935 return ip | 1183 return ip |
936 else: | 1184 else: |
937 return "localhost" | 1185 return "localhost" |
938 | 1186 |
939 | 1187 |
940 def get_port(self, port, nic_index=0): | 1188 def get_port(self, port, nic_index=0): |
941 """ | 1189 """ |
942 Return the port in host space corresponding to port in guest space. | 1190 Return the port in host space corresponding to port in guest space. |
943 | 1191 |
944 @param port: Port number in host space. | 1192 @param port: Port number in host space. |
945 @param nic_index: Index of the NIC. | 1193 @param nic_index: Index of the NIC. |
946 @return: If port redirection is used, return the host port redirected | 1194 @return: If port redirection is used, return the host port redirected |
947 to guest port port. Otherwise return port. | 1195 to guest port port. Otherwise return port. |
| 1196 @raise VMPortNotRedirectedError: If an unredirected port is requested |
| 1197 in user mode |
948 """ | 1198 """ |
949 nic_name = kvm_utils.get_sub_dict_names(self.params, "nics")[nic_index] | 1199 nic_name = self.params.objects("nics")[nic_index] |
950 nic_params = kvm_utils.get_sub_dict(self.params, nic_name) | 1200 nic_params = self.params.object_params(nic_name) |
951 if nic_params.get("nic_mode") == "tap": | 1201 if nic_params.get("nic_mode") == "tap": |
952 return port | 1202 return port |
953 else: | 1203 else: |
954 if not self.redirs.has_key(port): | 1204 try: |
955 logging.warn("Warning: guest port %s requested but not " | 1205 return self.redirs[port] |
956 "redirected" % port) | 1206 except KeyError: |
957 return self.redirs.get(port) | 1207 raise VMPortNotRedirectedError(port) |
958 | 1208 |
959 | 1209 |
960 def get_ifname(self, nic_index=0): | 1210 def get_ifname(self, nic_index=0): |
961 """ | 1211 """ |
962 Return the ifname of a tap device associated with a NIC. | 1212 Return the ifname of a tap device associated with a NIC. |
963 | 1213 |
964 @param nic_index: Index of the NIC | 1214 @param nic_index: Index of the NIC |
965 """ | 1215 """ |
966 nics = kvm_utils.get_sub_dict_names(self.params, "nics") | 1216 nics = self.params.objects("nics") |
967 nic_name = nics[nic_index] | 1217 nic_name = nics[nic_index] |
968 nic_params = kvm_utils.get_sub_dict(self.params, nic_name) | 1218 nic_params = self.params.object_params(nic_name) |
969 if nic_params.get("nic_ifname"): | 1219 if nic_params.get("nic_ifname"): |
970 return nic_params.get("nic_ifname") | 1220 return nic_params.get("nic_ifname") |
971 else: | 1221 else: |
972 return "t%d-%s" % (nic_index, self.instance[-11:]) | 1222 return "t%d-%s" % (nic_index, self.instance[-11:]) |
973 | 1223 |
974 | 1224 |
975 def get_mac_address(self, nic_index=0): | 1225 def get_mac_address(self, nic_index=0): |
976 """ | 1226 """ |
977 Return the MAC address of a NIC. | 1227 Return the MAC address of a NIC. |
978 | 1228 |
979 @param nic_index: Index of the NIC | 1229 @param nic_index: Index of the NIC |
| 1230 @raise VMMACAddressMissingError: If no MAC address is defined for the |
| 1231 requested NIC |
980 """ | 1232 """ |
981 return kvm_utils.get_mac_address(self.instance, nic_index) | 1233 mac = kvm_utils.get_mac_address(self.instance, nic_index) |
| 1234 if not mac: |
| 1235 raise VMMACAddressMissingError(nic_index) |
| 1236 return mac |
982 | 1237 |
983 | 1238 |
984 def free_mac_address(self, nic_index=0): | 1239 def free_mac_address(self, nic_index=0): |
985 """ | 1240 """ |
986 Free a NIC's MAC address. | 1241 Free a NIC's MAC address. |
987 | 1242 |
988 @param nic_index: Index of the NIC | 1243 @param nic_index: Index of the NIC |
989 """ | 1244 """ |
990 kvm_utils.free_mac_address(self.instance, nic_index) | 1245 kvm_utils.free_mac_address(self.instance, nic_index) |
991 | 1246 |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1024 if self.is_dead(): | 1279 if self.is_dead(): |
1025 logging.error("Could not get shared memory info from dead VM.") | 1280 logging.error("Could not get shared memory info from dead VM.") |
1026 return None | 1281 return None |
1027 | 1282 |
1028 filename = "/proc/%d/statm" % self.get_pid() | 1283 filename = "/proc/%d/statm" % self.get_pid() |
1029 shm = int(open(filename).read().split()[2]) | 1284 shm = int(open(filename).read().split()[2]) |
1030 # statm stores informations in pages, translate it to MB | 1285 # statm stores informations in pages, translate it to MB |
1031 return shm * 4.0 / 1024 | 1286 return shm * 4.0 / 1024 |
1032 | 1287 |
1033 | 1288 |
1034 def remote_login(self, nic_index=0, timeout=10): | 1289 @error.context_aware |
| 1290 def login(self, nic_index=0, timeout=10): |
1035 """ | 1291 """ |
1036 Log into the guest via SSH/Telnet/Netcat. | 1292 Log into the guest via SSH/Telnet/Netcat. |
1037 If timeout expires while waiting for output from the guest (e.g. a | 1293 If timeout expires while waiting for output from the guest (e.g. a |
1038 password prompt or a shell prompt) -- fail. | 1294 password prompt or a shell prompt) -- fail. |
1039 | 1295 |
1040 @param nic_index: The index of the NIC to connect to. | 1296 @param nic_index: The index of the NIC to connect to. |
1041 @param timeout: Time (seconds) before giving up logging into the | 1297 @param timeout: Time (seconds) before giving up logging into the |
1042 guest. | 1298 guest. |
1043 @return: kvm_spawn object on success and None on failure. | 1299 @return: A ShellSession object. |
1044 """ | 1300 """ |
| 1301 error.context("logging into '%s'" % self.name) |
1045 username = self.params.get("username", "") | 1302 username = self.params.get("username", "") |
1046 password = self.params.get("password", "") | 1303 password = self.params.get("password", "") |
1047 prompt = self.params.get("shell_prompt", "[\#\$]") | 1304 prompt = self.params.get("shell_prompt", "[\#\$]") |
1048 linesep = eval("'%s'" % self.params.get("shell_linesep", r"\n")) | 1305 linesep = eval("'%s'" % self.params.get("shell_linesep", r"\n")) |
1049 client = self.params.get("shell_client") | 1306 client = self.params.get("shell_client") |
1050 address = self.get_address(nic_index) | 1307 address = self.get_address(nic_index) |
1051 port = self.get_port(int(self.params.get("shell_port"))) | 1308 port = self.get_port(int(self.params.get("shell_port"))) |
1052 log_filename = ("session-%s-%s.log" % | 1309 log_filename = ("session-%s-%s.log" % |
1053 (self.name, kvm_utils.generate_random_string(4))) | 1310 (self.name, kvm_utils.generate_random_string(4))) |
1054 | |
1055 if not address or not port: | |
1056 logging.debug("IP address or port unavailable") | |
1057 return None | |
1058 | |
1059 session = kvm_utils.remote_login(client, address, port, username, | 1311 session = kvm_utils.remote_login(client, address, port, username, |
1060 password, prompt, linesep, | 1312 password, prompt, linesep, |
1061 log_filename, timeout) | 1313 log_filename, timeout) |
1062 | 1314 session.set_status_test_command(self.params.get("status_test_command", |
1063 if session: | 1315 "")) |
1064 session.set_status_test_command(self.params.get("status_test_" | |
1065 "command", "")) | |
1066 return session | 1316 return session |
1067 | 1317 |
1068 | 1318 |
1069 def copy_files_to(self, local_path, remote_path, nic_index=0, timeout=600): | 1319 def remote_login(self, nic_index=0, timeout=10): |
1070 """ | 1320 """ |
1071 Transfer files to the guest. | 1321 Alias for login() for backward compatibility. |
| 1322 """ |
| 1323 return self.login(nic_index, timeout) |
1072 | 1324 |
1073 @param local_path: Host path | 1325 |
1074 @param remote_path: Guest path | 1326 def wait_for_login(self, nic_index=0, timeout=240, internal_timeout=10): |
| 1327 """ |
| 1328 Make multiple attempts to log into the guest via SSH/Telnet/Netcat. |
| 1329 |
1075 @param nic_index: The index of the NIC to connect to. | 1330 @param nic_index: The index of the NIC to connect to. |
| 1331 @param timeout: Time (seconds) to keep trying to log in. |
| 1332 @param internal_timeout: Timeout to pass to login(). |
| 1333 @return: A ShellSession object. |
| 1334 """ |
| 1335 logging.debug("Attempting to log into '%s' (timeout %ds)", self.name, |
| 1336 timeout) |
| 1337 end_time = time.time() + timeout |
| 1338 while time.time() < end_time: |
| 1339 try: |
| 1340 return self.login(nic_index, internal_timeout) |
| 1341 except (kvm_utils.LoginError, VMError), e: |
| 1342 logging.debug(e) |
| 1343 time.sleep(2) |
| 1344 # Timeout expired; try one more time but don't catch exceptions |
| 1345 return self.login(nic_index, internal_timeout) |
| 1346 |
| 1347 |
| 1348 @error.context_aware |
| 1349 def copy_files_to(self, host_path, guest_path, nic_index=0, verbose=False, |
| 1350 timeout=600): |
| 1351 """ |
| 1352 Transfer files to the remote host(guest). |
| 1353 |
| 1354 @param host_path: Host path |
| 1355 @param guest_path: Guest path |
| 1356 @param nic_index: The index of the NIC to connect to. |
| 1357 @param verbose: If True, log some stats using logging.debug (RSS only) |
1076 @param timeout: Time (seconds) before giving up on doing the remote | 1358 @param timeout: Time (seconds) before giving up on doing the remote |
1077 copy. | 1359 copy. |
1078 """ | 1360 """ |
| 1361 error.context("sending file(s) to '%s'" % self.name) |
1079 username = self.params.get("username", "") | 1362 username = self.params.get("username", "") |
1080 password = self.params.get("password", "") | 1363 password = self.params.get("password", "") |
1081 client = self.params.get("file_transfer_client") | 1364 client = self.params.get("file_transfer_client") |
1082 address = self.get_address(nic_index) | 1365 address = self.get_address(nic_index) |
1083 port = self.get_port(int(self.params.get("file_transfer_port"))) | 1366 port = self.get_port(int(self.params.get("file_transfer_port"))) |
1084 | 1367 log_filename = ("transfer-%s-to-%s-%s.log" % |
1085 if not address or not port: | 1368 (self.name, address, |
1086 logging.debug("IP address or port unavailable") | 1369 kvm_utils.generate_random_string(4))) |
1087 return None | 1370 kvm_utils.copy_files_to(address, client, username, password, port, |
1088 | 1371 host_path, guest_path, log_filename, verbose, |
1089 if client == "scp": | 1372 timeout) |
1090 log_filename = ("scp-%s-%s.log" % | |
1091 (self.name, kvm_utils.generate_random_string(4))) | |
1092 return kvm_utils.scp_to_remote(address, port, username, password, | |
1093 local_path, remote_path, | |
1094 log_filename, timeout) | |
1095 elif client == "rss": | |
1096 c = rss_file_transfer.FileUploadClient(address, port) | |
1097 c.upload(local_path, remote_path, timeout) | |
1098 c.close() | |
1099 return True | |
1100 | 1373 |
1101 | 1374 |
1102 def copy_files_from(self, remote_path, local_path, nic_index=0, timeout=600)
: | 1375 @error.context_aware |
| 1376 def copy_files_from(self, guest_path, host_path, nic_index=0, |
| 1377 verbose=False, timeout=600): |
1103 """ | 1378 """ |
1104 Transfer files from the guest. | 1379 Transfer files from the guest. |
1105 | 1380 |
1106 @param local_path: Guest path | 1381 @param host_path: Guest path |
1107 @param remote_path: Host path | 1382 @param guest_path: Host path |
1108 @param nic_index: The index of the NIC to connect to. | 1383 @param nic_index: The index of the NIC to connect to. |
| 1384 @param verbose: If True, log some stats using logging.debug (RSS only) |
1109 @param timeout: Time (seconds) before giving up on doing the remote | 1385 @param timeout: Time (seconds) before giving up on doing the remote |
1110 copy. | 1386 copy. |
1111 """ | 1387 """ |
| 1388 error.context("receiving file(s) from '%s'" % self.name) |
1112 username = self.params.get("username", "") | 1389 username = self.params.get("username", "") |
1113 password = self.params.get("password", "") | 1390 password = self.params.get("password", "") |
1114 client = self.params.get("file_transfer_client") | 1391 client = self.params.get("file_transfer_client") |
1115 address = self.get_address(nic_index) | 1392 address = self.get_address(nic_index) |
1116 port = self.get_port(int(self.params.get("file_transfer_port"))) | 1393 port = self.get_port(int(self.params.get("file_transfer_port"))) |
1117 | 1394 log_filename = ("transfer-%s-from-%s-%s.log" % |
1118 if not address or not port: | 1395 (self.name, address, |
1119 logging.debug("IP address or port unavailable") | 1396 kvm_utils.generate_random_string(4))) |
1120 return None | 1397 kvm_utils.copy_files_from(address, client, username, password, port, |
1121 | 1398 guest_path, host_path, log_filename, |
1122 if client == "scp": | 1399 verbose, timeout) |
1123 log_filename = ("scp-%s-%s.log" % | |
1124 (self.name, kvm_utils.generate_random_string(4))) | |
1125 return kvm_utils.scp_from_remote(address, port, username, password, | |
1126 remote_path, local_path, | |
1127 log_filename, timeout) | |
1128 elif client == "rss": | |
1129 c = rss_file_transfer.FileDownloadClient(address, port) | |
1130 c.download(remote_path, local_path, timeout) | |
1131 c.close() | |
1132 return True | |
1133 | 1400 |
1134 | 1401 |
| 1402 @error.context_aware |
1135 def serial_login(self, timeout=10): | 1403 def serial_login(self, timeout=10): |
1136 """ | 1404 """ |
1137 Log into the guest via the serial console. | 1405 Log into the guest via the serial console. |
1138 If timeout expires while waiting for output from the guest (e.g. a | 1406 If timeout expires while waiting for output from the guest (e.g. a |
1139 password prompt or a shell prompt) -- fail. | 1407 password prompt or a shell prompt) -- fail. |
1140 | 1408 |
1141 @param timeout: Time (seconds) before giving up logging into the guest. | 1409 @param timeout: Time (seconds) before giving up logging into the guest. |
1142 @return: kvm_spawn object on success and None on failure. | 1410 @return: ShellSession object on success and None on failure. |
1143 """ | 1411 """ |
| 1412 error.context("logging into '%s' via serial console" % self.name) |
1144 username = self.params.get("username", "") | 1413 username = self.params.get("username", "") |
1145 password = self.params.get("password", "") | 1414 password = self.params.get("password", "") |
1146 prompt = self.params.get("shell_prompt", "[\#\$]") | 1415 prompt = self.params.get("shell_prompt", "[\#\$]") |
1147 linesep = eval("'%s'" % self.params.get("shell_linesep", r"\n")) | 1416 linesep = eval("'%s'" % self.params.get("shell_linesep", r"\n")) |
1148 status_test_command = self.params.get("status_test_command", "") | 1417 status_test_command = self.params.get("status_test_command", "") |
1149 | 1418 |
1150 if self.serial_console: | 1419 self.serial_console.set_linesep(linesep) |
1151 self.serial_console.set_linesep(linesep) | 1420 self.serial_console.set_status_test_command(status_test_command) |
1152 self.serial_console.set_status_test_command(status_test_command) | 1421 |
| 1422 # Try to get a login prompt |
| 1423 self.serial_console.sendline() |
| 1424 |
| 1425 kvm_utils._remote_login(self.serial_console, username, password, |
| 1426 prompt, timeout) |
| 1427 return self.serial_console |
| 1428 |
| 1429 |
| 1430 def wait_for_serial_login(self, timeout=240, internal_timeout=10): |
| 1431 """ |
| 1432 Make multiple attempts to log into the guest via serial console. |
| 1433 |
| 1434 @param timeout: Time (seconds) to keep trying to log in. |
| 1435 @param internal_timeout: Timeout to pass to serial_login(). |
| 1436 @return: A ShellSession object. |
| 1437 """ |
| 1438 logging.debug("Attempting to log into '%s' via serial console " |
| 1439 "(timeout %ds)", self.name, timeout) |
| 1440 end_time = time.time() + timeout |
| 1441 while time.time() < end_time: |
| 1442 try: |
| 1443 return self.serial_login(internal_timeout) |
| 1444 except kvm_utils.LoginError, e: |
| 1445 logging.debug(e) |
| 1446 time.sleep(2) |
| 1447 # Timeout expired; try one more time but don't catch exceptions |
| 1448 return self.serial_login(internal_timeout) |
| 1449 |
| 1450 |
| 1451 @error.context_aware |
| 1452 def migrate(self, timeout=3600, protocol="tcp", cancel_delay=None, |
| 1453 offline=False, stable_check=False, clean=True, |
| 1454 save_path="/tmp", dest_host="localhost", remote_port=None): |
| 1455 """ |
| 1456 Migrate the VM. |
| 1457 |
| 1458 If the migration is local, the VM object's state is switched with that |
| 1459 of the destination VM. Otherwise, the state is switched with that of |
| 1460 a dead VM (returned by self.clone()). |
| 1461 |
| 1462 @param timeout: Time to wait for migration to complete. |
| 1463 @param protocol: Migration protocol ('tcp', 'unix' or 'exec'). |
| 1464 @param cancel_delay: If provided, specifies a time duration after which |
| 1465 migration will be canceled. Used for testing migrate_cancel. |
| 1466 @param offline: If True, pause the source VM before migration. |
| 1467 @param stable_check: If True, compare the VM's state after migration to |
| 1468 its state before migration and raise an exception if they |
| 1469 differ. |
| 1470 @param clean: If True, delete the saved state files (relevant only if |
| 1471 stable_check is also True). |
| 1472 @save_path: The path for state files. |
| 1473 @param dest_host: Destination host (defaults to 'localhost'). |
| 1474 @param remote_port: Port to use for remote migration. |
| 1475 """ |
| 1476 error.base_context("migrating '%s'" % self.name) |
| 1477 |
| 1478 def mig_finished(): |
| 1479 o = self.monitor.info("migrate") |
| 1480 if isinstance(o, str): |
| 1481 return "status: active" not in o |
| 1482 else: |
| 1483 return o.get("status") != "active" |
| 1484 |
| 1485 def mig_succeeded(): |
| 1486 o = self.monitor.info("migrate") |
| 1487 if isinstance(o, str): |
| 1488 return "status: completed" in o |
| 1489 else: |
| 1490 return o.get("status") == "completed" |
| 1491 |
| 1492 def mig_failed(): |
| 1493 o = self.monitor.info("migrate") |
| 1494 if isinstance(o, str): |
| 1495 return "status: failed" in o |
| 1496 else: |
| 1497 return o.get("status") == "failed" |
| 1498 |
| 1499 def mig_cancelled(): |
| 1500 o = self.monitor.info("migrate") |
| 1501 if isinstance(o, str): |
| 1502 return ("Migration status: cancelled" in o or |
| 1503 "Migration status: canceled" in o) |
| 1504 else: |
| 1505 return (o.get("status") == "cancelled" or |
| 1506 o.get("status") == "canceled") |
| 1507 |
| 1508 def wait_for_migration(): |
| 1509 if not kvm_utils.wait_for(mig_finished, timeout, 2, 2, |
| 1510 "Waiting for migration to complete"): |
| 1511 raise VMMigrateTimeoutError("Timeout expired while waiting " |
| 1512 "for migration to finish") |
| 1513 |
| 1514 local = dest_host == "localhost" |
| 1515 |
| 1516 clone = self.clone() |
| 1517 if local: |
| 1518 error.context("creating destination VM") |
| 1519 if stable_check: |
| 1520 # Pause the dest vm after creation |
| 1521 extra_params = clone.params.get("extra_params", "") + " -S" |
| 1522 clone.params["extra_params"] = extra_params |
| 1523 clone.create(migration_mode=protocol, mac_source=self) |
| 1524 error.context() |
| 1525 |
| 1526 try: |
| 1527 if protocol == "tcp": |
| 1528 if local: |
| 1529 uri = "tcp:localhost:%d" % clone.migration_port |
| 1530 else: |
| 1531 uri = "tcp:%s:%d" % (dest_host, remote_port) |
| 1532 elif protocol == "unix": |
| 1533 uri = "unix:%s" % clone.migration_file |
| 1534 elif protocol == "exec": |
| 1535 uri = '"exec:nc localhost %s"' % clone.migration_port |
| 1536 |
| 1537 if offline: |
| 1538 self.monitor.cmd("stop") |
| 1539 |
| 1540 logging.info("Migrating to %s", uri) |
| 1541 self.monitor.migrate(uri) |
| 1542 |
| 1543 if cancel_delay: |
| 1544 time.sleep(cancel_delay) |
| 1545 self.monitor.cmd("migrate_cancel") |
| 1546 if not kvm_utils.wait_for(mig_cancelled, 60, 2, 2, |
| 1547 "Waiting for migration " |
| 1548 "cancellation"): |
| 1549 raise VMMigrateCancelError("Cannot cancel migration") |
| 1550 return |
| 1551 |
| 1552 wait_for_migration() |
| 1553 |
| 1554 # Report migration status |
| 1555 if mig_succeeded(): |
| 1556 logging.info("Migration completed successfully") |
| 1557 elif mig_failed(): |
| 1558 raise VMMigrateFailedError("Migration failed") |
| 1559 else: |
| 1560 raise VMMigrateFailedError("Migration ended with unknown " |
| 1561 "status") |
| 1562 |
| 1563 # Switch self <-> clone |
| 1564 temp = self.clone(copy_state=True) |
| 1565 self.__dict__ = clone.__dict__ |
| 1566 clone = temp |
| 1567 |
| 1568 # From now on, clone is the source VM that will soon be destroyed |
| 1569 # and self is the destination VM that will remain alive. If this |
| 1570 # is remote migration, self is a dead VM object. |
| 1571 |
| 1572 error.context("after migration") |
| 1573 if local: |
| 1574 time.sleep(1) |
| 1575 self.verify_alive() |
| 1576 |
| 1577 if local and stable_check: |
| 1578 try: |
| 1579 save1 = os.path.join(save_path, "src-" + clone.instance) |
| 1580 save2 = os.path.join(save_path, "dst-" + self.instance) |
| 1581 clone.save_to_file(save1) |
| 1582 self.save_to_file(save2) |
| 1583 # Fail if we see deltas |
| 1584 md5_save1 = utils.hash_file(save1) |
| 1585 md5_save2 = utils.hash_file(save2) |
| 1586 if md5_save1 != md5_save2: |
| 1587 raise VMMigrateStateMismatchError(md5_save1, md5_save2) |
| 1588 finally: |
| 1589 if clean: |
| 1590 if os.path.isfile(save1): |
| 1591 os.remove(save1) |
| 1592 if os.path.isfile(save2): |
| 1593 os.remove(save2) |
| 1594 |
| 1595 finally: |
| 1596 # If we're doing remote migration and it's completed successfully, |
| 1597 # self points to a dead VM object |
| 1598 if self.is_alive(): |
| 1599 self.monitor.cmd("cont") |
| 1600 clone.destroy(gracefully=False) |
| 1601 |
| 1602 |
| 1603 @error.context_aware |
| 1604 def reboot(self, session=None, method="shell", nic_index=0, timeout=240): |
| 1605 """ |
| 1606 Reboot the VM and wait for it to come back up by trying to log in until |
| 1607 timeout expires. |
| 1608 |
| 1609 @param session: A shell session object or None. |
| 1610 @param method: Reboot method. Can be "shell" (send a shell reboot |
| 1611 command) or "system_reset" (send a system_reset monitor command)
. |
| 1612 @param nic_index: Index of NIC to access in the VM, when logging in |
| 1613 after rebooting. |
| 1614 @param timeout: Time to wait for login to succeed (after rebooting). |
| 1615 @return: A new shell session object. |
| 1616 """ |
| 1617 error.base_context("rebooting '%s'" % self.name, logging.info) |
| 1618 error.context("before reboot") |
| 1619 session = session or self.login() |
| 1620 error.context() |
| 1621 |
| 1622 if method == "shell": |
| 1623 session.sendline(self.params.get("reboot_command")) |
| 1624 elif method == "system_reset": |
| 1625 # Clear the event list of all QMP monitors |
| 1626 qmp_monitors = [m for m in self.monitors if m.protocol == "qmp"] |
| 1627 for m in qmp_monitors: |
| 1628 m.clear_events() |
| 1629 # Send a system_reset monitor command |
| 1630 self.monitor.cmd("system_reset") |
| 1631 # Look for RESET QMP events |
| 1632 time.sleep(1) |
| 1633 for m in qmp_monitors: |
| 1634 if m.get_event("RESET"): |
| 1635 logging.info("RESET QMP event received") |
| 1636 else: |
| 1637 raise VMRebootError("RESET QMP event not received after " |
| 1638 "system_reset (monitor '%s')" % m.name) |
1153 else: | 1639 else: |
1154 return None | 1640 raise VMRebootError("Unknown reboot method: %s" % method) |
1155 | 1641 |
1156 # Make sure we get a login prompt | 1642 error.context("waiting for guest to go down", logging.info) |
1157 self.serial_console.sendline() | 1643 if not kvm_utils.wait_for(lambda: |
1158 | 1644 not session.is_responsive(timeout=30), |
1159 if kvm_utils._remote_login(self.serial_console, username, password, | 1645 120, 0, 1): |
1160 prompt, timeout): | 1646 raise VMRebootError("Guest refuses to go down") |
1161 return self.serial_console | 1647 session.close() |
| 1648 |
| 1649 error.context("logging in after reboot", logging.info) |
| 1650 return self.wait_for_login(nic_index, timeout=timeout) |
1162 | 1651 |
1163 | 1652 |
1164 def send_key(self, keystr): | 1653 def send_key(self, keystr): |
1165 """ | 1654 """ |
1166 Send a key event to the VM. | 1655 Send a key event to the VM. |
1167 | 1656 |
1168 @param: keystr: A key event string (e.g. "ctrl-alt-delete") | 1657 @param: keystr: A key event string (e.g. "ctrl-alt-delete") |
1169 """ | 1658 """ |
1170 # For compatibility with versions of QEMU that do not recognize all | 1659 # For compatibility with versions of QEMU that do not recognize all |
1171 # key names: replace keyname with the hex value from the dict, which | 1660 # key names: replace keyname with the hex value from the dict, which |
(...skipping 30 matching lines...) Expand all Loading... |
1202 if self.params.get("uuid") == "random": | 1691 if self.params.get("uuid") == "random": |
1203 return self.uuid | 1692 return self.uuid |
1204 else: | 1693 else: |
1205 return self.params.get("uuid", None) | 1694 return self.params.get("uuid", None) |
1206 | 1695 |
1207 | 1696 |
1208 def get_cpu_count(self): | 1697 def get_cpu_count(self): |
1209 """ | 1698 """ |
1210 Get the cpu count of the VM. | 1699 Get the cpu count of the VM. |
1211 """ | 1700 """ |
1212 session = self.remote_login() | 1701 session = self.login() |
1213 if not session: | |
1214 return None | |
1215 try: | 1702 try: |
1216 cmd = self.params.get("cpu_chk_cmd") | 1703 return int(session.cmd(self.params.get("cpu_chk_cmd"))) |
1217 s, count = session.get_command_status_output(cmd) | |
1218 if s == 0: | |
1219 return int(count) | |
1220 return None | |
1221 finally: | 1704 finally: |
1222 session.close() | 1705 session.close() |
1223 | 1706 |
1224 | 1707 |
1225 def get_memory_size(self, cmd=None): | 1708 def get_memory_size(self, cmd=None): |
1226 """ | 1709 """ |
1227 Get bootup memory size of the VM. | 1710 Get bootup memory size of the VM. |
1228 | 1711 |
1229 @param check_cmd: Command used to check memory. If not provided, | 1712 @param check_cmd: Command used to check memory. If not provided, |
1230 self.params.get("mem_chk_cmd") will be used. | 1713 self.params.get("mem_chk_cmd") will be used. |
1231 """ | 1714 """ |
1232 session = self.remote_login() | 1715 session = self.login() |
1233 if not session: | |
1234 return None | |
1235 try: | 1716 try: |
1236 if not cmd: | 1717 if not cmd: |
1237 cmd = self.params.get("mem_chk_cmd") | 1718 cmd = self.params.get("mem_chk_cmd") |
1238 s, mem_str = session.get_command_status_output(cmd) | 1719 mem_str = session.cmd(cmd) |
1239 if s != 0: | |
1240 return None | |
1241 mem = re.findall("([0-9]+)", mem_str) | 1720 mem = re.findall("([0-9]+)", mem_str) |
1242 mem_size = 0 | 1721 mem_size = 0 |
1243 for m in mem: | 1722 for m in mem: |
1244 mem_size += int(m) | 1723 mem_size += int(m) |
1245 if "GB" in mem_str: | 1724 if "GB" in mem_str: |
1246 mem_size *= 1024 | 1725 mem_size *= 1024 |
1247 elif "MB" in mem_str: | 1726 elif "MB" in mem_str: |
1248 pass | 1727 pass |
1249 else: | 1728 else: |
1250 mem_size /= 1024 | 1729 mem_size /= 1024 |
1251 return int(mem_size) | 1730 return int(mem_size) |
1252 finally: | 1731 finally: |
1253 session.close() | 1732 session.close() |
1254 | 1733 |
1255 | 1734 |
1256 def get_current_memory_size(self): | 1735 def get_current_memory_size(self): |
1257 """ | 1736 """ |
1258 Get current memory size of the VM, rather than bootup memory. | 1737 Get current memory size of the VM, rather than bootup memory. |
1259 """ | 1738 """ |
1260 cmd = self.params.get("mem_chk_cur_cmd") | 1739 cmd = self.params.get("mem_chk_cur_cmd") |
1261 return self.get_memory_size(cmd) | 1740 return self.get_memory_size(cmd) |
| 1741 |
| 1742 |
| 1743 def save_to_file(self, path): |
| 1744 """ |
| 1745 Save the state of virtual machine to a file through migrate to |
| 1746 exec |
| 1747 """ |
| 1748 # Make sure we only get one iteration |
| 1749 self.monitor.cmd("migrate_set_speed 1000g") |
| 1750 self.monitor.cmd("migrate_set_downtime 100000000") |
| 1751 self.monitor.migrate('"exec:cat>%s"' % path) |
| 1752 # Restore the speed and downtime of migration |
| 1753 self.monitor.cmd("migrate_set_speed %d" % (32<<20)) |
| 1754 self.monitor.cmd("migrate_set_downtime 0.03") |
OLD | NEW |