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