kvm_vm.py revision ce1b0620fddfb6efa33cc9441d9c91ce06b30de3
1""" 2Utility classes and functions to handle Virtual Machine creation using qemu. 3 4@copyright: 2008-2009 Red Hat Inc. 5""" 6 7import time, os, logging, fcntl, re, commands, glob 8from autotest_lib.client.common_lib import error 9from autotest_lib.client.bin import utils 10import virt_utils, virt_vm, kvm_monitor, aexpect 11 12 13class VM(virt_vm.BaseVM): 14 """ 15 This class handles all basic VM operations. 16 """ 17 18 MIGRATION_PROTOS = ['tcp', 'unix', 'exec'] 19 20 def __init__(self, name, params, root_dir, address_cache, state=None): 21 """ 22 Initialize the object and set a few attributes. 23 24 @param name: The name of the object 25 @param params: A dict containing VM params 26 (see method make_qemu_command for a full description) 27 @param root_dir: Base directory for relative filenames 28 @param address_cache: A dict that maps MAC addresses to IP addresses 29 @param state: If provided, use this as self.__dict__ 30 """ 31 virt_vm.BaseVM.__init__(self, name, params) 32 33 if state: 34 self.__dict__ = state 35 else: 36 self.process = None 37 self.serial_console = None 38 self.redirs = {} 39 self.vnc_port = 5900 40 self.monitors = [] 41 self.pci_assignable = None 42 self.netdev_id = [] 43 self.device_id = [] 44 self.uuid = None 45 46 47 self.spice_port = 8000 48 self.name = name 49 self.params = params 50 self.root_dir = root_dir 51 self.address_cache = address_cache 52 53 54 def verify_alive(self): 55 """ 56 Make sure the VM is alive and that the main monitor is responsive. 57 58 @raise VMDeadError: If the VM is dead 59 @raise: Various monitor exceptions if the monitor is unresponsive 60 """ 61 try: 62 virt_vm.BaseVM.verify_alive(self) 63 if self.monitors: 64 self.monitor.verify_responsive() 65 except virt_vm.VMDeadError: 66 raise virt_vm.VMDeadError(self.process.get_status(), 67 self.process.get_output()) 68 69 70 def is_alive(self): 71 """ 72 Return True if the VM is alive and its monitor is responsive. 73 """ 74 return not self.is_dead() and (not self.monitors or 75 self.monitor.is_responsive()) 76 77 78 def is_dead(self): 79 """ 80 Return True if the qemu process is dead. 81 """ 82 return not self.process or not self.process.is_alive() 83 84 85 86 87 def clone(self, name=None, params=None, root_dir=None, address_cache=None, 88 copy_state=False): 89 """ 90 Return a clone of the VM object with optionally modified parameters. 91 The clone is initially not alive and needs to be started using create(). 92 Any parameters not passed to this function are copied from the source 93 VM. 94 95 @param name: Optional new VM name 96 @param params: Optional new VM creation parameters 97 @param root_dir: Optional new base directory for relative filenames 98 @param address_cache: A dict that maps MAC addresses to IP addresses 99 @param copy_state: If True, copy the original VM's state to the clone. 100 Mainly useful for make_qemu_command(). 101 """ 102 if name is None: 103 name = self.name 104 if params is None: 105 params = self.params.copy() 106 if root_dir is None: 107 root_dir = self.root_dir 108 if address_cache is None: 109 address_cache = self.address_cache 110 if copy_state: 111 state = self.__dict__.copy() 112 else: 113 state = None 114 return VM(name, params, root_dir, address_cache, state) 115 116 117 def __make_qemu_command(self, name=None, params=None, root_dir=None): 118 """ 119 Generate a qemu command line. All parameters are optional. If a 120 parameter is not supplied, the corresponding value stored in the 121 class attributes is used. 122 123 @param name: The name of the object 124 @param params: A dict containing VM params 125 @param root_dir: Base directory for relative filenames 126 127 @note: The params dict should contain: 128 mem -- memory size in MBs 129 cdrom -- ISO filename to use with the qemu -cdrom parameter 130 extra_params -- a string to append to the qemu command 131 shell_port -- port of the remote shell daemon on the guest 132 (SSH, Telnet or the home-made Remote Shell Server) 133 shell_client -- client program to use for connecting to the 134 remote shell daemon on the guest (ssh, telnet or nc) 135 x11_display -- if specified, the DISPLAY environment variable 136 will be be set to this value for the qemu process (useful for 137 SDL rendering) 138 images -- a list of image object names, separated by spaces 139 nics -- a list of NIC object names, separated by spaces 140 141 For each image in images: 142 drive_format -- string to pass as 'if' parameter for this 143 image (e.g. ide, scsi) 144 image_snapshot -- if yes, pass 'snapshot=on' to qemu for 145 this image 146 image_boot -- if yes, pass 'boot=on' to qemu for this image 147 In addition, all parameters required by get_image_filename. 148 149 For each NIC in nics: 150 nic_model -- string to pass as 'model' parameter for this 151 NIC (e.g. e1000) 152 """ 153 # Helper function for command line option wrappers 154 def has_option(help, option): 155 return bool(re.search(r"^-%s(\s|$)" % option, help, re.MULTILINE)) 156 157 # Wrappers for all supported qemu command line parameters. 158 # This is meant to allow support for multiple qemu versions. 159 # Each of these functions receives the output of 'qemu -help' as a 160 # parameter, and should add the requested command line option 161 # accordingly. 162 163 def add_name(help, name): 164 return " -name '%s'" % name 165 166 def add_human_monitor(help, filename): 167 return " -monitor unix:'%s',server,nowait" % filename 168 169 def add_qmp_monitor(help, filename): 170 return " -qmp unix:'%s',server,nowait" % filename 171 172 def add_serial(help, filename): 173 return " -serial unix:'%s',server,nowait" % filename 174 175 def add_mem(help, mem): 176 return " -m %s" % mem 177 178 def add_smp(help, smp): 179 return " -smp %s" % smp 180 181 def add_cdrom(help, filename, index=None): 182 if has_option(help, "drive"): 183 cmd = " -drive file='%s',media=cdrom" % filename 184 if index is not None: cmd += ",index=%s" % index 185 return cmd 186 else: 187 return " -cdrom '%s'" % filename 188 189 def add_drive(help, filename, index=None, format=None, cache=None, 190 werror=None, serial=None, snapshot=False, boot=False): 191 cmd = " -drive file='%s'" % filename 192 if index is not None: 193 cmd += ",index=%s" % index 194 if format: 195 cmd += ",if=%s" % format 196 if cache: 197 cmd += ",cache=%s" % cache 198 if werror: 199 cmd += ",werror=%s" % werror 200 if serial: 201 cmd += ",serial='%s'" % serial 202 if snapshot: 203 cmd += ",snapshot=on" 204 if boot: 205 cmd += ",boot=on" 206 return cmd 207 208 def add_nic(help, vlan, model=None, mac=None, device_id=None, netdev_id=None, 209 nic_extra_params=None): 210 if has_option(help, "netdev"): 211 netdev_vlan_str = ",netdev=%s" % netdev_id 212 else: 213 netdev_vlan_str = ",vlan=%d" % vlan 214 if has_option(help, "device"): 215 if not model: 216 model = "rtl8139" 217 elif model == "virtio": 218 model = "virtio-net-pci" 219 cmd = " -device %s" % model + netdev_vlan_str 220 if mac: 221 cmd += ",mac='%s'" % mac 222 if nic_extra_params: 223 cmd += ",%s" % nic_extra_params 224 else: 225 cmd = " -net nic" + netdev_vlan_str 226 if model: 227 cmd += ",model=%s" % model 228 if mac: 229 cmd += ",macaddr='%s'" % mac 230 if device_id: 231 cmd += ",id='%s'" % device_id 232 return cmd 233 234 def add_net(help, vlan, mode, ifname=None, script=None, 235 downscript=None, tftp=None, bootfile=None, hostfwd=[], 236 netdev_id=None, netdev_extra_params=None): 237 if has_option(help, "netdev"): 238 cmd = " -netdev %s,id=%s" % (mode, netdev_id) 239 if netdev_extra_params: 240 cmd += ",%s" % netdev_extra_params 241 else: 242 cmd = " -net %s,vlan=%d" % (mode, vlan) 243 if mode == "tap": 244 if ifname: cmd += ",ifname='%s'" % ifname 245 if script: cmd += ",script='%s'" % script 246 cmd += ",downscript='%s'" % (downscript or "no") 247 elif mode == "user": 248 if tftp and "[,tftp=" in help: 249 cmd += ",tftp='%s'" % tftp 250 if bootfile and "[,bootfile=" in help: 251 cmd += ",bootfile='%s'" % bootfile 252 if "[,hostfwd=" in help: 253 for host_port, guest_port in hostfwd: 254 cmd += ",hostfwd=tcp::%s-:%s" % (host_port, guest_port) 255 return cmd 256 257 def add_floppy(help, filename): 258 return " -fda '%s'" % filename 259 260 def add_tftp(help, filename): 261 # If the new syntax is supported, don't add -tftp 262 if "[,tftp=" in help: 263 return "" 264 else: 265 return " -tftp '%s'" % filename 266 267 def add_bootp(help, filename): 268 # If the new syntax is supported, don't add -bootp 269 if "[,bootfile=" in help: 270 return "" 271 else: 272 return " -bootp '%s'" % filename 273 274 def add_tcp_redir(help, host_port, guest_port): 275 # If the new syntax is supported, don't add -redir 276 if "[,hostfwd=" in help: 277 return "" 278 else: 279 return " -redir tcp:%s::%s" % (host_port, guest_port) 280 281 def add_vnc(help, vnc_port): 282 return " -vnc :%d" % (vnc_port - 5900) 283 284 def add_sdl(help): 285 if has_option(help, "sdl"): 286 return " -sdl" 287 else: 288 return "" 289 290 def add_nographic(help): 291 return " -nographic" 292 293 def add_uuid(help, uuid): 294 return " -uuid '%s'" % uuid 295 296 def add_pcidevice(help, host): 297 return " -pcidevice host='%s'" % host 298 299 def add_spice(help, port, param): 300 if has_option(help,"spice"): 301 return " -spice port=%s,%s" % (port, param) 302 else: 303 return "" 304 305 def add_qxl_vga(help, qxl, vga, qxl_dev_nr=None): 306 str = "" 307 if has_option(help, "qxl"): 308 if qxl and qxl_dev_nr is not None: 309 str += " -qxl %s" % qxl_dev_nr 310 if has_option(help, "vga") and vga and vga != "qxl": 311 str += " -vga %s" % vga 312 elif has_option(help, "vga"): 313 if qxl: 314 str += " -vga qxl" 315 elif vga: 316 str += " -vga %s" % vga 317 return str 318 319 def add_kernel(help, filename): 320 return " -kernel '%s'" % filename 321 322 def add_initrd(help, filename): 323 return " -initrd '%s'" % filename 324 325 def add_kernel_cmdline(help, cmdline): 326 return " -append %s" % cmdline 327 328 def add_testdev(help, filename): 329 return (" -chardev file,id=testlog,path=%s" 330 " -device testdev,chardev=testlog" % filename) 331 332 def add_no_hpet(help): 333 if has_option(help, "no-hpet"): 334 return " -no-hpet" 335 else: 336 return "" 337 338 # End of command line option wrappers 339 340 if name is None: 341 name = self.name 342 if params is None: 343 params = self.params 344 if root_dir is None: 345 root_dir = self.root_dir 346 347 # Clone this VM using the new params 348 vm = self.clone(name, params, root_dir, copy_state=True) 349 350 qemu_binary = virt_utils.get_path(root_dir, params.get("qemu_binary", 351 "qemu")) 352 # Get the output of 'qemu -help' (log a message in case this call never 353 # returns or causes some other kind of trouble) 354 logging.debug("Getting output of 'qemu -help'") 355 help = commands.getoutput("%s -help" % qemu_binary) 356 357 # Start constructing the qemu command 358 qemu_cmd = "" 359 # Set the X11 display parameter if requested 360 if params.get("x11_display"): 361 qemu_cmd += "DISPLAY=%s " % params.get("x11_display") 362 # Add the qemu binary 363 qemu_cmd += qemu_binary 364 # Add the VM's name 365 qemu_cmd += add_name(help, name) 366 # Add monitors 367 for monitor_name in params.objects("monitors"): 368 monitor_params = params.object_params(monitor_name) 369 monitor_filename = vm.get_monitor_filename(monitor_name) 370 if monitor_params.get("monitor_type") == "qmp": 371 qemu_cmd += add_qmp_monitor(help, monitor_filename) 372 else: 373 qemu_cmd += add_human_monitor(help, monitor_filename) 374 375 # Add serial console redirection 376 qemu_cmd += add_serial(help, vm.get_serial_console_filename()) 377 378 for image_name in params.objects("images"): 379 image_params = params.object_params(image_name) 380 if image_params.get("boot_drive") == "no": 381 continue 382 qemu_cmd += add_drive(help, 383 virt_vm.get_image_filename(image_params, root_dir), 384 image_params.get("drive_index"), 385 image_params.get("drive_format"), 386 image_params.get("drive_cache"), 387 image_params.get("drive_werror"), 388 image_params.get("drive_serial"), 389 image_params.get("image_snapshot") == "yes", 390 image_params.get("image_boot") == "yes") 391 392 redirs = [] 393 for redir_name in params.objects("redirs"): 394 redir_params = params.object_params(redir_name) 395 guest_port = int(redir_params.get("guest_port")) 396 host_port = vm.redirs.get(guest_port) 397 redirs += [(host_port, guest_port)] 398 399 vlan = 0 400 for nic_name in params.objects("nics"): 401 nic_params = params.object_params(nic_name) 402 try: 403 netdev_id = vm.netdev_id[vlan] 404 device_id = vm.device_id[vlan] 405 except IndexError: 406 netdev_id = None 407 device_id = None 408 # Handle the '-net nic' part 409 try: 410 mac = vm.get_mac_address(vlan) 411 except virt_vm.VMAddressError: 412 mac = None 413 qemu_cmd += add_nic(help, vlan, nic_params.get("nic_model"), mac, 414 device_id, netdev_id, nic_params.get("nic_extra_params")) 415 # Handle the '-net tap' or '-net user' or '-netdev' part 416 script = nic_params.get("nic_script") 417 downscript = nic_params.get("nic_downscript") 418 tftp = nic_params.get("tftp") 419 if script: 420 script = virt_utils.get_path(root_dir, script) 421 if downscript: 422 downscript = virt_utils.get_path(root_dir, downscript) 423 if tftp: 424 tftp = virt_utils.get_path(root_dir, tftp) 425 qemu_cmd += add_net(help, vlan, nic_params.get("nic_mode", "user"), 426 vm.get_ifname(vlan), 427 script, downscript, tftp, 428 nic_params.get("bootp"), redirs, netdev_id, 429 nic_params.get("netdev_extra_params")) 430 # Proceed to next NIC 431 vlan += 1 432 433 mem = params.get("mem") 434 if mem: 435 qemu_cmd += add_mem(help, mem) 436 437 smp = params.get("smp") 438 if smp: 439 qemu_cmd += add_smp(help, smp) 440 441 for cdrom in params.objects("cdroms"): 442 cdrom_params = params.object_params(cdrom) 443 iso = cdrom_params.get("cdrom") 444 if iso: 445 qemu_cmd += add_cdrom(help, virt_utils.get_path(root_dir, iso), 446 cdrom_params.get("drive_index")) 447 448 # We may want to add {floppy_otps} parameter for -fda 449 # {fat:floppy:}/path/. However vvfat is not usually recommended. 450 floppy = params.get("floppy") 451 if floppy: 452 floppy = virt_utils.get_path(root_dir, floppy) 453 qemu_cmd += add_floppy(help, floppy) 454 455 tftp = params.get("tftp") 456 if tftp: 457 tftp = virt_utils.get_path(root_dir, tftp) 458 qemu_cmd += add_tftp(help, tftp) 459 460 bootp = params.get("bootp") 461 if bootp: 462 qemu_cmd += add_bootp(help, bootp) 463 464 kernel = params.get("kernel") 465 if kernel: 466 kernel = virt_utils.get_path(root_dir, kernel) 467 qemu_cmd += add_kernel(help, kernel) 468 469 kernel_cmdline = params.get("kernel_cmdline") 470 if kernel_cmdline: 471 qemu_cmd += add_kernel_cmdline(help, kernel_cmdline) 472 473 initrd = params.get("initrd") 474 if initrd: 475 initrd = virt_utils.get_path(root_dir, initrd) 476 qemu_cmd += add_initrd(help, initrd) 477 478 for host_port, guest_port in redirs: 479 qemu_cmd += add_tcp_redir(help, host_port, guest_port) 480 481 if params.get("display") == "vnc": 482 qemu_cmd += add_vnc(help, vm.vnc_port) 483 elif params.get("display") == "sdl": 484 qemu_cmd += add_sdl(help) 485 elif params.get("display") == "nographic": 486 qemu_cmd += add_nographic(help) 487 elif params.get("display") == "spice": 488 qemu_cmd += add_spice(help, self.spice_port, params.get("spice")) 489 490 qxl = "" 491 vga = "" 492 if params.get("qxl"): 493 qxl = params.get("qxl") 494 if params.get("vga"): 495 vga = params.get("vga") 496 if qxl or vga: 497 if params.get("display") == "spice": 498 qxl_dev_nr = params.get("qxl_dev_nr", None) 499 qemu_cmd += add_qxl_vga(help, qxl, vga, qxl_dev_nr) 500 501 if params.get("uuid") == "random": 502 qemu_cmd += add_uuid(help, vm.uuid) 503 elif params.get("uuid"): 504 qemu_cmd += add_uuid(help, params.get("uuid")) 505 506 if params.get("testdev") == "yes": 507 qemu_cmd += add_testdev(help, vm.get_testlog_filename()) 508 509 if params.get("disable_hpet") == "yes": 510 qemu_cmd += add_no_hpet(help) 511 512 # If the PCI assignment step went OK, add each one of the PCI assigned 513 # devices to the qemu command line. 514 if vm.pci_assignable: 515 for pci_id in vm.pa_pci_ids: 516 qemu_cmd += add_pcidevice(help, pci_id) 517 518 extra_params = params.get("extra_params") 519 if extra_params: 520 qemu_cmd += " %s" % extra_params 521 522 return qemu_cmd 523 524 525 @error.context_aware 526 def create(self, name=None, params=None, root_dir=None, timeout=5.0, 527 migration_mode=None, mac_source=None): 528 """ 529 Start the VM by running a qemu command. 530 All parameters are optional. If name, params or root_dir are not 531 supplied, the respective values stored as class attributes are used. 532 533 @param name: The name of the object 534 @param params: A dict containing VM params 535 @param root_dir: Base directory for relative filenames 536 @param migration_mode: If supplied, start VM for incoming migration 537 using this protocol (either 'tcp', 'unix' or 'exec') 538 @param migration_exec_cmd: Command to embed in '-incoming "exec: ..."' 539 (e.g. 'gzip -c -d filename') if migration_mode is 'exec' 540 @param mac_source: A VM object from which to copy MAC addresses. If not 541 specified, new addresses will be generated. 542 543 @raise VMCreateError: If qemu terminates unexpectedly 544 @raise VMKVMInitError: If KVM initialization fails 545 @raise VMHugePageError: If hugepage initialization fails 546 @raise VMImageMissingError: If a CD image is missing 547 @raise VMHashMismatchError: If a CD image hash has doesn't match the 548 expected hash 549 @raise VMBadPATypeError: If an unsupported PCI assignment type is 550 requested 551 @raise VMPAError: If no PCI assignable devices could be assigned 552 """ 553 error.context("creating '%s'" % self.name) 554 self.destroy(free_mac_addresses=False) 555 556 if name is not None: 557 self.name = name 558 if params is not None: 559 self.params = params 560 if root_dir is not None: 561 self.root_dir = root_dir 562 name = self.name 563 params = self.params 564 root_dir = self.root_dir 565 566 # Verify the md5sum of the ISO images 567 for cdrom in params.objects("cdroms"): 568 cdrom_params = params.object_params(cdrom) 569 iso = cdrom_params.get("cdrom") 570 if iso: 571 iso = virt_utils.get_path(root_dir, iso) 572 if not os.path.exists(iso): 573 raise virt_vm.VMImageMissingError(iso) 574 compare = False 575 if cdrom_params.get("md5sum_1m"): 576 logging.debug("Comparing expected MD5 sum with MD5 sum of " 577 "first MB of ISO file...") 578 actual_hash = utils.hash_file(iso, 1048576, method="md5") 579 expected_hash = cdrom_params.get("md5sum_1m") 580 compare = True 581 elif cdrom_params.get("md5sum"): 582 logging.debug("Comparing expected MD5 sum with MD5 sum of " 583 "ISO file...") 584 actual_hash = utils.hash_file(iso, method="md5") 585 expected_hash = cdrom_params.get("md5sum") 586 compare = True 587 elif cdrom_params.get("sha1sum"): 588 logging.debug("Comparing expected SHA1 sum with SHA1 sum " 589 "of ISO file...") 590 actual_hash = utils.hash_file(iso, method="sha1") 591 expected_hash = cdrom_params.get("sha1sum") 592 compare = True 593 if compare: 594 if actual_hash == expected_hash: 595 logging.debug("Hashes match") 596 else: 597 raise virt_vm.VMHashMismatchError(actual_hash, 598 expected_hash) 599 600 # Make sure the following code is not executed by more than one thread 601 # at the same time 602 lockfile = open("/tmp/kvm-autotest-vm-create.lock", "w+") 603 fcntl.lockf(lockfile, fcntl.LOCK_EX) 604 605 try: 606 # Handle port redirections 607 redir_names = params.objects("redirs") 608 host_ports = virt_utils.find_free_ports(5000, 6000, len(redir_names)) 609 self.redirs = {} 610 for i in range(len(redir_names)): 611 redir_params = params.object_params(redir_names[i]) 612 guest_port = int(redir_params.get("guest_port")) 613 self.redirs[guest_port] = host_ports[i] 614 615 # Generate netdev/device IDs for all NICs 616 self.netdev_id = [] 617 self.device_id = [] 618 for nic in params.objects("nics"): 619 self.netdev_id.append(virt_utils.generate_random_id()) 620 self.device_id.append(virt_utils.generate_random_id()) 621 622 # Find available VNC port, if needed 623 if params.get("display") == "vnc": 624 self.vnc_port = virt_utils.find_free_port(5900, 6100) 625 626 # Find available spice port, if needed 627 if params.get("spice"): 628 self.spice_port = virt_utils.find_free_port(8000, 8100) 629 630 # Find random UUID if specified 'uuid = random' in config file 631 if params.get("uuid") == "random": 632 f = open("/proc/sys/kernel/random/uuid") 633 self.uuid = f.read().strip() 634 f.close() 635 636 # Generate or copy MAC addresses for all NICs 637 num_nics = len(params.objects("nics")) 638 for vlan in range(num_nics): 639 nic_name = params.objects("nics")[vlan] 640 nic_params = params.object_params(nic_name) 641 mac = (nic_params.get("nic_mac") or 642 mac_source and mac_source.get_mac_address(vlan)) 643 if mac: 644 virt_utils.set_mac_address(self.instance, vlan, mac) 645 else: 646 virt_utils.generate_mac_address(self.instance, vlan) 647 648 # Assign a PCI assignable device 649 self.pci_assignable = None 650 pa_type = params.get("pci_assignable") 651 if pa_type and pa_type != "no": 652 pa_devices_requested = params.get("devices_requested") 653 654 # Virtual Functions (VF) assignable devices 655 if pa_type == "vf": 656 self.pci_assignable = virt_utils.PciAssignable( 657 type=pa_type, 658 driver=params.get("driver"), 659 driver_option=params.get("driver_option"), 660 devices_requested=pa_devices_requested) 661 # Physical NIC (PF) assignable devices 662 elif pa_type == "pf": 663 self.pci_assignable = virt_utils.PciAssignable( 664 type=pa_type, 665 names=params.get("device_names"), 666 devices_requested=pa_devices_requested) 667 # Working with both VF and PF 668 elif pa_type == "mixed": 669 self.pci_assignable = virt_utils.PciAssignable( 670 type=pa_type, 671 driver=params.get("driver"), 672 driver_option=params.get("driver_option"), 673 names=params.get("device_names"), 674 devices_requested=pa_devices_requested) 675 else: 676 raise virt_vm.VMBadPATypeError(pa_type) 677 678 self.pa_pci_ids = self.pci_assignable.request_devs() 679 680 if self.pa_pci_ids: 681 logging.debug("Successfuly assigned devices: %s", 682 self.pa_pci_ids) 683 else: 684 raise virt_vm.VMPAError(pa_type) 685 686 # Make qemu command 687 qemu_command = self.__make_qemu_command() 688 689 # Add migration parameters if required 690 if migration_mode == "tcp": 691 self.migration_port = virt_utils.find_free_port(5200, 6000) 692 qemu_command += " -incoming tcp:0:%d" % self.migration_port 693 elif migration_mode == "unix": 694 self.migration_file = "/tmp/migration-unix-%s" % self.instance 695 qemu_command += " -incoming unix:%s" % self.migration_file 696 elif migration_mode == "exec": 697 self.migration_port = virt_utils.find_free_port(5200, 6000) 698 qemu_command += (' -incoming "exec:nc -l %s"' % 699 self.migration_port) 700 701 logging.info("Running qemu command:\n%s", qemu_command) 702 self.process = aexpect.run_bg(qemu_command, None, 703 logging.info, "(qemu) ") 704 705 # Make sure the process was started successfully 706 if not self.process.is_alive(): 707 e = virt_vm.VMCreateError(qemu_command, 708 self.process.get_status(), 709 self.process.get_output()) 710 self.destroy() 711 raise e 712 713 # Establish monitor connections 714 self.monitors = [] 715 for monitor_name in params.objects("monitors"): 716 monitor_params = params.object_params(monitor_name) 717 # Wait for monitor connection to succeed 718 end_time = time.time() + timeout 719 while time.time() < end_time: 720 try: 721 if monitor_params.get("monitor_type") == "qmp": 722 # Add a QMP monitor 723 monitor = kvm_monitor.QMPMonitor( 724 monitor_name, 725 self.get_monitor_filename(monitor_name)) 726 else: 727 # Add a "human" monitor 728 monitor = kvm_monitor.HumanMonitor( 729 monitor_name, 730 self.get_monitor_filename(monitor_name)) 731 monitor.verify_responsive() 732 break 733 except kvm_monitor.MonitorError, e: 734 logging.warn(e) 735 time.sleep(1) 736 else: 737 self.destroy() 738 raise e 739 # Add this monitor to the list 740 self.monitors += [monitor] 741 742 # Get the output so far, to see if we have any problems with 743 # KVM modules or with hugepage setup. 744 output = self.process.get_output() 745 746 if re.search("Could not initialize KVM", output, re.IGNORECASE): 747 e = virt_vm.VMKVMInitError(qemu_command, self.process.get_output()) 748 self.destroy() 749 raise e 750 751 if "alloc_mem_area" in output: 752 e = virt_vm.VMHugePageError(qemu_command, self.process.get_output()) 753 self.destroy() 754 raise e 755 756 logging.debug("VM appears to be alive with PID %s", self.get_pid()) 757 758 # Establish a session with the serial console -- requires a version 759 # of netcat that supports -U 760 self.serial_console = aexpect.ShellSession( 761 "nc -U %s" % self.get_serial_console_filename(), 762 auto_close=False, 763 output_func=virt_utils.log_line, 764 output_params=("serial-%s.log" % name,)) 765 766 finally: 767 fcntl.lockf(lockfile, fcntl.LOCK_UN) 768 lockfile.close() 769 770 771 def destroy(self, gracefully=True, free_mac_addresses=True): 772 """ 773 Destroy the VM. 774 775 If gracefully is True, first attempt to shutdown the VM with a shell 776 command. Then, attempt to destroy the VM via the monitor with a 'quit' 777 command. If that fails, send SIGKILL to the qemu process. 778 779 @param gracefully: If True, an attempt will be made to end the VM 780 using a shell command before trying to end the qemu process 781 with a 'quit' or a kill signal. 782 @param free_mac_addresses: If True, the MAC addresses used by the VM 783 will be freed. 784 """ 785 try: 786 # Is it already dead? 787 if self.is_dead(): 788 return 789 790 logging.debug("Destroying VM with PID %s...", self.get_pid()) 791 792 if gracefully and self.params.get("shutdown_command"): 793 # Try to destroy with shell command 794 logging.debug("Trying to shutdown VM with shell command...") 795 try: 796 session = self.login() 797 except (virt_utils.LoginError, virt_vm.VMError), e: 798 logging.debug(e) 799 else: 800 try: 801 # Send the shutdown command 802 session.sendline(self.params.get("shutdown_command")) 803 logging.debug("Shutdown command sent; waiting for VM " 804 "to go down...") 805 if virt_utils.wait_for(self.is_dead, 60, 1, 1): 806 logging.debug("VM is down") 807 return 808 finally: 809 session.close() 810 811 if self.monitor: 812 # Try to destroy with a monitor command 813 logging.debug("Trying to kill VM with monitor command...") 814 try: 815 self.monitor.quit() 816 except kvm_monitor.MonitorError, e: 817 logging.warn(e) 818 else: 819 # Wait for the VM to be really dead 820 if virt_utils.wait_for(self.is_dead, 5, 0.5, 0.5): 821 logging.debug("VM is down") 822 return 823 824 # If the VM isn't dead yet... 825 logging.debug("Cannot quit normally; sending a kill to close the " 826 "deal...") 827 virt_utils.kill_process_tree(self.process.get_pid(), 9) 828 # Wait for the VM to be really dead 829 if virt_utils.wait_for(self.is_dead, 5, 0.5, 0.5): 830 logging.debug("VM is down") 831 return 832 833 logging.error("Process %s is a zombie!", self.process.get_pid()) 834 835 finally: 836 self.monitors = [] 837 if self.pci_assignable: 838 self.pci_assignable.release_devs() 839 if self.process: 840 self.process.close() 841 if self.serial_console: 842 self.serial_console.close() 843 for f in ([self.get_testlog_filename(), 844 self.get_serial_console_filename()] + 845 self.get_monitor_filenames()): 846 try: 847 os.unlink(f) 848 except OSError: 849 pass 850 if hasattr(self, "migration_file"): 851 try: 852 os.unlink(self.migration_file) 853 except OSError: 854 pass 855 if free_mac_addresses: 856 num_nics = len(self.params.objects("nics")) 857 for vlan in range(num_nics): 858 self.free_mac_address(vlan) 859 860 861 @property 862 def monitor(self): 863 """ 864 Return the main monitor object, selected by the parameter main_monitor. 865 If main_monitor isn't defined, return the first monitor. 866 If no monitors exist, or if main_monitor refers to a nonexistent 867 monitor, return None. 868 """ 869 for m in self.monitors: 870 if m.name == self.params.get("main_monitor"): 871 return m 872 if self.monitors and not self.params.get("main_monitor"): 873 return self.monitors[0] 874 875 876 def get_monitor_filename(self, monitor_name): 877 """ 878 Return the filename corresponding to a given monitor name. 879 """ 880 return "/tmp/monitor-%s-%s" % (monitor_name, self.instance) 881 882 883 def get_monitor_filenames(self): 884 """ 885 Return a list of all monitor filenames (as specified in the VM's 886 params). 887 """ 888 return [self.get_monitor_filename(m) for m in 889 self.params.objects("monitors")] 890 891 892 def get_address(self, index=0): 893 """ 894 Return the address of a NIC of the guest, in host space. 895 896 If port redirection is used, return 'localhost' (the NIC has no IP 897 address of its own). Otherwise return the NIC's IP address. 898 899 @param index: Index of the NIC whose address is requested. 900 @raise VMMACAddressMissingError: If no MAC address is defined for the 901 requested NIC 902 @raise VMIPAddressMissingError: If no IP address is found for the the 903 NIC's MAC address 904 @raise VMAddressVerificationError: If the MAC-IP address mapping cannot 905 be verified (using arping) 906 """ 907 nics = self.params.objects("nics") 908 nic_name = nics[index] 909 nic_params = self.params.object_params(nic_name) 910 if nic_params.get("nic_mode") == "tap": 911 mac = self.get_mac_address(index).lower() 912 # Get the IP address from the cache 913 ip = self.address_cache.get(mac) 914 if not ip: 915 raise virt_vm.VMIPAddressMissingError(mac) 916 # Make sure the IP address is assigned to this guest 917 macs = [self.get_mac_address(i) for i in range(len(nics))] 918 if not virt_utils.verify_ip_address_ownership(ip, macs): 919 raise virt_vm.VMAddressVerificationError(mac, ip) 920 return ip 921 else: 922 return "localhost" 923 924 925 def get_port(self, port, nic_index=0): 926 """ 927 Return the port in host space corresponding to port in guest space. 928 929 @param port: Port number in host space. 930 @param nic_index: Index of the NIC. 931 @return: If port redirection is used, return the host port redirected 932 to guest port port. Otherwise return port. 933 @raise VMPortNotRedirectedError: If an unredirected port is requested 934 in user mode 935 """ 936 nic_name = self.params.objects("nics")[nic_index] 937 nic_params = self.params.object_params(nic_name) 938 if nic_params.get("nic_mode") == "tap": 939 return port 940 else: 941 try: 942 return self.redirs[port] 943 except KeyError: 944 raise virt_vm.VMPortNotRedirectedError(port) 945 946 947 def get_peer(self, netid): 948 """ 949 Return the peer of netdev or network deivce. 950 951 @param netid: id of netdev or device 952 @return: id of the peer device otherwise None 953 """ 954 network_info = self.monitor.info("network") 955 try: 956 return re.findall("%s:.*peer=(.*)" % netid, network_info)[0] 957 except IndexError: 958 return None 959 960 961 def get_ifname(self, nic_index=0): 962 """ 963 Return the ifname of a tap device associated with a NIC. 964 965 @param nic_index: Index of the NIC 966 """ 967 nics = self.params.objects("nics") 968 nic_name = nics[nic_index] 969 nic_params = self.params.object_params(nic_name) 970 if nic_params.get("nic_ifname"): 971 return nic_params.get("nic_ifname") 972 else: 973 return "t%d-%s" % (nic_index, self.instance[-11:]) 974 975 976 def get_mac_address(self, nic_index=0): 977 """ 978 Return the MAC address of a NIC. 979 980 @param nic_index: Index of the NIC 981 @raise VMMACAddressMissingError: If no MAC address is defined for the 982 requested NIC 983 """ 984 nic_name = self.params.objects("nics")[nic_index] 985 nic_params = self.params.object_params(nic_name) 986 mac = (nic_params.get("nic_mac") or 987 virt_utils.get_mac_address(self.instance, nic_index)) 988 if not mac: 989 raise virt_vm.VMMACAddressMissingError(nic_index) 990 return mac 991 992 993 def free_mac_address(self, nic_index=0): 994 """ 995 Free a NIC's MAC address. 996 997 @param nic_index: Index of the NIC 998 """ 999 virt_utils.free_mac_address(self.instance, nic_index) 1000 1001 1002 def get_pid(self): 1003 """ 1004 Return the VM's PID. If the VM is dead return None. 1005 1006 @note: This works under the assumption that self.process.get_pid() 1007 returns the PID of the parent shell process. 1008 """ 1009 try: 1010 children = commands.getoutput("ps --ppid=%d -o pid=" % 1011 self.process.get_pid()).split() 1012 return int(children[0]) 1013 except (TypeError, IndexError, ValueError): 1014 return None 1015 1016 1017 def get_shell_pid(self): 1018 """ 1019 Return the PID of the parent shell process. 1020 1021 @note: This works under the assumption that self.process.get_pid() 1022 returns the PID of the parent shell process. 1023 """ 1024 return self.process.get_pid() 1025 1026 1027 def get_shared_meminfo(self): 1028 """ 1029 Returns the VM's shared memory information. 1030 1031 @return: Shared memory used by VM (MB) 1032 """ 1033 if self.is_dead(): 1034 logging.error("Could not get shared memory info from dead VM.") 1035 return None 1036 1037 filename = "/proc/%d/statm" % self.get_pid() 1038 shm = int(open(filename).read().split()[2]) 1039 # statm stores informations in pages, translate it to MB 1040 return shm * 4.0 / 1024 1041 1042 1043 @error.context_aware 1044 def migrate(self, timeout=3600, protocol="tcp", cancel_delay=None, 1045 offline=False, stable_check=False, clean=True, 1046 save_path="/tmp", dest_host="localhost", remote_port=None): 1047 """ 1048 Migrate the VM. 1049 1050 If the migration is local, the VM object's state is switched with that 1051 of the destination VM. Otherwise, the state is switched with that of 1052 a dead VM (returned by self.clone()). 1053 1054 @param timeout: Time to wait for migration to complete. 1055 @param protocol: Migration protocol (as defined in MIGRATION_PROTOS) 1056 @param cancel_delay: If provided, specifies a time duration after which 1057 migration will be canceled. Used for testing migrate_cancel. 1058 @param offline: If True, pause the source VM before migration. 1059 @param stable_check: If True, compare the VM's state after migration to 1060 its state before migration and raise an exception if they 1061 differ. 1062 @param clean: If True, delete the saved state files (relevant only if 1063 stable_check is also True). 1064 @save_path: The path for state files. 1065 @param dest_host: Destination host (defaults to 'localhost'). 1066 @param remote_port: Port to use for remote migration. 1067 """ 1068 if protocol not in self.MIGRATION_PROTOS: 1069 raise virt_vm.VMMigrateProtoUnsupportedError 1070 1071 error.base_context("migrating '%s'" % self.name) 1072 1073 def mig_finished(): 1074 o = self.monitor.info("migrate") 1075 if isinstance(o, str): 1076 return "status: active" not in o 1077 else: 1078 return o.get("status") != "active" 1079 1080 def mig_succeeded(): 1081 o = self.monitor.info("migrate") 1082 if isinstance(o, str): 1083 return "status: completed" in o 1084 else: 1085 return o.get("status") == "completed" 1086 1087 def mig_failed(): 1088 o = self.monitor.info("migrate") 1089 if isinstance(o, str): 1090 return "status: failed" in o 1091 else: 1092 return o.get("status") == "failed" 1093 1094 def mig_cancelled(): 1095 o = self.monitor.info("migrate") 1096 if isinstance(o, str): 1097 return ("Migration status: cancelled" in o or 1098 "Migration status: canceled" in o) 1099 else: 1100 return (o.get("status") == "cancelled" or 1101 o.get("status") == "canceled") 1102 1103 def wait_for_migration(): 1104 if not virt_utils.wait_for(mig_finished, timeout, 2, 2, 1105 "Waiting for migration to complete"): 1106 raise virt_vm.VMMigrateTimeoutError("Timeout expired while waiting " 1107 "for migration to finish") 1108 1109 local = dest_host == "localhost" 1110 1111 clone = self.clone() 1112 if local: 1113 error.context("creating destination VM") 1114 if stable_check: 1115 # Pause the dest vm after creation 1116 extra_params = clone.params.get("extra_params", "") + " -S" 1117 clone.params["extra_params"] = extra_params 1118 clone.create(migration_mode=protocol, mac_source=self) 1119 error.context() 1120 1121 try: 1122 if protocol == "tcp": 1123 if local: 1124 uri = "tcp:localhost:%d" % clone.migration_port 1125 else: 1126 uri = "tcp:%s:%d" % (dest_host, remote_port) 1127 elif protocol == "unix": 1128 uri = "unix:%s" % clone.migration_file 1129 elif protocol == "exec": 1130 uri = '"exec:nc localhost %s"' % clone.migration_port 1131 1132 if offline: 1133 self.monitor.cmd("stop") 1134 1135 logging.info("Migrating to %s", uri) 1136 self.monitor.migrate(uri) 1137 1138 if cancel_delay: 1139 time.sleep(cancel_delay) 1140 self.monitor.cmd("migrate_cancel") 1141 if not virt_utils.wait_for(mig_cancelled, 60, 2, 2, 1142 "Waiting for migration " 1143 "cancellation"): 1144 raise virt_vm.VMMigrateCancelError("Cannot cancel migration") 1145 return 1146 1147 wait_for_migration() 1148 1149 # Report migration status 1150 if mig_succeeded(): 1151 logging.info("Migration completed successfully") 1152 elif mig_failed(): 1153 raise virt_vm.VMMigrateFailedError("Migration failed") 1154 else: 1155 raise virt_vm.VMMigrateFailedError("Migration ended with " 1156 "unknown status") 1157 1158 # Switch self <-> clone 1159 temp = self.clone(copy_state=True) 1160 self.__dict__ = clone.__dict__ 1161 clone = temp 1162 1163 # From now on, clone is the source VM that will soon be destroyed 1164 # and self is the destination VM that will remain alive. If this 1165 # is remote migration, self is a dead VM object. 1166 1167 error.context("after migration") 1168 if local: 1169 time.sleep(1) 1170 self.verify_alive() 1171 1172 if local and stable_check: 1173 try: 1174 save1 = os.path.join(save_path, "src-" + clone.instance) 1175 save2 = os.path.join(save_path, "dst-" + self.instance) 1176 clone.save_to_file(save1) 1177 self.save_to_file(save2) 1178 # Fail if we see deltas 1179 md5_save1 = utils.hash_file(save1) 1180 md5_save2 = utils.hash_file(save2) 1181 if md5_save1 != md5_save2: 1182 raise virt_vm.VMMigrateStateMismatchError(md5_save1, 1183 md5_save2) 1184 finally: 1185 if clean: 1186 if os.path.isfile(save1): 1187 os.remove(save1) 1188 if os.path.isfile(save2): 1189 os.remove(save2) 1190 1191 finally: 1192 # If we're doing remote migration and it's completed successfully, 1193 # self points to a dead VM object 1194 if self.is_alive(): 1195 self.monitor.cmd("cont") 1196 clone.destroy(gracefully=False) 1197 1198 1199 @error.context_aware 1200 def reboot(self, session=None, method="shell", nic_index=0, timeout=240): 1201 """ 1202 Reboot the VM and wait for it to come back up by trying to log in until 1203 timeout expires. 1204 1205 @param session: A shell session object or None. 1206 @param method: Reboot method. Can be "shell" (send a shell reboot 1207 command) or "system_reset" (send a system_reset monitor command). 1208 @param nic_index: Index of NIC to access in the VM, when logging in 1209 after rebooting. 1210 @param timeout: Time to wait for login to succeed (after rebooting). 1211 @return: A new shell session object. 1212 """ 1213 error.base_context("rebooting '%s'" % self.name, logging.info) 1214 error.context("before reboot") 1215 session = session or self.login() 1216 error.context() 1217 1218 if method == "shell": 1219 session.sendline(self.params.get("reboot_command")) 1220 elif method == "system_reset": 1221 # Clear the event list of all QMP monitors 1222 qmp_monitors = [m for m in self.monitors if m.protocol == "qmp"] 1223 for m in qmp_monitors: 1224 m.clear_events() 1225 # Send a system_reset monitor command 1226 self.monitor.cmd("system_reset") 1227 # Look for RESET QMP events 1228 time.sleep(1) 1229 for m in qmp_monitors: 1230 if m.get_event("RESET"): 1231 logging.info("RESET QMP event received") 1232 else: 1233 raise virt_vm.VMRebootError("RESET QMP event not received " 1234 "after system_reset " 1235 "(monitor '%s')" % m.name) 1236 else: 1237 raise virt_vm.VMRebootError("Unknown reboot method: %s" % method) 1238 1239 error.context("waiting for guest to go down", logging.info) 1240 if not virt_utils.wait_for(lambda: 1241 not session.is_responsive(timeout=30), 1242 120, 0, 1): 1243 raise virt_vm.VMRebootError("Guest refuses to go down") 1244 session.close() 1245 1246 error.context("logging in after reboot", logging.info) 1247 return self.wait_for_login(nic_index, timeout=timeout) 1248 1249 1250 def send_key(self, keystr): 1251 """ 1252 Send a key event to the VM. 1253 1254 @param: keystr: A key event string (e.g. "ctrl-alt-delete") 1255 """ 1256 # For compatibility with versions of QEMU that do not recognize all 1257 # key names: replace keyname with the hex value from the dict, which 1258 # QEMU will definitely accept 1259 dict = {"comma": "0x33", 1260 "dot": "0x34", 1261 "slash": "0x35"} 1262 for key, value in dict.items(): 1263 keystr = keystr.replace(key, value) 1264 self.monitor.sendkey(keystr) 1265 time.sleep(0.2) 1266 1267 1268 # should this really be expected from VMs of all hypervisor types? 1269 def screendump(self, filename): 1270 try: 1271 if self.monitor: 1272 self.monitor.screendump(filename=filename) 1273 except kvm_monitor.MonitorError, e: 1274 logging.warn(e) 1275 1276 1277 def save_to_file(self, path): 1278 """ 1279 Save the state of virtual machine to a file through migrate to 1280 exec 1281 """ 1282 # Make sure we only get one iteration 1283 self.monitor.cmd("migrate_set_speed 1000g") 1284 self.monitor.cmd("migrate_set_downtime 100000000") 1285 self.monitor.migrate('"exec:cat>%s"' % path) 1286 # Restore the speed and downtime of migration 1287 self.monitor.cmd("migrate_set_speed %d" % (32<<20)) 1288 self.monitor.cmd("migrate_set_downtime 0.03") 1289 1290 1291 def needs_restart(self, name, params, basedir): 1292 """ 1293 Verifies whether the current qemu commandline matches the requested 1294 one, based on the test parameters. 1295 """ 1296 return (self.__make_qemu_command() != 1297 self.__make_qemu_command(name, params, basedir)) 1298