| 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 |