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