| 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, os, logging, fcntl, re, commands, glob | 8 import time, os, logging, fcntl, re, commands, glob |
| 9 import kvm_utils, kvm_subprocess, kvm_monitor | |
| 10 from autotest_lib.client.common_lib import error | 9 from autotest_lib.client.common_lib import error |
| 11 from autotest_lib.client.bin import utils | 10 from autotest_lib.client.bin import utils |
| 11 import virt_utils, virt_vm, kvm_monitor, aexpect |
| 12 | 12 |
| 13 | 13 |
| 14 class VMError(Exception): | 14 class VM(virt_vm.BaseVM): |
| 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 VMDeadKernelCrashError(VMError): | |
| 109 def __init__(self, kernel_crash): | |
| 110 VMError.__init__(self, kernel_crash) | |
| 111 self.kernel_crash = kernel_crash | |
| 112 | |
| 113 def __str__(self): | |
| 114 return ("VM is dead due to a kernel crash:\n%s" % self.kernel_crash) | |
| 115 | |
| 116 | |
| 117 class VMAddressError(VMError): | |
| 118 pass | |
| 119 | |
| 120 | |
| 121 class VMPortNotRedirectedError(VMAddressError): | |
| 122 def __init__(self, port): | |
| 123 VMAddressError.__init__(self, port) | |
| 124 self.port = port | |
| 125 | |
| 126 def __str__(self): | |
| 127 return "Port not redirected: %s" % self.port | |
| 128 | |
| 129 | |
| 130 class VMAddressVerificationError(VMAddressError): | |
| 131 def __init__(self, mac, ip): | |
| 132 VMAddressError.__init__(self, mac, ip) | |
| 133 self.mac = mac | |
| 134 self.ip = ip | |
| 135 | |
| 136 def __str__(self): | |
| 137 return ("Cannot verify MAC-IP address mapping using arping: " | |
| 138 "%s ---> %s" % (self.mac, self.ip)) | |
| 139 | |
| 140 | |
| 141 class VMMACAddressMissingError(VMAddressError): | |
| 142 def __init__(self, nic_index): | |
| 143 VMAddressError.__init__(self, nic_index) | |
| 144 self.nic_index = nic_index | |
| 145 | |
| 146 def __str__(self): | |
| 147 return "No MAC address defined for NIC #%s" % self.nic_index | |
| 148 | |
| 149 | |
| 150 class VMIPAddressMissingError(VMAddressError): | |
| 151 def __init__(self, mac): | |
| 152 VMAddressError.__init__(self, mac) | |
| 153 self.mac = mac | |
| 154 | |
| 155 def __str__(self): | |
| 156 return "Cannot find IP address for MAC address %s" % self.mac | |
| 157 | |
| 158 | |
| 159 class VMMigrateError(VMError): | |
| 160 pass | |
| 161 | |
| 162 | |
| 163 class VMMigrateTimeoutError(VMMigrateError): | |
| 164 pass | |
| 165 | |
| 166 | |
| 167 class VMMigrateCancelError(VMMigrateError): | |
| 168 pass | |
| 169 | |
| 170 | |
| 171 class VMMigrateFailedError(VMMigrateError): | |
| 172 pass | |
| 173 | |
| 174 | |
| 175 class VMMigrateStateMismatchError(VMMigrateError): | |
| 176 def __init__(self, src_hash, dst_hash): | |
| 177 VMMigrateError.__init__(self, src_hash, dst_hash) | |
| 178 self.src_hash = src_hash | |
| 179 self.dst_hash = dst_hash | |
| 180 | |
| 181 def __str__(self): | |
| 182 return ("Mismatch of VM state before and after migration (%s != %s)" % | |
| 183 (self.src_hash, self.dst_hash)) | |
| 184 | |
| 185 | |
| 186 class VMRebootError(VMError): | |
| 187 pass | |
| 188 | |
| 189 | |
| 190 def get_image_filename(params, root_dir): | |
| 191 """ | |
| 192 Generate an image path from params and root_dir. | |
| 193 | |
| 194 @param params: Dictionary containing the test parameters. | |
| 195 @param root_dir: Base directory for relative filenames. | |
| 196 | |
| 197 @note: params should contain: | |
| 198 image_name -- the name of the image file, without extension | |
| 199 image_format -- the format of the image (qcow2, raw etc) | |
| 200 """ | |
| 201 image_name = params.get("image_name", "image") | |
| 202 image_format = params.get("image_format", "qcow2") | |
| 203 if params.get("image_raw_device") == "yes": | |
| 204 return image_name | |
| 205 image_filename = "%s.%s" % (image_name, image_format) | |
| 206 image_filename = kvm_utils.get_path(root_dir, image_filename) | |
| 207 return image_filename | |
| 208 | |
| 209 | |
| 210 def create_image(params, root_dir): | |
| 211 """ | |
| 212 Create an image using qemu_image. | |
| 213 | |
| 214 @param params: Dictionary containing the test parameters. | |
| 215 @param root_dir: Base directory for relative filenames. | |
| 216 | |
| 217 @note: params should contain: | |
| 218 image_name -- the name of the image file, without extension | |
| 219 image_format -- the format of the image (qcow2, raw etc) | |
| 220 image_size -- the requested size of the image (a string | |
| 221 qemu-img can understand, such as '10G') | |
| 222 """ | |
| 223 qemu_img_cmd = kvm_utils.get_path(root_dir, params.get("qemu_img_binary", | |
| 224 "qemu-img")) | |
| 225 qemu_img_cmd += " create" | |
| 226 | |
| 227 format = params.get("image_format", "qcow2") | |
| 228 qemu_img_cmd += " -f %s" % format | |
| 229 | |
| 230 image_filename = get_image_filename(params, root_dir) | |
| 231 qemu_img_cmd += " %s" % image_filename | |
| 232 | |
| 233 size = params.get("image_size", "10G") | |
| 234 qemu_img_cmd += " %s" % size | |
| 235 | |
| 236 utils.system(qemu_img_cmd) | |
| 237 logging.info("Image created in %r", image_filename) | |
| 238 return image_filename | |
| 239 | |
| 240 | |
| 241 def remove_image(params, root_dir): | |
| 242 """ | |
| 243 Remove an image file. | |
| 244 | |
| 245 @param params: A dict | |
| 246 @param root_dir: Base directory for relative filenames. | |
| 247 | |
| 248 @note: params should contain: | |
| 249 image_name -- the name of the image file, without extension | |
| 250 image_format -- the format of the image (qcow2, raw etc) | |
| 251 """ | |
| 252 image_filename = get_image_filename(params, root_dir) | |
| 253 logging.debug("Removing image file %s...", image_filename) | |
| 254 if os.path.exists(image_filename): | |
| 255 os.unlink(image_filename) | |
| 256 else: | |
| 257 logging.debug("Image file %s not found") | |
| 258 | |
| 259 | |
| 260 def check_image(params, root_dir): | |
| 261 """ | |
| 262 Check an image using qemu-img. | |
| 263 | |
| 264 @param params: Dictionary containing the test parameters. | |
| 265 @param root_dir: Base directory for relative filenames. | |
| 266 | |
| 267 @note: params should contain: | |
| 268 image_name -- the name of the image file, without extension | |
| 269 image_format -- the format of the image (qcow2, raw etc) | |
| 270 | |
| 271 @raise VMImageCheckError: In case qemu-img check fails on the image. | |
| 272 """ | |
| 273 image_filename = get_image_filename(params, root_dir) | |
| 274 logging.debug("Checking image file %s...", image_filename) | |
| 275 qemu_img_cmd = kvm_utils.get_path(root_dir, | |
| 276 params.get("qemu_img_binary", "qemu-img")) | |
| 277 image_is_qcow2 = params.get("image_format") == 'qcow2' | |
| 278 if os.path.exists(image_filename) and image_is_qcow2: | |
| 279 # Verifying if qemu-img supports 'check' | |
| 280 q_result = utils.run(qemu_img_cmd, ignore_status=True) | |
| 281 q_output = q_result.stdout | |
| 282 check_img = True | |
| 283 if not "check" in q_output: | |
| 284 logging.error("qemu-img does not support 'check', " | |
| 285 "skipping check...") | |
| 286 check_img = False | |
| 287 if not "info" in q_output: | |
| 288 logging.error("qemu-img does not support 'info', " | |
| 289 "skipping check...") | |
| 290 check_img = False | |
| 291 if check_img: | |
| 292 try: | |
| 293 utils.system("%s info %s" % (qemu_img_cmd, image_filename)) | |
| 294 except error.CmdError: | |
| 295 logging.error("Error getting info from image %s", | |
| 296 image_filename) | |
| 297 | |
| 298 cmd_result = utils.run("%s check %s" % | |
| 299 (qemu_img_cmd, image_filename), | |
| 300 ignore_status=True) | |
| 301 # Error check, large chances of a non-fatal problem. | |
| 302 # There are chances that bad data was skipped though | |
| 303 if cmd_result.exit_status == 1: | |
| 304 for e_line in cmd_result.stdout.splitlines(): | |
| 305 logging.error("[stdout] %s", e_line) | |
| 306 for e_line in cmd_result.stderr.splitlines(): | |
| 307 logging.error("[stderr] %s", e_line) | |
| 308 raise error.TestWarn("qemu-img check error. Some bad data in " | |
| 309 "the image may have gone unnoticed") | |
| 310 # Exit status 2 is data corruption for sure, so fail the test | |
| 311 elif cmd_result.exit_status == 2: | |
| 312 for e_line in cmd_result.stdout.splitlines(): | |
| 313 logging.error("[stdout] %s", e_line) | |
| 314 for e_line in cmd_result.stderr.splitlines(): | |
| 315 logging.error("[stderr] %s", e_line) | |
| 316 raise VMImageCheckError(image_filename) | |
| 317 # Leaked clusters, they are known to be harmless to data integrity | |
| 318 elif cmd_result.exit_status == 3: | |
| 319 raise error.TestWarn("Leaked clusters were noticed during " | |
| 320 "image check. No data integrity problem " | |
| 321 "was found though.") | |
| 322 | |
| 323 else: | |
| 324 if not os.path.exists(image_filename): | |
| 325 logging.debug("Image file %s not found, skipping check...", | |
| 326 image_filename) | |
| 327 elif not image_is_qcow2: | |
| 328 logging.debug("Image file %s not qcow2, skipping check...", | |
| 329 image_filename) | |
| 330 | |
| 331 | |
| 332 class VM: | |
| 333 """ | 15 """ |
| 334 This class handles all basic VM operations. | 16 This class handles all basic VM operations. |
| 335 """ | 17 """ |
| 336 | 18 |
| 19 MIGRATION_PROTOS = ['tcp', 'unix', 'exec'] |
| 20 |
| 337 def __init__(self, name, params, root_dir, address_cache, state=None): | 21 def __init__(self, name, params, root_dir, address_cache, state=None): |
| 338 """ | 22 """ |
| 339 Initialize the object and set a few attributes. | 23 Initialize the object and set a few attributes. |
| 340 | 24 |
| 341 @param name: The name of the object | 25 @param name: The name of the object |
| 342 @param params: A dict containing VM params | 26 @param params: A dict containing VM params |
| 343 (see method make_qemu_command for a full description) | 27 (see method make_qemu_command for a full description) |
| 344 @param root_dir: Base directory for relative filenames | 28 @param root_dir: Base directory for relative filenames |
| 345 @param address_cache: A dict that maps MAC addresses to IP addresses | 29 @param address_cache: A dict that maps MAC addresses to IP addresses |
| 346 @param state: If provided, use this as self.__dict__ | 30 @param state: If provided, use this as self.__dict__ |
| 347 """ | 31 """ |
| 32 virt_vm.BaseVM.__init__(self, name, params) |
| 33 |
| 348 if state: | 34 if state: |
| 349 self.__dict__ = state | 35 self.__dict__ = state |
| 350 else: | 36 else: |
| 351 self.process = None | 37 self.process = None |
| 352 self.serial_console = None | 38 self.serial_console = None |
| 353 self.redirs = {} | 39 self.redirs = {} |
| 354 self.vnc_port = 5900 | 40 self.vnc_port = 5900 |
| 355 self.monitors = [] | 41 self.monitors = [] |
| 356 self.pci_assignable = None | 42 self.pci_assignable = None |
| 357 self.netdev_id = [] | 43 self.netdev_id = [] |
| 358 self.device_id = [] | 44 self.device_id = [] |
| 359 self.uuid = None | 45 self.uuid = None |
| 360 | 46 |
| 361 # Find a unique identifier for this VM | |
| 362 while True: | |
| 363 self.instance = (time.strftime("%Y%m%d-%H%M%S-") + | |
| 364 kvm_utils.generate_random_string(4)) | |
| 365 if not glob.glob("/tmp/*%s" % self.instance): | |
| 366 break | |
| 367 | 47 |
| 368 self.spice_port = 8000 | 48 self.spice_port = 8000 |
| 369 self.name = name | 49 self.name = name |
| 370 self.params = params | 50 self.params = params |
| 371 self.root_dir = root_dir | 51 self.root_dir = root_dir |
| 372 self.address_cache = address_cache | 52 self.address_cache = address_cache |
| 373 | 53 |
| 374 | 54 |
| 55 def verify_alive(self): |
| 56 """ |
| 57 Make sure the VM is alive and that the main monitor is responsive. |
| 58 |
| 59 @raise VMDeadError: If the VM is dead |
| 60 @raise: Various monitor exceptions if the monitor is unresponsive |
| 61 """ |
| 62 try: |
| 63 virt_vm.BaseVM.verify_alive(self) |
| 64 if self.monitors: |
| 65 self.monitor.verify_responsive() |
| 66 except virt_vm.VMDeadError: |
| 67 raise virt_vm.VMDeadError(self.process.get_status(), |
| 68 self.process.get_output()) |
| 69 |
| 70 |
| 71 def is_alive(self): |
| 72 """ |
| 73 Return True if the VM is alive and its monitor is responsive. |
| 74 """ |
| 75 return not self.is_dead() and (not self.monitors or |
| 76 self.monitor.is_responsive()) |
| 77 |
| 78 |
| 79 def is_dead(self): |
| 80 """ |
| 81 Return True if the qemu process is dead. |
| 82 """ |
| 83 return not self.process or not self.process.is_alive() |
| 84 |
| 85 |
| 86 |
| 87 |
| 375 def clone(self, name=None, params=None, root_dir=None, address_cache=None, | 88 def clone(self, name=None, params=None, root_dir=None, address_cache=None, |
| 376 copy_state=False): | 89 copy_state=False): |
| 377 """ | 90 """ |
| 378 Return a clone of the VM object with optionally modified parameters. | 91 Return a clone of the VM object with optionally modified parameters. |
| 379 The clone is initially not alive and needs to be started using create(). | 92 The clone is initially not alive and needs to be started using create(). |
| 380 Any parameters not passed to this function are copied from the source | 93 Any parameters not passed to this function are copied from the source |
| 381 VM. | 94 VM. |
| 382 | 95 |
| 383 @param name: Optional new VM name | 96 @param name: Optional new VM name |
| 384 @param params: Optional new VM creation parameters | 97 @param params: Optional new VM creation parameters |
| (...skipping 10 matching lines...) Expand all Loading... |
| 395 root_dir = self.root_dir | 108 root_dir = self.root_dir |
| 396 if address_cache is None: | 109 if address_cache is None: |
| 397 address_cache = self.address_cache | 110 address_cache = self.address_cache |
| 398 if copy_state: | 111 if copy_state: |
| 399 state = self.__dict__.copy() | 112 state = self.__dict__.copy() |
| 400 else: | 113 else: |
| 401 state = None | 114 state = None |
| 402 return VM(name, params, root_dir, address_cache, state) | 115 return VM(name, params, root_dir, address_cache, state) |
| 403 | 116 |
| 404 | 117 |
| 405 def make_qemu_command(self, name=None, params=None, root_dir=None): | 118 def __make_qemu_command(self, name=None, params=None, root_dir=None): |
| 406 """ | 119 """ |
| 407 Generate a qemu command line. All parameters are optional. If a | 120 Generate a qemu command line. All parameters are optional. If a |
| 408 parameter is not supplied, the corresponding value stored in the | 121 parameter is not supplied, the corresponding value stored in the |
| 409 class attributes is used. | 122 class attributes is used. |
| 410 | 123 |
| 411 @param name: The name of the object | 124 @param name: The name of the object |
| 412 @param params: A dict containing VM params | 125 @param params: A dict containing VM params |
| 413 @param root_dir: Base directory for relative filenames | 126 @param root_dir: Base directory for relative filenames |
| 414 | 127 |
| 415 @note: The params dict should contain: | 128 @note: The params dict should contain: |
| (...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 628 if name is None: | 341 if name is None: |
| 629 name = self.name | 342 name = self.name |
| 630 if params is None: | 343 if params is None: |
| 631 params = self.params | 344 params = self.params |
| 632 if root_dir is None: | 345 if root_dir is None: |
| 633 root_dir = self.root_dir | 346 root_dir = self.root_dir |
| 634 | 347 |
| 635 # Clone this VM using the new params | 348 # Clone this VM using the new params |
| 636 vm = self.clone(name, params, root_dir, copy_state=True) | 349 vm = self.clone(name, params, root_dir, copy_state=True) |
| 637 | 350 |
| 638 qemu_binary = kvm_utils.get_path(root_dir, params.get("qemu_binary", | 351 qemu_binary = virt_utils.get_path(root_dir, params.get("qemu_binary", |
| 639 "qemu")) | 352 "qemu")) |
| 640 # Get the output of 'qemu -help' (log a message in case this call never | 353 # Get the output of 'qemu -help' (log a message in case this call never |
| 641 # returns or causes some other kind of trouble) | 354 # returns or causes some other kind of trouble) |
| 642 logging.debug("Getting output of 'qemu -help'") | 355 logging.debug("Getting output of 'qemu -help'") |
| 643 help = commands.getoutput("%s -help" % qemu_binary) | 356 help = commands.getoutput("%s -help" % qemu_binary) |
| 644 | 357 |
| 645 # Start constructing the qemu command | 358 # Start constructing the qemu command |
| 646 qemu_cmd = "" | 359 qemu_cmd = "" |
| 647 # Set the X11 display parameter if requested | 360 # Set the X11 display parameter if requested |
| 648 if params.get("x11_display"): | 361 if params.get("x11_display"): |
| (...skipping 12 matching lines...) Expand all Loading... |
| 661 qemu_cmd += add_human_monitor(help, monitor_filename) | 374 qemu_cmd += add_human_monitor(help, monitor_filename) |
| 662 | 375 |
| 663 # Add serial console redirection | 376 # Add serial console redirection |
| 664 qemu_cmd += add_serial(help, vm.get_serial_console_filename()) | 377 qemu_cmd += add_serial(help, vm.get_serial_console_filename()) |
| 665 | 378 |
| 666 for image_name in params.objects("images"): | 379 for image_name in params.objects("images"): |
| 667 image_params = params.object_params(image_name) | 380 image_params = params.object_params(image_name) |
| 668 if image_params.get("boot_drive") == "no": | 381 if image_params.get("boot_drive") == "no": |
| 669 continue | 382 continue |
| 670 qemu_cmd += add_drive(help, | 383 qemu_cmd += add_drive(help, |
| 671 get_image_filename(image_params, root_dir), | 384 virt_vm.get_image_filename(image_params, root_dir), |
| 672 image_params.get("drive_index"), | 385 image_params.get("drive_index"), |
| 673 image_params.get("drive_format"), | 386 image_params.get("drive_format"), |
| 674 image_params.get("drive_cache"), | 387 image_params.get("drive_cache"), |
| 675 image_params.get("drive_werror"), | 388 image_params.get("drive_werror"), |
| 676 image_params.get("drive_serial"), | 389 image_params.get("drive_serial"), |
| 677 image_params.get("image_snapshot") == "yes", | 390 image_params.get("image_snapshot") == "yes", |
| 678 image_params.get("image_boot") == "yes") | 391 image_params.get("image_boot") == "yes") |
| 679 | 392 |
| 680 redirs = [] | 393 redirs = [] |
| 681 for redir_name in params.objects("redirs"): | 394 for redir_name in params.objects("redirs"): |
| 682 redir_params = params.object_params(redir_name) | 395 redir_params = params.object_params(redir_name) |
| 683 guest_port = int(redir_params.get("guest_port")) | 396 guest_port = int(redir_params.get("guest_port")) |
| 684 host_port = vm.redirs.get(guest_port) | 397 host_port = vm.redirs.get(guest_port) |
| 685 redirs += [(host_port, guest_port)] | 398 redirs += [(host_port, guest_port)] |
| 686 | 399 |
| 687 vlan = 0 | 400 vlan = 0 |
| 688 for nic_name in params.objects("nics"): | 401 for nic_name in params.objects("nics"): |
| 689 nic_params = params.object_params(nic_name) | 402 nic_params = params.object_params(nic_name) |
| 690 try: | 403 try: |
| 691 netdev_id = vm.netdev_id[vlan] | 404 netdev_id = vm.netdev_id[vlan] |
| 692 device_id = vm.device_id[vlan] | 405 device_id = vm.device_id[vlan] |
| 693 except IndexError: | 406 except IndexError: |
| 694 netdev_id = None | 407 netdev_id = None |
| 695 # Handle the '-net nic' part | 408 # Handle the '-net nic' part |
| 696 try: | 409 try: |
| 697 mac = vm.get_mac_address(vlan) | 410 mac = vm.get_mac_address(vlan) |
| 698 except VMAddressError: | 411 except virt_vm.VMAddressError: |
| 699 mac = None | 412 mac = None |
| 700 qemu_cmd += add_nic(help, vlan, nic_params.get("nic_model"), mac, | 413 qemu_cmd += add_nic(help, vlan, nic_params.get("nic_model"), mac, |
| 701 device_id, netdev_id, nic_params.get("nic_extra_
params")) | 414 device_id, netdev_id, nic_params.get("nic_extra_
params")) |
| 702 # Handle the '-net tap' or '-net user' or '-netdev' part | 415 # Handle the '-net tap' or '-net user' or '-netdev' part |
| 703 script = nic_params.get("nic_script") | 416 script = nic_params.get("nic_script") |
| 704 downscript = nic_params.get("nic_downscript") | 417 downscript = nic_params.get("nic_downscript") |
| 705 tftp = nic_params.get("tftp") | 418 tftp = nic_params.get("tftp") |
| 706 if script: | 419 if script: |
| 707 script = kvm_utils.get_path(root_dir, script) | 420 script = virt_utils.get_path(root_dir, script) |
| 708 if downscript: | 421 if downscript: |
| 709 downscript = kvm_utils.get_path(root_dir, downscript) | 422 downscript = virt_utils.get_path(root_dir, downscript) |
| 710 if tftp: | 423 if tftp: |
| 711 tftp = kvm_utils.get_path(root_dir, tftp) | 424 tftp = virt_utils.get_path(root_dir, tftp) |
| 712 qemu_cmd += add_net(help, vlan, nic_params.get("nic_mode", "user"), | 425 qemu_cmd += add_net(help, vlan, nic_params.get("nic_mode", "user"), |
| 713 vm.get_ifname(vlan), | 426 vm.get_ifname(vlan), |
| 714 script, downscript, tftp, | 427 script, downscript, tftp, |
| 715 nic_params.get("bootp"), redirs, netdev_id, | 428 nic_params.get("bootp"), redirs, netdev_id, |
| 716 nic_params.get("netdev_extra_params")) | 429 nic_params.get("netdev_extra_params")) |
| 717 # Proceed to next NIC | 430 # Proceed to next NIC |
| 718 vlan += 1 | 431 vlan += 1 |
| 719 | 432 |
| 720 mem = params.get("mem") | 433 mem = params.get("mem") |
| 721 if mem: | 434 if mem: |
| 722 qemu_cmd += add_mem(help, mem) | 435 qemu_cmd += add_mem(help, mem) |
| 723 | 436 |
| 724 smp = params.get("smp") | 437 smp = params.get("smp") |
| 725 if smp: | 438 if smp: |
| 726 qemu_cmd += add_smp(help, smp) | 439 qemu_cmd += add_smp(help, smp) |
| 727 | 440 |
| 728 for cdrom in params.objects("cdroms"): | 441 for cdrom in params.objects("cdroms"): |
| 729 cdrom_params = params.object_params(cdrom) | 442 cdrom_params = params.object_params(cdrom) |
| 730 iso = cdrom_params.get("cdrom") | 443 iso = cdrom_params.get("cdrom") |
| 731 if iso: | 444 if iso: |
| 732 qemu_cmd += add_cdrom(help, kvm_utils.get_path(root_dir, iso), | 445 qemu_cmd += add_cdrom(help, virt_utils.get_path(root_dir, iso), |
| 733 cdrom_params.get("drive_index")) | 446 cdrom_params.get("drive_index")) |
| 734 | 447 |
| 735 # We may want to add {floppy_otps} parameter for -fda | 448 # We may want to add {floppy_otps} parameter for -fda |
| 736 # {fat:floppy:}/path/. However vvfat is not usually recommended. | 449 # {fat:floppy:}/path/. However vvfat is not usually recommended. |
| 737 floppy = params.get("floppy") | 450 floppy = params.get("floppy") |
| 738 if floppy: | 451 if floppy: |
| 739 floppy = kvm_utils.get_path(root_dir, floppy) | 452 floppy = virt_utils.get_path(root_dir, floppy) |
| 740 qemu_cmd += add_floppy(help, floppy) | 453 qemu_cmd += add_floppy(help, floppy) |
| 741 | 454 |
| 742 tftp = params.get("tftp") | 455 tftp = params.get("tftp") |
| 743 if tftp: | 456 if tftp: |
| 744 tftp = kvm_utils.get_path(root_dir, tftp) | 457 tftp = virt_utils.get_path(root_dir, tftp) |
| 745 qemu_cmd += add_tftp(help, tftp) | 458 qemu_cmd += add_tftp(help, tftp) |
| 746 | 459 |
| 747 bootp = params.get("bootp") | 460 bootp = params.get("bootp") |
| 748 if bootp: | 461 if bootp: |
| 749 qemu_cmd += add_bootp(help, bootp) | 462 qemu_cmd += add_bootp(help, bootp) |
| 750 | 463 |
| 751 kernel = params.get("kernel") | 464 kernel = params.get("kernel") |
| 752 if kernel: | 465 if kernel: |
| 753 kernel = kvm_utils.get_path(root_dir, kernel) | 466 kernel = virt_utils.get_path(root_dir, kernel) |
| 754 qemu_cmd += add_kernel(help, kernel) | 467 qemu_cmd += add_kernel(help, kernel) |
| 755 | 468 |
| 756 kernel_cmdline = params.get("kernel_cmdline") | 469 kernel_cmdline = params.get("kernel_cmdline") |
| 757 if kernel_cmdline: | 470 if kernel_cmdline: |
| 758 qemu_cmd += add_kernel_cmdline(help, kernel_cmdline) | 471 qemu_cmd += add_kernel_cmdline(help, kernel_cmdline) |
| 759 | 472 |
| 760 initrd = params.get("initrd") | 473 initrd = params.get("initrd") |
| 761 if initrd: | 474 if initrd: |
| 762 initrd = kvm_utils.get_path(root_dir, initrd) | 475 initrd = virt_utils.get_path(root_dir, initrd) |
| 763 qemu_cmd += add_initrd(help, initrd) | 476 qemu_cmd += add_initrd(help, initrd) |
| 764 | 477 |
| 765 for host_port, guest_port in redirs: | 478 for host_port, guest_port in redirs: |
| 766 qemu_cmd += add_tcp_redir(help, host_port, guest_port) | 479 qemu_cmd += add_tcp_redir(help, host_port, guest_port) |
| 767 | 480 |
| 768 if params.get("display") == "vnc": | 481 if params.get("display") == "vnc": |
| 769 qemu_cmd += add_vnc(help, vm.vnc_port) | 482 qemu_cmd += add_vnc(help, vm.vnc_port) |
| 770 elif params.get("display") == "sdl": | 483 elif params.get("display") == "sdl": |
| 771 qemu_cmd += add_sdl(help) | 484 qemu_cmd += add_sdl(help) |
| 772 elif params.get("display") == "nographic": | 485 elif params.get("display") == "nographic": |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 848 self.root_dir = root_dir | 561 self.root_dir = root_dir |
| 849 name = self.name | 562 name = self.name |
| 850 params = self.params | 563 params = self.params |
| 851 root_dir = self.root_dir | 564 root_dir = self.root_dir |
| 852 | 565 |
| 853 # Verify the md5sum of the ISO images | 566 # Verify the md5sum of the ISO images |
| 854 for cdrom in params.objects("cdroms"): | 567 for cdrom in params.objects("cdroms"): |
| 855 cdrom_params = params.object_params(cdrom) | 568 cdrom_params = params.object_params(cdrom) |
| 856 iso = cdrom_params.get("cdrom") | 569 iso = cdrom_params.get("cdrom") |
| 857 if iso: | 570 if iso: |
| 858 iso = kvm_utils.get_path(root_dir, iso) | 571 iso = virt_utils.get_path(root_dir, iso) |
| 859 if not os.path.exists(iso): | 572 if not os.path.exists(iso): |
| 860 raise VMImageMissingError(iso) | 573 raise virt_vm.VMImageMissingError(iso) |
| 861 compare = False | 574 compare = False |
| 862 if cdrom_params.get("md5sum_1m"): | 575 if cdrom_params.get("md5sum_1m"): |
| 863 logging.debug("Comparing expected MD5 sum with MD5 sum of " | 576 logging.debug("Comparing expected MD5 sum with MD5 sum of " |
| 864 "first MB of ISO file...") | 577 "first MB of ISO file...") |
| 865 actual_hash = utils.hash_file(iso, 1048576, method="md5") | 578 actual_hash = utils.hash_file(iso, 1048576, method="md5") |
| 866 expected_hash = cdrom_params.get("md5sum_1m") | 579 expected_hash = cdrom_params.get("md5sum_1m") |
| 867 compare = True | 580 compare = True |
| 868 elif cdrom_params.get("md5sum"): | 581 elif cdrom_params.get("md5sum"): |
| 869 logging.debug("Comparing expected MD5 sum with MD5 sum of " | 582 logging.debug("Comparing expected MD5 sum with MD5 sum of " |
| 870 "ISO file...") | 583 "ISO file...") |
| 871 actual_hash = utils.hash_file(iso, method="md5") | 584 actual_hash = utils.hash_file(iso, method="md5") |
| 872 expected_hash = cdrom_params.get("md5sum") | 585 expected_hash = cdrom_params.get("md5sum") |
| 873 compare = True | 586 compare = True |
| 874 elif cdrom_params.get("sha1sum"): | 587 elif cdrom_params.get("sha1sum"): |
| 875 logging.debug("Comparing expected SHA1 sum with SHA1 sum " | 588 logging.debug("Comparing expected SHA1 sum with SHA1 sum " |
| 876 "of ISO file...") | 589 "of ISO file...") |
| 877 actual_hash = utils.hash_file(iso, method="sha1") | 590 actual_hash = utils.hash_file(iso, method="sha1") |
| 878 expected_hash = cdrom_params.get("sha1sum") | 591 expected_hash = cdrom_params.get("sha1sum") |
| 879 compare = True | 592 compare = True |
| 880 if compare: | 593 if compare: |
| 881 if actual_hash == expected_hash: | 594 if actual_hash == expected_hash: |
| 882 logging.debug("Hashes match") | 595 logging.debug("Hashes match") |
| 883 else: | 596 else: |
| 884 raise VMHashMismatchError(actual_hash, expected_hash) | 597 raise virt_vm.VMHashMismatchError(actual_hash, |
| 598 expected_hash) |
| 885 | 599 |
| 886 # Make sure the following code is not executed by more than one thread | 600 # Make sure the following code is not executed by more than one thread |
| 887 # at the same time | 601 # at the same time |
| 888 lockfile = open("/tmp/kvm-autotest-vm-create.lock", "w+") | 602 lockfile = open("/tmp/kvm-autotest-vm-create.lock", "w+") |
| 889 fcntl.lockf(lockfile, fcntl.LOCK_EX) | 603 fcntl.lockf(lockfile, fcntl.LOCK_EX) |
| 890 | 604 |
| 891 try: | 605 try: |
| 892 # Handle port redirections | 606 # Handle port redirections |
| 893 redir_names = params.objects("redirs") | 607 redir_names = params.objects("redirs") |
| 894 host_ports = kvm_utils.find_free_ports(5000, 6000, len(redir_names)) | 608 host_ports = virt_utils.find_free_ports(5000, 6000, len(redir_names)
) |
| 895 self.redirs = {} | 609 self.redirs = {} |
| 896 for i in range(len(redir_names)): | 610 for i in range(len(redir_names)): |
| 897 redir_params = params.object_params(redir_names[i]) | 611 redir_params = params.object_params(redir_names[i]) |
| 898 guest_port = int(redir_params.get("guest_port")) | 612 guest_port = int(redir_params.get("guest_port")) |
| 899 self.redirs[guest_port] = host_ports[i] | 613 self.redirs[guest_port] = host_ports[i] |
| 900 | 614 |
| 901 # Generate netdev/device IDs for all NICs | 615 # Generate netdev/device IDs for all NICs |
| 902 self.netdev_id = [] | 616 self.netdev_id = [] |
| 903 self.device_id = [] | 617 self.device_id = [] |
| 904 for nic in params.objects("nics"): | 618 for nic in params.objects("nics"): |
| 905 self.netdev_id.append(kvm_utils.generate_random_id()) | 619 self.netdev_id.append(virt_utils.generate_random_id()) |
| 906 self.device_id.append(kvm_utils.generate_random_id()) | 620 self.device_id.append(virt_utils.generate_random_id()) |
| 907 | 621 |
| 908 # Find available VNC port, if needed | 622 # Find available VNC port, if needed |
| 909 if params.get("display") == "vnc": | 623 if params.get("display") == "vnc": |
| 910 self.vnc_port = kvm_utils.find_free_port(5900, 6100) | 624 self.vnc_port = virt_utils.find_free_port(5900, 6100) |
| 911 | 625 |
| 912 # Find available spice port | 626 # Find available spice port, if needed |
| 913 if params.get("spice"): | 627 if params.get("spice"): |
| 914 self.spice_port = kvm_utils.find_free_port(8000, 8100) | 628 self.spice_port = virt_utils.find_free_port(8000, 8100) |
| 915 | 629 |
| 916 # Find random UUID if specified 'uuid = random' in config file | 630 # Find random UUID if specified 'uuid = random' in config file |
| 917 if params.get("uuid") == "random": | 631 if params.get("uuid") == "random": |
| 918 f = open("/proc/sys/kernel/random/uuid") | 632 f = open("/proc/sys/kernel/random/uuid") |
| 919 self.uuid = f.read().strip() | 633 self.uuid = f.read().strip() |
| 920 f.close() | 634 f.close() |
| 921 | 635 |
| 922 # Generate or copy MAC addresses for all NICs | 636 # Generate or copy MAC addresses for all NICs |
| 923 num_nics = len(params.objects("nics")) | 637 num_nics = len(params.objects("nics")) |
| 924 for vlan in range(num_nics): | 638 for vlan in range(num_nics): |
| 925 nic_name = params.objects("nics")[vlan] | 639 nic_name = params.objects("nics")[vlan] |
| 926 nic_params = params.object_params(nic_name) | 640 nic_params = params.object_params(nic_name) |
| 927 mac = (nic_params.get("nic_mac") or | 641 mac = (nic_params.get("nic_mac") or |
| 928 mac_source and mac_source.get_mac_address(vlan)) | 642 mac_source and mac_source.get_mac_address(vlan)) |
| 929 if mac: | 643 if mac: |
| 930 kvm_utils.set_mac_address(self.instance, vlan, mac) | 644 virt_utils.set_mac_address(self.instance, vlan, mac) |
| 931 else: | 645 else: |
| 932 kvm_utils.generate_mac_address(self.instance, vlan) | 646 virt_utils.generate_mac_address(self.instance, vlan) |
| 933 | 647 |
| 934 # Assign a PCI assignable device | 648 # Assign a PCI assignable device |
| 935 self.pci_assignable = None | 649 self.pci_assignable = None |
| 936 pa_type = params.get("pci_assignable") | 650 pa_type = params.get("pci_assignable") |
| 937 if pa_type and pa_type != "no": | 651 if pa_type and pa_type != "no": |
| 938 pa_devices_requested = params.get("devices_requested") | 652 pa_devices_requested = params.get("devices_requested") |
| 939 | 653 |
| 940 # Virtual Functions (VF) assignable devices | 654 # Virtual Functions (VF) assignable devices |
| 941 if pa_type == "vf": | 655 if pa_type == "vf": |
| 942 self.pci_assignable = kvm_utils.PciAssignable( | 656 self.pci_assignable = virt_utils.PciAssignable( |
| 943 type=pa_type, | 657 type=pa_type, |
| 944 driver=params.get("driver"), | 658 driver=params.get("driver"), |
| 945 driver_option=params.get("driver_option"), | 659 driver_option=params.get("driver_option"), |
| 946 devices_requested=pa_devices_requested) | 660 devices_requested=pa_devices_requested) |
| 947 # Physical NIC (PF) assignable devices | 661 # Physical NIC (PF) assignable devices |
| 948 elif pa_type == "pf": | 662 elif pa_type == "pf": |
| 949 self.pci_assignable = kvm_utils.PciAssignable( | 663 self.pci_assignable = virt_utils.PciAssignable( |
| 950 type=pa_type, | 664 type=pa_type, |
| 951 names=params.get("device_names"), | 665 names=params.get("device_names"), |
| 952 devices_requested=pa_devices_requested) | 666 devices_requested=pa_devices_requested) |
| 953 # Working with both VF and PF | 667 # Working with both VF and PF |
| 954 elif pa_type == "mixed": | 668 elif pa_type == "mixed": |
| 955 self.pci_assignable = kvm_utils.PciAssignable( | 669 self.pci_assignable = virt_utils.PciAssignable( |
| 956 type=pa_type, | 670 type=pa_type, |
| 957 driver=params.get("driver"), | 671 driver=params.get("driver"), |
| 958 driver_option=params.get("driver_option"), | 672 driver_option=params.get("driver_option"), |
| 959 names=params.get("device_names"), | 673 names=params.get("device_names"), |
| 960 devices_requested=pa_devices_requested) | 674 devices_requested=pa_devices_requested) |
| 961 else: | 675 else: |
| 962 raise VMBadPATypeError(pa_type) | 676 raise virt_vm.VMBadPATypeError(pa_type) |
| 963 | 677 |
| 964 self.pa_pci_ids = self.pci_assignable.request_devs() | 678 self.pa_pci_ids = self.pci_assignable.request_devs() |
| 965 | 679 |
| 966 if self.pa_pci_ids: | 680 if self.pa_pci_ids: |
| 967 logging.debug("Successfuly assigned devices: %s", | 681 logging.debug("Successfuly assigned devices: %s", |
| 968 self.pa_pci_ids) | 682 self.pa_pci_ids) |
| 969 else: | 683 else: |
| 970 raise VMPAError(pa_type) | 684 raise virt_vm.VMPAError(pa_type) |
| 971 | 685 |
| 972 # Make qemu command | 686 # Make qemu command |
| 973 qemu_command = self.make_qemu_command() | 687 qemu_command = self.__make_qemu_command() |
| 974 | 688 |
| 975 # Add migration parameters if required | 689 # Add migration parameters if required |
| 976 if migration_mode == "tcp": | 690 if migration_mode == "tcp": |
| 977 self.migration_port = kvm_utils.find_free_port(5200, 6000) | 691 self.migration_port = virt_utils.find_free_port(5200, 6000) |
| 978 qemu_command += " -incoming tcp:0:%d" % self.migration_port | 692 qemu_command += " -incoming tcp:0:%d" % self.migration_port |
| 979 elif migration_mode == "unix": | 693 elif migration_mode == "unix": |
| 980 self.migration_file = "/tmp/migration-unix-%s" % self.instance | 694 self.migration_file = "/tmp/migration-unix-%s" % self.instance |
| 981 qemu_command += " -incoming unix:%s" % self.migration_file | 695 qemu_command += " -incoming unix:%s" % self.migration_file |
| 982 elif migration_mode == "exec": | 696 elif migration_mode == "exec": |
| 983 self.migration_port = kvm_utils.find_free_port(5200, 6000) | 697 self.migration_port = virt_utils.find_free_port(5200, 6000) |
| 984 qemu_command += (' -incoming "exec:nc -l %s"' % | 698 qemu_command += (' -incoming "exec:nc -l %s"' % |
| 985 self.migration_port) | 699 self.migration_port) |
| 986 | 700 |
| 987 logging.info("Running qemu command:\n%s", qemu_command) | 701 logging.info("Running qemu command:\n%s", qemu_command) |
| 988 self.process = kvm_subprocess.run_bg(qemu_command, None, | 702 self.process = aexpect.run_bg(qemu_command, None, |
| 989 logging.info, "(qemu) ") | 703 logging.info, "(qemu) ") |
| 990 | 704 |
| 991 # Make sure the process was started successfully | 705 # Make sure the process was started successfully |
| 992 if not self.process.is_alive(): | 706 if not self.process.is_alive(): |
| 993 e = VMCreateError(qemu_command, | 707 e = virt_vm.VMCreateError(qemu_command, |
| 994 self.process.get_status(), | 708 self.process.get_status(), |
| 995 self.process.get_output()) | 709 self.process.get_output()) |
| 996 self.destroy() | 710 self.destroy() |
| 997 raise e | 711 raise e |
| 998 | 712 |
| 999 # Establish monitor connections | 713 # Establish monitor connections |
| 1000 self.monitors = [] | 714 self.monitors = [] |
| 1001 for monitor_name in params.objects("monitors"): | 715 for monitor_name in params.objects("monitors"): |
| 1002 monitor_params = params.object_params(monitor_name) | 716 monitor_params = params.object_params(monitor_name) |
| 1003 # Wait for monitor connection to succeed | 717 # Wait for monitor connection to succeed |
| 1004 end_time = time.time() + timeout | 718 end_time = time.time() + timeout |
| 1005 while time.time() < end_time: | 719 while time.time() < end_time: |
| (...skipping 17 matching lines...) Expand all Loading... |
| 1023 self.destroy() | 737 self.destroy() |
| 1024 raise e | 738 raise e |
| 1025 # Add this monitor to the list | 739 # Add this monitor to the list |
| 1026 self.monitors += [monitor] | 740 self.monitors += [monitor] |
| 1027 | 741 |
| 1028 # Get the output so far, to see if we have any problems with | 742 # Get the output so far, to see if we have any problems with |
| 1029 # KVM modules or with hugepage setup. | 743 # KVM modules or with hugepage setup. |
| 1030 output = self.process.get_output() | 744 output = self.process.get_output() |
| 1031 | 745 |
| 1032 if re.search("Could not initialize KVM", output, re.IGNORECASE): | 746 if re.search("Could not initialize KVM", output, re.IGNORECASE): |
| 1033 e = VMKVMInitError(qemu_command, self.process.get_output()) | 747 e = virt_vm.VMKVMInitError(qemu_command, self.process.get_output
()) |
| 1034 self.destroy() | 748 self.destroy() |
| 1035 raise e | 749 raise e |
| 1036 | 750 |
| 1037 if "alloc_mem_area" in output: | 751 if "alloc_mem_area" in output: |
| 1038 e = VMHugePageError(qemu_command, self.process.get_output()) | 752 e = virt_vm.VMHugePageError(qemu_command, self.process.get_outpu
t()) |
| 1039 self.destroy() | 753 self.destroy() |
| 1040 raise e | 754 raise e |
| 1041 | 755 |
| 1042 logging.debug("VM appears to be alive with PID %s", self.get_pid()) | 756 logging.debug("VM appears to be alive with PID %s", self.get_pid()) |
| 1043 | 757 |
| 1044 # Establish a session with the serial console -- requires a version | 758 # Establish a session with the serial console -- requires a version |
| 1045 # of netcat that supports -U | 759 # of netcat that supports -U |
| 1046 self.serial_console = kvm_subprocess.ShellSession( | 760 self.serial_console = aexpect.ShellSession( |
| 1047 "nc -U %s" % self.get_serial_console_filename(), | 761 "nc -U %s" % self.get_serial_console_filename(), |
| 1048 auto_close=False, | 762 auto_close=False, |
| 1049 output_func=kvm_utils.log_line, | 763 output_func=virt_utils.log_line, |
| 1050 output_params=("serial-%s.log" % name,)) | 764 output_params=("serial-%s.log" % name,)) |
| 1051 | 765 |
| 1052 finally: | 766 finally: |
| 1053 fcntl.lockf(lockfile, fcntl.LOCK_UN) | 767 fcntl.lockf(lockfile, fcntl.LOCK_UN) |
| 1054 lockfile.close() | 768 lockfile.close() |
| 1055 | 769 |
| 1056 | 770 |
| 1057 def destroy(self, gracefully=True, free_mac_addresses=True): | 771 def destroy(self, gracefully=True, free_mac_addresses=True): |
| 1058 """ | 772 """ |
| 1059 Destroy the VM. | 773 Destroy the VM. |
| (...skipping 13 matching lines...) Expand all Loading... |
| 1073 if self.is_dead(): | 787 if self.is_dead(): |
| 1074 return | 788 return |
| 1075 | 789 |
| 1076 logging.debug("Destroying VM with PID %s...", self.get_pid()) | 790 logging.debug("Destroying VM with PID %s...", self.get_pid()) |
| 1077 | 791 |
| 1078 if gracefully and self.params.get("shutdown_command"): | 792 if gracefully and self.params.get("shutdown_command"): |
| 1079 # Try to destroy with shell command | 793 # Try to destroy with shell command |
| 1080 logging.debug("Trying to shutdown VM with shell command...") | 794 logging.debug("Trying to shutdown VM with shell command...") |
| 1081 try: | 795 try: |
| 1082 session = self.login() | 796 session = self.login() |
| 1083 except (kvm_utils.LoginError, VMError), e: | 797 except (virt_utils.LoginError, virt_vm.VMError), e: |
| 1084 logging.debug(e) | 798 logging.debug(e) |
| 1085 else: | 799 else: |
| 1086 try: | 800 try: |
| 1087 # Send the shutdown command | 801 # Send the shutdown command |
| 1088 session.sendline(self.params.get("shutdown_command")) | 802 session.sendline(self.params.get("shutdown_command")) |
| 1089 logging.debug("Shutdown command sent; waiting for VM " | 803 logging.debug("Shutdown command sent; waiting for VM " |
| 1090 "to go down...") | 804 "to go down...") |
| 1091 if kvm_utils.wait_for(self.is_dead, 60, 1, 1): | 805 if virt_utils.wait_for(self.is_dead, 60, 1, 1): |
| 1092 logging.debug("VM is down") | 806 logging.debug("VM is down") |
| 1093 return | 807 return |
| 1094 finally: | 808 finally: |
| 1095 session.close() | 809 session.close() |
| 1096 | 810 |
| 1097 if self.monitor: | 811 if self.monitor: |
| 1098 # Try to destroy with a monitor command | 812 # Try to destroy with a monitor command |
| 1099 logging.debug("Trying to kill VM with monitor command...") | 813 logging.debug("Trying to kill VM with monitor command...") |
| 1100 try: | 814 try: |
| 1101 self.monitor.quit() | 815 self.monitor.quit() |
| 1102 except kvm_monitor.MonitorError, e: | 816 except kvm_monitor.MonitorError, e: |
| 1103 logging.warn(e) | 817 logging.warn(e) |
| 1104 else: | 818 else: |
| 1105 # Wait for the VM to be really dead | 819 # Wait for the VM to be really dead |
| 1106 if kvm_utils.wait_for(self.is_dead, 5, 0.5, 0.5): | 820 if virt_utils.wait_for(self.is_dead, 5, 0.5, 0.5): |
| 1107 logging.debug("VM is down") | 821 logging.debug("VM is down") |
| 1108 return | 822 return |
| 1109 | 823 |
| 1110 # If the VM isn't dead yet... | 824 # If the VM isn't dead yet... |
| 1111 logging.debug("Cannot quit normally; sending a kill to close the " | 825 logging.debug("Cannot quit normally; sending a kill to close the " |
| 1112 "deal...") | 826 "deal...") |
| 1113 kvm_utils.kill_process_tree(self.process.get_pid(), 9) | 827 virt_utils.kill_process_tree(self.process.get_pid(), 9) |
| 1114 # Wait for the VM to be really dead | 828 # Wait for the VM to be really dead |
| 1115 if kvm_utils.wait_for(self.is_dead, 5, 0.5, 0.5): | 829 if virt_utils.wait_for(self.is_dead, 5, 0.5, 0.5): |
| 1116 logging.debug("VM is down") | 830 logging.debug("VM is down") |
| 1117 return | 831 return |
| 1118 | 832 |
| 1119 logging.error("Process %s is a zombie!", self.process.get_pid()) | 833 logging.error("Process %s is a zombie!", self.process.get_pid()) |
| 1120 | 834 |
| 1121 finally: | 835 finally: |
| 1122 self.monitors = [] | 836 self.monitors = [] |
| 1123 if self.pci_assignable: | 837 if self.pci_assignable: |
| 1124 self.pci_assignable.release_devs() | 838 self.pci_assignable.release_devs() |
| 1125 if self.process: | 839 if self.process: |
| (...skipping 26 matching lines...) Expand all Loading... |
| 1152 If no monitors exist, or if main_monitor refers to a nonexistent | 866 If no monitors exist, or if main_monitor refers to a nonexistent |
| 1153 monitor, return None. | 867 monitor, return None. |
| 1154 """ | 868 """ |
| 1155 for m in self.monitors: | 869 for m in self.monitors: |
| 1156 if m.name == self.params.get("main_monitor"): | 870 if m.name == self.params.get("main_monitor"): |
| 1157 return m | 871 return m |
| 1158 if self.monitors and not self.params.get("main_monitor"): | 872 if self.monitors and not self.params.get("main_monitor"): |
| 1159 return self.monitors[0] | 873 return self.monitors[0] |
| 1160 | 874 |
| 1161 | 875 |
| 1162 def verify_alive(self): | |
| 1163 """ | |
| 1164 Make sure the VM is alive and that the main monitor is responsive. | |
| 1165 | |
| 1166 @raise VMDeadError: If the VM is dead | |
| 1167 @raise: Various monitor exceptions if the monitor is unresponsive | |
| 1168 """ | |
| 1169 if self.is_dead(): | |
| 1170 raise VMDeadError(self.process.get_status(), | |
| 1171 self.process.get_output()) | |
| 1172 if self.monitors: | |
| 1173 self.monitor.verify_responsive() | |
| 1174 | |
| 1175 | |
| 1176 def is_alive(self): | |
| 1177 """ | |
| 1178 Return True if the VM is alive and its monitor is responsive. | |
| 1179 """ | |
| 1180 return not self.is_dead() and (not self.monitors or | |
| 1181 self.monitor.is_responsive()) | |
| 1182 | |
| 1183 | |
| 1184 def is_dead(self): | |
| 1185 """ | |
| 1186 Return True if the qemu process is dead. | |
| 1187 """ | |
| 1188 return not self.process or not self.process.is_alive() | |
| 1189 | |
| 1190 | |
| 1191 def verify_kernel_crash(self): | |
| 1192 """ | |
| 1193 Find kernel crash message on the VM serial console. | |
| 1194 | |
| 1195 @raise: VMDeadKernelCrashError, in case a kernel crash message was | |
| 1196 found. | |
| 1197 """ | |
| 1198 data = self.serial_console.get_output() | |
| 1199 match = re.search(r"BUG:.*---\[ end trace .* \]---", data, | |
| 1200 re.DOTALL|re.MULTILINE) | |
| 1201 if match is not None: | |
| 1202 raise VMDeadKernelCrashError(match.group(0)) | |
| 1203 | |
| 1204 | |
| 1205 def get_params(self): | |
| 1206 """ | |
| 1207 Return the VM's params dict. Most modified params take effect only | |
| 1208 upon VM.create(). | |
| 1209 """ | |
| 1210 return self.params | |
| 1211 | |
| 1212 | |
| 1213 def get_monitor_filename(self, monitor_name): | 876 def get_monitor_filename(self, monitor_name): |
| 1214 """ | 877 """ |
| 1215 Return the filename corresponding to a given monitor name. | 878 Return the filename corresponding to a given monitor name. |
| 1216 """ | 879 """ |
| 1217 return "/tmp/monitor-%s-%s" % (monitor_name, self.instance) | 880 return "/tmp/monitor-%s-%s" % (monitor_name, self.instance) |
| 1218 | 881 |
| 1219 | 882 |
| 1220 def get_monitor_filenames(self): | 883 def get_monitor_filenames(self): |
| 1221 """ | 884 """ |
| 1222 Return a list of all monitor filenames (as specified in the VM's | 885 Return a list of all monitor filenames (as specified in the VM's |
| 1223 params). | 886 params). |
| 1224 """ | 887 """ |
| 1225 return [self.get_monitor_filename(m) for m in | 888 return [self.get_monitor_filename(m) for m in |
| 1226 self.params.objects("monitors")] | 889 self.params.objects("monitors")] |
| 1227 | 890 |
| 1228 | 891 |
| 1229 def get_serial_console_filename(self): | |
| 1230 """ | |
| 1231 Return the serial console filename. | |
| 1232 """ | |
| 1233 return "/tmp/serial-%s" % self.instance | |
| 1234 | |
| 1235 | |
| 1236 def get_testlog_filename(self): | |
| 1237 """ | |
| 1238 Return the testlog filename. | |
| 1239 """ | |
| 1240 return "/tmp/testlog-%s" % self.instance | |
| 1241 | |
| 1242 | |
| 1243 def get_address(self, index=0): | 892 def get_address(self, index=0): |
| 1244 """ | 893 """ |
| 1245 Return the address of a NIC of the guest, in host space. | 894 Return the address of a NIC of the guest, in host space. |
| 1246 | 895 |
| 1247 If port redirection is used, return 'localhost' (the NIC has no IP | 896 If port redirection is used, return 'localhost' (the NIC has no IP |
| 1248 address of its own). Otherwise return the NIC's IP address. | 897 address of its own). Otherwise return the NIC's IP address. |
| 1249 | 898 |
| 1250 @param index: Index of the NIC whose address is requested. | 899 @param index: Index of the NIC whose address is requested. |
| 1251 @raise VMMACAddressMissingError: If no MAC address is defined for the | 900 @raise VMMACAddressMissingError: If no MAC address is defined for the |
| 1252 requested NIC | 901 requested NIC |
| 1253 @raise VMIPAddressMissingError: If no IP address is found for the the | 902 @raise VMIPAddressMissingError: If no IP address is found for the the |
| 1254 NIC's MAC address | 903 NIC's MAC address |
| 1255 @raise VMAddressVerificationError: If the MAC-IP address mapping cannot | 904 @raise VMAddressVerificationError: If the MAC-IP address mapping cannot |
| 1256 be verified (using arping) | 905 be verified (using arping) |
| 1257 """ | 906 """ |
| 1258 nics = self.params.objects("nics") | 907 nics = self.params.objects("nics") |
| 1259 nic_name = nics[index] | 908 nic_name = nics[index] |
| 1260 nic_params = self.params.object_params(nic_name) | 909 nic_params = self.params.object_params(nic_name) |
| 1261 if nic_params.get("nic_mode") == "tap": | 910 if nic_params.get("nic_mode") == "tap": |
| 1262 mac = self.get_mac_address(index).lower() | 911 mac = self.get_mac_address(index).lower() |
| 1263 # Get the IP address from the cache | 912 # Get the IP address from the cache |
| 1264 ip = self.address_cache.get(mac) | 913 ip = self.address_cache.get(mac) |
| 1265 if not ip: | 914 if not ip: |
| 1266 raise VMIPAddressMissingError(mac) | 915 raise virt_vm.VMIPAddressMissingError(mac) |
| 1267 # Make sure the IP address is assigned to this guest | 916 # Make sure the IP address is assigned to this guest |
| 1268 macs = [self.get_mac_address(i) for i in range(len(nics))] | 917 macs = [self.get_mac_address(i) for i in range(len(nics))] |
| 1269 if not kvm_utils.verify_ip_address_ownership(ip, macs): | 918 if not virt_utils.verify_ip_address_ownership(ip, macs): |
| 1270 raise VMAddressVerificationError(mac, ip) | 919 raise virt_vm.VMAddressVerificationError(mac, ip) |
| 1271 return ip | 920 return ip |
| 1272 else: | 921 else: |
| 1273 return "localhost" | 922 return "localhost" |
| 1274 | 923 |
| 1275 | 924 |
| 1276 def get_port(self, port, nic_index=0): | 925 def get_port(self, port, nic_index=0): |
| 1277 """ | 926 """ |
| 1278 Return the port in host space corresponding to port in guest space. | 927 Return the port in host space corresponding to port in guest space. |
| 1279 | 928 |
| 1280 @param port: Port number in host space. | 929 @param port: Port number in host space. |
| 1281 @param nic_index: Index of the NIC. | 930 @param nic_index: Index of the NIC. |
| 1282 @return: If port redirection is used, return the host port redirected | 931 @return: If port redirection is used, return the host port redirected |
| 1283 to guest port port. Otherwise return port. | 932 to guest port port. Otherwise return port. |
| 1284 @raise VMPortNotRedirectedError: If an unredirected port is requested | 933 @raise VMPortNotRedirectedError: If an unredirected port is requested |
| 1285 in user mode | 934 in user mode |
| 1286 """ | 935 """ |
| 1287 nic_name = self.params.objects("nics")[nic_index] | 936 nic_name = self.params.objects("nics")[nic_index] |
| 1288 nic_params = self.params.object_params(nic_name) | 937 nic_params = self.params.object_params(nic_name) |
| 1289 if nic_params.get("nic_mode") == "tap": | 938 if nic_params.get("nic_mode") == "tap": |
| 1290 return port | 939 return port |
| 1291 else: | 940 else: |
| 1292 try: | 941 try: |
| 1293 return self.redirs[port] | 942 return self.redirs[port] |
| 1294 except KeyError: | 943 except KeyError: |
| 1295 raise VMPortNotRedirectedError(port) | 944 raise virt_vm.VMPortNotRedirectedError(port) |
| 1296 | 945 |
| 1297 | 946 |
| 1298 def get_peer(self, netid): | 947 def get_peer(self, netid): |
| 1299 """ | 948 """ |
| 1300 Return the peer of netdev or network deivce. | 949 Return the peer of netdev or network deivce. |
| 1301 | 950 |
| 1302 @param netid: id of netdev or device | 951 @param netid: id of netdev or device |
| 1303 @return: id of the peer device otherwise None | 952 @return: id of the peer device otherwise None |
| 1304 """ | 953 """ |
| 1305 network_info = self.monitor.info("network") | 954 network_info = self.monitor.info("network") |
| (...skipping 22 matching lines...) Expand all Loading... |
| 1328 """ | 977 """ |
| 1329 Return the MAC address of a NIC. | 978 Return the MAC address of a NIC. |
| 1330 | 979 |
| 1331 @param nic_index: Index of the NIC | 980 @param nic_index: Index of the NIC |
| 1332 @raise VMMACAddressMissingError: If no MAC address is defined for the | 981 @raise VMMACAddressMissingError: If no MAC address is defined for the |
| 1333 requested NIC | 982 requested NIC |
| 1334 """ | 983 """ |
| 1335 nic_name = self.params.objects("nics")[nic_index] | 984 nic_name = self.params.objects("nics")[nic_index] |
| 1336 nic_params = self.params.object_params(nic_name) | 985 nic_params = self.params.object_params(nic_name) |
| 1337 mac = (nic_params.get("nic_mac") or | 986 mac = (nic_params.get("nic_mac") or |
| 1338 kvm_utils.get_mac_address(self.instance, nic_index)) | 987 virt_utils.get_mac_address(self.instance, nic_index)) |
| 1339 if not mac: | 988 if not mac: |
| 1340 raise VMMACAddressMissingError(nic_index) | 989 raise virt_vm.VMMACAddressMissingError(nic_index) |
| 1341 return mac | 990 return mac |
| 1342 | 991 |
| 1343 | 992 |
| 1344 def free_mac_address(self, nic_index=0): | 993 def free_mac_address(self, nic_index=0): |
| 1345 """ | 994 """ |
| 1346 Free a NIC's MAC address. | 995 Free a NIC's MAC address. |
| 1347 | 996 |
| 1348 @param nic_index: Index of the NIC | 997 @param nic_index: Index of the NIC |
| 1349 """ | 998 """ |
| 1350 kvm_utils.free_mac_address(self.instance, nic_index) | 999 virt_utils.free_mac_address(self.instance, nic_index) |
| 1351 | 1000 |
| 1352 | 1001 |
| 1353 def get_pid(self): | 1002 def get_pid(self): |
| 1354 """ | 1003 """ |
| 1355 Return the VM's PID. If the VM is dead return None. | 1004 Return the VM's PID. If the VM is dead return None. |
| 1356 | 1005 |
| 1357 @note: This works under the assumption that self.process.get_pid() | 1006 @note: This works under the assumption that self.process.get_pid() |
| 1358 returns the PID of the parent shell process. | 1007 returns the PID of the parent shell process. |
| 1359 """ | 1008 """ |
| 1360 try: | 1009 try: |
| (...skipping 24 matching lines...) Expand all Loading... |
| 1385 logging.error("Could not get shared memory info from dead VM.") | 1034 logging.error("Could not get shared memory info from dead VM.") |
| 1386 return None | 1035 return None |
| 1387 | 1036 |
| 1388 filename = "/proc/%d/statm" % self.get_pid() | 1037 filename = "/proc/%d/statm" % self.get_pid() |
| 1389 shm = int(open(filename).read().split()[2]) | 1038 shm = int(open(filename).read().split()[2]) |
| 1390 # statm stores informations in pages, translate it to MB | 1039 # statm stores informations in pages, translate it to MB |
| 1391 return shm * 4.0 / 1024 | 1040 return shm * 4.0 / 1024 |
| 1392 | 1041 |
| 1393 | 1042 |
| 1394 @error.context_aware | 1043 @error.context_aware |
| 1395 def login(self, nic_index=0, timeout=10): | |
| 1396 """ | |
| 1397 Log into the guest via SSH/Telnet/Netcat. | |
| 1398 If timeout expires while waiting for output from the guest (e.g. a | |
| 1399 password prompt or a shell prompt) -- fail. | |
| 1400 | |
| 1401 @param nic_index: The index of the NIC to connect to. | |
| 1402 @param timeout: Time (seconds) before giving up logging into the | |
| 1403 guest. | |
| 1404 @return: A ShellSession object. | |
| 1405 """ | |
| 1406 error.context("logging into '%s'" % self.name) | |
| 1407 username = self.params.get("username", "") | |
| 1408 password = self.params.get("password", "") | |
| 1409 prompt = self.params.get("shell_prompt", "[\#\$]") | |
| 1410 linesep = eval("'%s'" % self.params.get("shell_linesep", r"\n")) | |
| 1411 client = self.params.get("shell_client") | |
| 1412 address = self.get_address(nic_index) | |
| 1413 port = self.get_port(int(self.params.get("shell_port"))) | |
| 1414 log_filename = ("session-%s-%s.log" % | |
| 1415 (self.name, kvm_utils.generate_random_string(4))) | |
| 1416 session = kvm_utils.remote_login(client, address, port, username, | |
| 1417 password, prompt, linesep, | |
| 1418 log_filename, timeout) | |
| 1419 session.set_status_test_command(self.params.get("status_test_command", | |
| 1420 "")) | |
| 1421 return session | |
| 1422 | |
| 1423 | |
| 1424 def remote_login(self, nic_index=0, timeout=10): | |
| 1425 """ | |
| 1426 Alias for login() for backward compatibility. | |
| 1427 """ | |
| 1428 return self.login(nic_index, timeout) | |
| 1429 | |
| 1430 | |
| 1431 def wait_for_login(self, nic_index=0, timeout=240, internal_timeout=10): | |
| 1432 """ | |
| 1433 Make multiple attempts to log into the guest via SSH/Telnet/Netcat. | |
| 1434 | |
| 1435 @param nic_index: The index of the NIC to connect to. | |
| 1436 @param timeout: Time (seconds) to keep trying to log in. | |
| 1437 @param internal_timeout: Timeout to pass to login(). | |
| 1438 @return: A ShellSession object. | |
| 1439 """ | |
| 1440 logging.debug("Attempting to log into '%s' (timeout %ds)", self.name, | |
| 1441 timeout) | |
| 1442 end_time = time.time() + timeout | |
| 1443 while time.time() < end_time: | |
| 1444 try: | |
| 1445 return self.login(nic_index, internal_timeout) | |
| 1446 except (kvm_utils.LoginError, VMError), e: | |
| 1447 logging.debug(e) | |
| 1448 time.sleep(2) | |
| 1449 # Timeout expired; try one more time but don't catch exceptions | |
| 1450 return self.login(nic_index, internal_timeout) | |
| 1451 | |
| 1452 | |
| 1453 @error.context_aware | |
| 1454 def copy_files_to(self, host_path, guest_path, nic_index=0, verbose=False, | |
| 1455 timeout=600): | |
| 1456 """ | |
| 1457 Transfer files to the remote host(guest). | |
| 1458 | |
| 1459 @param host_path: Host path | |
| 1460 @param guest_path: Guest path | |
| 1461 @param nic_index: The index of the NIC to connect to. | |
| 1462 @param verbose: If True, log some stats using logging.debug (RSS only) | |
| 1463 @param timeout: Time (seconds) before giving up on doing the remote | |
| 1464 copy. | |
| 1465 """ | |
| 1466 error.context("sending file(s) to '%s'" % self.name) | |
| 1467 username = self.params.get("username", "") | |
| 1468 password = self.params.get("password", "") | |
| 1469 client = self.params.get("file_transfer_client") | |
| 1470 address = self.get_address(nic_index) | |
| 1471 port = self.get_port(int(self.params.get("file_transfer_port"))) | |
| 1472 log_filename = ("transfer-%s-to-%s-%s.log" % | |
| 1473 (self.name, address, | |
| 1474 kvm_utils.generate_random_string(4))) | |
| 1475 kvm_utils.copy_files_to(address, client, username, password, port, | |
| 1476 host_path, guest_path, log_filename, verbose, | |
| 1477 timeout) | |
| 1478 | |
| 1479 | |
| 1480 @error.context_aware | |
| 1481 def copy_files_from(self, guest_path, host_path, nic_index=0, | |
| 1482 verbose=False, timeout=600): | |
| 1483 """ | |
| 1484 Transfer files from the guest. | |
| 1485 | |
| 1486 @param host_path: Guest path | |
| 1487 @param guest_path: Host path | |
| 1488 @param nic_index: The index of the NIC to connect to. | |
| 1489 @param verbose: If True, log some stats using logging.debug (RSS only) | |
| 1490 @param timeout: Time (seconds) before giving up on doing the remote | |
| 1491 copy. | |
| 1492 """ | |
| 1493 error.context("receiving file(s) from '%s'" % self.name) | |
| 1494 username = self.params.get("username", "") | |
| 1495 password = self.params.get("password", "") | |
| 1496 client = self.params.get("file_transfer_client") | |
| 1497 address = self.get_address(nic_index) | |
| 1498 port = self.get_port(int(self.params.get("file_transfer_port"))) | |
| 1499 log_filename = ("transfer-%s-from-%s-%s.log" % | |
| 1500 (self.name, address, | |
| 1501 kvm_utils.generate_random_string(4))) | |
| 1502 kvm_utils.copy_files_from(address, client, username, password, port, | |
| 1503 guest_path, host_path, log_filename, | |
| 1504 verbose, timeout) | |
| 1505 | |
| 1506 | |
| 1507 @error.context_aware | |
| 1508 def serial_login(self, timeout=10): | |
| 1509 """ | |
| 1510 Log into the guest via the serial console. | |
| 1511 If timeout expires while waiting for output from the guest (e.g. a | |
| 1512 password prompt or a shell prompt) -- fail. | |
| 1513 | |
| 1514 @param timeout: Time (seconds) before giving up logging into the guest. | |
| 1515 @return: ShellSession object on success and None on failure. | |
| 1516 """ | |
| 1517 error.context("logging into '%s' via serial console" % self.name) | |
| 1518 username = self.params.get("username", "") | |
| 1519 password = self.params.get("password", "") | |
| 1520 prompt = self.params.get("shell_prompt", "[\#\$]") | |
| 1521 linesep = eval("'%s'" % self.params.get("shell_linesep", r"\n")) | |
| 1522 status_test_command = self.params.get("status_test_command", "") | |
| 1523 | |
| 1524 self.serial_console.set_linesep(linesep) | |
| 1525 self.serial_console.set_status_test_command(status_test_command) | |
| 1526 | |
| 1527 # Try to get a login prompt | |
| 1528 self.serial_console.sendline() | |
| 1529 | |
| 1530 kvm_utils._remote_login(self.serial_console, username, password, | |
| 1531 prompt, timeout) | |
| 1532 return self.serial_console | |
| 1533 | |
| 1534 | |
| 1535 def wait_for_serial_login(self, timeout=240, internal_timeout=10): | |
| 1536 """ | |
| 1537 Make multiple attempts to log into the guest via serial console. | |
| 1538 | |
| 1539 @param timeout: Time (seconds) to keep trying to log in. | |
| 1540 @param internal_timeout: Timeout to pass to serial_login(). | |
| 1541 @return: A ShellSession object. | |
| 1542 """ | |
| 1543 logging.debug("Attempting to log into '%s' via serial console " | |
| 1544 "(timeout %ds)", self.name, timeout) | |
| 1545 end_time = time.time() + timeout | |
| 1546 while time.time() < end_time: | |
| 1547 try: | |
| 1548 return self.serial_login(internal_timeout) | |
| 1549 except kvm_utils.LoginError, e: | |
| 1550 logging.debug(e) | |
| 1551 time.sleep(2) | |
| 1552 # Timeout expired; try one more time but don't catch exceptions | |
| 1553 return self.serial_login(internal_timeout) | |
| 1554 | |
| 1555 | |
| 1556 @error.context_aware | |
| 1557 def migrate(self, timeout=3600, protocol="tcp", cancel_delay=None, | 1044 def migrate(self, timeout=3600, protocol="tcp", cancel_delay=None, |
| 1558 offline=False, stable_check=False, clean=True, | 1045 offline=False, stable_check=False, clean=True, |
| 1559 save_path="/tmp", dest_host="localhost", remote_port=None): | 1046 save_path="/tmp", dest_host="localhost", remote_port=None): |
| 1560 """ | 1047 """ |
| 1561 Migrate the VM. | 1048 Migrate the VM. |
| 1562 | 1049 |
| 1563 If the migration is local, the VM object's state is switched with that | 1050 If the migration is local, the VM object's state is switched with that |
| 1564 of the destination VM. Otherwise, the state is switched with that of | 1051 of the destination VM. Otherwise, the state is switched with that of |
| 1565 a dead VM (returned by self.clone()). | 1052 a dead VM (returned by self.clone()). |
| 1566 | 1053 |
| 1567 @param timeout: Time to wait for migration to complete. | 1054 @param timeout: Time to wait for migration to complete. |
| 1568 @param protocol: Migration protocol ('tcp', 'unix' or 'exec'). | 1055 @param protocol: Migration protocol (as defined in MIGRATION_PROTOS) |
| 1569 @param cancel_delay: If provided, specifies a time duration after which | 1056 @param cancel_delay: If provided, specifies a time duration after which |
| 1570 migration will be canceled. Used for testing migrate_cancel. | 1057 migration will be canceled. Used for testing migrate_cancel. |
| 1571 @param offline: If True, pause the source VM before migration. | 1058 @param offline: If True, pause the source VM before migration. |
| 1572 @param stable_check: If True, compare the VM's state after migration to | 1059 @param stable_check: If True, compare the VM's state after migration to |
| 1573 its state before migration and raise an exception if they | 1060 its state before migration and raise an exception if they |
| 1574 differ. | 1061 differ. |
| 1575 @param clean: If True, delete the saved state files (relevant only if | 1062 @param clean: If True, delete the saved state files (relevant only if |
| 1576 stable_check is also True). | 1063 stable_check is also True). |
| 1577 @save_path: The path for state files. | 1064 @save_path: The path for state files. |
| 1578 @param dest_host: Destination host (defaults to 'localhost'). | 1065 @param dest_host: Destination host (defaults to 'localhost'). |
| 1579 @param remote_port: Port to use for remote migration. | 1066 @param remote_port: Port to use for remote migration. |
| 1580 """ | 1067 """ |
| 1068 if protocol not in self.MIGRATION_PROTOS: |
| 1069 raise virt_vm.VMMigrateProtoUnsupportedError |
| 1070 |
| 1581 error.base_context("migrating '%s'" % self.name) | 1071 error.base_context("migrating '%s'" % self.name) |
| 1582 | 1072 |
| 1583 def mig_finished(): | 1073 def mig_finished(): |
| 1584 o = self.monitor.info("migrate") | 1074 o = self.monitor.info("migrate") |
| 1585 if isinstance(o, str): | 1075 if isinstance(o, str): |
| 1586 return "status: active" not in o | 1076 return "status: active" not in o |
| 1587 else: | 1077 else: |
| 1588 return o.get("status") != "active" | 1078 return o.get("status") != "active" |
| 1589 | 1079 |
| 1590 def mig_succeeded(): | 1080 def mig_succeeded(): |
| (...skipping 13 matching lines...) Expand all Loading... |
| 1604 def mig_cancelled(): | 1094 def mig_cancelled(): |
| 1605 o = self.monitor.info("migrate") | 1095 o = self.monitor.info("migrate") |
| 1606 if isinstance(o, str): | 1096 if isinstance(o, str): |
| 1607 return ("Migration status: cancelled" in o or | 1097 return ("Migration status: cancelled" in o or |
| 1608 "Migration status: canceled" in o) | 1098 "Migration status: canceled" in o) |
| 1609 else: | 1099 else: |
| 1610 return (o.get("status") == "cancelled" or | 1100 return (o.get("status") == "cancelled" or |
| 1611 o.get("status") == "canceled") | 1101 o.get("status") == "canceled") |
| 1612 | 1102 |
| 1613 def wait_for_migration(): | 1103 def wait_for_migration(): |
| 1614 if not kvm_utils.wait_for(mig_finished, timeout, 2, 2, | 1104 if not virt_utils.wait_for(mig_finished, timeout, 2, 2, |
| 1615 "Waiting for migration to complete"): | 1105 "Waiting for migration to complete"): |
| 1616 raise VMMigrateTimeoutError("Timeout expired while waiting " | 1106 raise virt_vm.VMMigrateTimeoutError("Timeout expired while waiti
ng " |
| 1617 "for migration to finish") | 1107 "for migration to finish") |
| 1618 | 1108 |
| 1619 local = dest_host == "localhost" | 1109 local = dest_host == "localhost" |
| 1620 | 1110 |
| 1621 clone = self.clone() | 1111 clone = self.clone() |
| 1622 if local: | 1112 if local: |
| 1623 error.context("creating destination VM") | 1113 error.context("creating destination VM") |
| 1624 if stable_check: | 1114 if stable_check: |
| 1625 # Pause the dest vm after creation | 1115 # Pause the dest vm after creation |
| 1626 extra_params = clone.params.get("extra_params", "") + " -S" | 1116 extra_params = clone.params.get("extra_params", "") + " -S" |
| (...skipping 14 matching lines...) Expand all Loading... |
| 1641 | 1131 |
| 1642 if offline: | 1132 if offline: |
| 1643 self.monitor.cmd("stop") | 1133 self.monitor.cmd("stop") |
| 1644 | 1134 |
| 1645 logging.info("Migrating to %s", uri) | 1135 logging.info("Migrating to %s", uri) |
| 1646 self.monitor.migrate(uri) | 1136 self.monitor.migrate(uri) |
| 1647 | 1137 |
| 1648 if cancel_delay: | 1138 if cancel_delay: |
| 1649 time.sleep(cancel_delay) | 1139 time.sleep(cancel_delay) |
| 1650 self.monitor.cmd("migrate_cancel") | 1140 self.monitor.cmd("migrate_cancel") |
| 1651 if not kvm_utils.wait_for(mig_cancelled, 60, 2, 2, | 1141 if not virt_utils.wait_for(mig_cancelled, 60, 2, 2, |
| 1652 "Waiting for migration " | 1142 "Waiting for migration " |
| 1653 "cancellation"): | 1143 "cancellation"): |
| 1654 raise VMMigrateCancelError("Cannot cancel migration") | 1144 raise virt_vm.VMMigrateCancelError("Cannot cancel migration"
) |
| 1655 return | 1145 return |
| 1656 | 1146 |
| 1657 wait_for_migration() | 1147 wait_for_migration() |
| 1658 | 1148 |
| 1659 # Report migration status | 1149 # Report migration status |
| 1660 if mig_succeeded(): | 1150 if mig_succeeded(): |
| 1661 logging.info("Migration completed successfully") | 1151 logging.info("Migration completed successfully") |
| 1662 elif mig_failed(): | 1152 elif mig_failed(): |
| 1663 raise VMMigrateFailedError("Migration failed") | 1153 raise virt_vm.VMMigrateFailedError("Migration failed") |
| 1664 else: | 1154 else: |
| 1665 raise VMMigrateFailedError("Migration ended with unknown " | 1155 raise virt_vm.VMMigrateFailedError("Migration ended with " |
| 1666 "status") | 1156 "unknown status") |
| 1667 | 1157 |
| 1668 # Switch self <-> clone | 1158 # Switch self <-> clone |
| 1669 temp = self.clone(copy_state=True) | 1159 temp = self.clone(copy_state=True) |
| 1670 self.__dict__ = clone.__dict__ | 1160 self.__dict__ = clone.__dict__ |
| 1671 clone = temp | 1161 clone = temp |
| 1672 | 1162 |
| 1673 # From now on, clone is the source VM that will soon be destroyed | 1163 # From now on, clone is the source VM that will soon be destroyed |
| 1674 # and self is the destination VM that will remain alive. If this | 1164 # and self is the destination VM that will remain alive. If this |
| 1675 # is remote migration, self is a dead VM object. | 1165 # is remote migration, self is a dead VM object. |
| 1676 | 1166 |
| 1677 error.context("after migration") | 1167 error.context("after migration") |
| 1678 if local: | 1168 if local: |
| 1679 time.sleep(1) | 1169 time.sleep(1) |
| 1680 self.verify_alive() | 1170 self.verify_alive() |
| 1681 self.verify_kernel_crash() | |
| 1682 | 1171 |
| 1683 if local and stable_check: | 1172 if local and stable_check: |
| 1684 try: | 1173 try: |
| 1685 save1 = os.path.join(save_path, "src-" + clone.instance) | 1174 save1 = os.path.join(save_path, "src-" + clone.instance) |
| 1686 save2 = os.path.join(save_path, "dst-" + self.instance) | 1175 save2 = os.path.join(save_path, "dst-" + self.instance) |
| 1687 clone.save_to_file(save1) | 1176 clone.save_to_file(save1) |
| 1688 self.save_to_file(save2) | 1177 self.save_to_file(save2) |
| 1689 # Fail if we see deltas | 1178 # Fail if we see deltas |
| 1690 md5_save1 = utils.hash_file(save1) | 1179 md5_save1 = utils.hash_file(save1) |
| 1691 md5_save2 = utils.hash_file(save2) | 1180 md5_save2 = utils.hash_file(save2) |
| 1692 if md5_save1 != md5_save2: | 1181 if md5_save1 != md5_save2: |
| 1693 raise VMMigrateStateMismatchError(md5_save1, md5_save2) | 1182 raise virt_vm.VMMigrateStateMismatchError(md5_save1, |
| 1183 md5_save2) |
| 1694 finally: | 1184 finally: |
| 1695 if clean: | 1185 if clean: |
| 1696 if os.path.isfile(save1): | 1186 if os.path.isfile(save1): |
| 1697 os.remove(save1) | 1187 os.remove(save1) |
| 1698 if os.path.isfile(save2): | 1188 if os.path.isfile(save2): |
| 1699 os.remove(save2) | 1189 os.remove(save2) |
| 1700 | 1190 |
| 1701 finally: | 1191 finally: |
| 1702 # If we're doing remote migration and it's completed successfully, | 1192 # If we're doing remote migration and it's completed successfully, |
| 1703 # self points to a dead VM object | 1193 # self points to a dead VM object |
| (...skipping 29 matching lines...) Expand all Loading... |
| 1733 for m in qmp_monitors: | 1223 for m in qmp_monitors: |
| 1734 m.clear_events() | 1224 m.clear_events() |
| 1735 # Send a system_reset monitor command | 1225 # Send a system_reset monitor command |
| 1736 self.monitor.cmd("system_reset") | 1226 self.monitor.cmd("system_reset") |
| 1737 # Look for RESET QMP events | 1227 # Look for RESET QMP events |
| 1738 time.sleep(1) | 1228 time.sleep(1) |
| 1739 for m in qmp_monitors: | 1229 for m in qmp_monitors: |
| 1740 if m.get_event("RESET"): | 1230 if m.get_event("RESET"): |
| 1741 logging.info("RESET QMP event received") | 1231 logging.info("RESET QMP event received") |
| 1742 else: | 1232 else: |
| 1743 raise VMRebootError("RESET QMP event not received after " | 1233 raise virt_vm.VMRebootError("RESET QMP event not received " |
| 1744 "system_reset (monitor '%s')" % m.name) | 1234 "after system_reset " |
| 1235 "(monitor '%s')" % m.name) |
| 1745 else: | 1236 else: |
| 1746 raise VMRebootError("Unknown reboot method: %s" % method) | 1237 raise virt_vm.VMRebootError("Unknown reboot method: %s" % method) |
| 1747 | 1238 |
| 1748 error.context("waiting for guest to go down", logging.info) | 1239 error.context("waiting for guest to go down", logging.info) |
| 1749 if not kvm_utils.wait_for(lambda: | 1240 if not virt_utils.wait_for(lambda: |
| 1750 not session.is_responsive(timeout=30), | 1241 not session.is_responsive(timeout=30), |
| 1751 120, 0, 1): | 1242 120, 0, 1): |
| 1752 raise VMRebootError("Guest refuses to go down") | 1243 raise virt_vm.VMRebootError("Guest refuses to go down") |
| 1753 session.close() | 1244 session.close() |
| 1754 | 1245 |
| 1755 error.context("logging in after reboot", logging.info) | 1246 error.context("logging in after reboot", logging.info) |
| 1756 return self.wait_for_login(nic_index, timeout=timeout) | 1247 return self.wait_for_login(nic_index, timeout=timeout) |
| 1757 | 1248 |
| 1758 | 1249 |
| 1759 def send_key(self, keystr): | 1250 def send_key(self, keystr): |
| 1760 """ | 1251 """ |
| 1761 Send a key event to the VM. | 1252 Send a key event to the VM. |
| 1762 | 1253 |
| 1763 @param: keystr: A key event string (e.g. "ctrl-alt-delete") | 1254 @param: keystr: A key event string (e.g. "ctrl-alt-delete") |
| 1764 """ | 1255 """ |
| 1765 # For compatibility with versions of QEMU that do not recognize all | 1256 # For compatibility with versions of QEMU that do not recognize all |
| 1766 # key names: replace keyname with the hex value from the dict, which | 1257 # key names: replace keyname with the hex value from the dict, which |
| 1767 # QEMU will definitely accept | 1258 # QEMU will definitely accept |
| 1768 dict = {"comma": "0x33", | 1259 dict = {"comma": "0x33", |
| 1769 "dot": "0x34", | 1260 "dot": "0x34", |
| 1770 "slash": "0x35"} | 1261 "slash": "0x35"} |
| 1771 for key, value in dict.items(): | 1262 for key, value in dict.items(): |
| 1772 keystr = keystr.replace(key, value) | 1263 keystr = keystr.replace(key, value) |
| 1773 self.monitor.sendkey(keystr) | 1264 self.monitor.sendkey(keystr) |
| 1774 time.sleep(0.2) | 1265 time.sleep(0.2) |
| 1775 | 1266 |
| 1776 | 1267 |
| 1777 def send_string(self, str): | 1268 # should this really be expected from VMs of all hypervisor types? |
| 1778 """ | 1269 def screendump(self, filename): |
| 1779 Send a string to the VM. | |
| 1780 | |
| 1781 @param str: String, that must consist of alphanumeric characters only. | |
| 1782 Capital letters are allowed. | |
| 1783 """ | |
| 1784 for char in str: | |
| 1785 if char.isupper(): | |
| 1786 self.send_key("shift-%s" % char.lower()) | |
| 1787 else: | |
| 1788 self.send_key(char) | |
| 1789 | |
| 1790 | |
| 1791 def get_uuid(self): | |
| 1792 """ | |
| 1793 Catch UUID of the VM. | |
| 1794 | |
| 1795 @return: None,if not specified in config file | |
| 1796 """ | |
| 1797 if self.params.get("uuid") == "random": | |
| 1798 return self.uuid | |
| 1799 else: | |
| 1800 return self.params.get("uuid", None) | |
| 1801 | |
| 1802 | |
| 1803 def get_cpu_count(self): | |
| 1804 """ | |
| 1805 Get the cpu count of the VM. | |
| 1806 """ | |
| 1807 session = self.login() | |
| 1808 try: | 1270 try: |
| 1809 return int(session.cmd(self.params.get("cpu_chk_cmd"))) | 1271 if self.monitor: |
| 1810 finally: | 1272 self.monitor.screendump(filename=filename) |
| 1811 session.close() | 1273 except kvm_monitor.MonitorError, e: |
| 1812 | 1274 logging.warn(e) |
| 1813 | |
| 1814 def get_memory_size(self, cmd=None): | |
| 1815 """ | |
| 1816 Get bootup memory size of the VM. | |
| 1817 | |
| 1818 @param check_cmd: Command used to check memory. If not provided, | |
| 1819 self.params.get("mem_chk_cmd") will be used. | |
| 1820 """ | |
| 1821 session = self.login() | |
| 1822 try: | |
| 1823 if not cmd: | |
| 1824 cmd = self.params.get("mem_chk_cmd") | |
| 1825 mem_str = session.cmd(cmd) | |
| 1826 mem = re.findall("([0-9]+)", mem_str) | |
| 1827 mem_size = 0 | |
| 1828 for m in mem: | |
| 1829 mem_size += int(m) | |
| 1830 if "GB" in mem_str: | |
| 1831 mem_size *= 1024 | |
| 1832 elif "MB" in mem_str: | |
| 1833 pass | |
| 1834 else: | |
| 1835 mem_size /= 1024 | |
| 1836 return int(mem_size) | |
| 1837 finally: | |
| 1838 session.close() | |
| 1839 | |
| 1840 | |
| 1841 def get_current_memory_size(self): | |
| 1842 """ | |
| 1843 Get current memory size of the VM, rather than bootup memory. | |
| 1844 """ | |
| 1845 cmd = self.params.get("mem_chk_cur_cmd") | |
| 1846 return self.get_memory_size(cmd) | |
| 1847 | 1275 |
| 1848 | 1276 |
| 1849 def save_to_file(self, path): | 1277 def save_to_file(self, path): |
| 1850 """ | 1278 """ |
| 1851 Save the state of virtual machine to a file through migrate to | 1279 Save the state of virtual machine to a file through migrate to |
| 1852 exec | 1280 exec |
| 1853 """ | 1281 """ |
| 1854 # Make sure we only get one iteration | 1282 # Make sure we only get one iteration |
| 1855 self.monitor.cmd("migrate_set_speed 1000g") | 1283 self.monitor.cmd("migrate_set_speed 1000g") |
| 1856 self.monitor.cmd("migrate_set_downtime 100000000") | 1284 self.monitor.cmd("migrate_set_downtime 100000000") |
| 1857 self.monitor.migrate('"exec:cat>%s"' % path) | 1285 self.monitor.migrate('"exec:cat>%s"' % path) |
| 1858 # Restore the speed and downtime of migration | 1286 # Restore the speed and downtime of migration |
| 1859 self.monitor.cmd("migrate_set_speed %d" % (32<<20)) | 1287 self.monitor.cmd("migrate_set_speed %d" % (32<<20)) |
| 1860 self.monitor.cmd("migrate_set_downtime 0.03") | 1288 self.monitor.cmd("migrate_set_downtime 0.03") |
| 1289 |
| 1290 |
| 1291 def needs_restart(self, name, params, basedir): |
| 1292 """ |
| 1293 Verifies whether the current qemu commandline matches the requested |
| 1294 one, based on the test parameters. |
| 1295 """ |
| 1296 return (self.__make_qemu_command() != |
| 1297 self.__make_qemu_command(name, params, basedir)) |
| OLD | NEW |