KVM test: setup tap fd and pass it to qemu-kvm v3
[autotest-zwu.git] / client / virt / kvm_vm.py
blob21300f979e88dd8baa8bb7c1a70f2a68e02447fe
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 # Add monitors
375 for monitor_name in params.objects("monitors"):
376 monitor_params = params.object_params(monitor_name)
377 monitor_filename = vm.get_monitor_filename(monitor_name)
378 if monitor_params.get("monitor_type") == "qmp":
379 qemu_cmd += add_qmp_monitor(help, monitor_filename)
380 else:
381 qemu_cmd += add_human_monitor(help, monitor_filename)
383 # Add serial console redirection
384 qemu_cmd += add_serial(help, vm.get_serial_console_filename())
386 for image_name in params.objects("images"):
387 image_params = params.object_params(image_name)
388 if image_params.get("boot_drive") == "no":
389 continue
390 qemu_cmd += add_drive(help,
391 virt_vm.get_image_filename(image_params, root_dir),
392 image_params.get("drive_index"),
393 image_params.get("drive_format"),
394 image_params.get("drive_cache"),
395 image_params.get("drive_werror"),
396 image_params.get("drive_serial"),
397 image_params.get("image_snapshot") == "yes",
398 image_params.get("image_boot") == "yes")
400 redirs = []
401 for redir_name in params.objects("redirs"):
402 redir_params = params.object_params(redir_name)
403 guest_port = int(redir_params.get("guest_port"))
404 host_port = vm.redirs.get(guest_port)
405 redirs += [(host_port, guest_port)]
407 vlan = 0
408 for nic_name in params.objects("nics"):
409 nic_params = params.object_params(nic_name)
410 try:
411 netdev_id = vm.netdev_id[vlan]
412 device_id = vm.device_id[vlan]
413 except IndexError:
414 netdev_id = None
415 device_id = None
416 # Handle the '-net nic' part
417 try:
418 mac = vm.get_mac_address(vlan)
419 except virt_vm.VMAddressError:
420 mac = None
421 qemu_cmd += add_nic(help, vlan, nic_params.get("nic_model"), mac,
422 device_id, netdev_id, nic_params.get("nic_extra_params"))
423 # Handle the '-net tap' or '-net user' or '-netdev' part
424 tftp = nic_params.get("tftp")
425 if tftp:
426 tftp = virt_utils.get_path(root_dir, tftp)
427 if nic_params.get("nic_mode") == "tap":
428 try:
429 tapfd = vm.tapfds[vlan]
430 except:
431 tapfd = None
432 else:
433 tapfd = None
434 qemu_cmd += add_net(help, vlan,
435 nic_params.get("nic_mode", "user"),
436 vm.get_ifname(vlan), tftp,
437 nic_params.get("bootp"), redirs, netdev_id,
438 nic_params.get("netdev_extra_params"),
439 tapfd)
440 # Proceed to next NIC
441 vlan += 1
443 mem = params.get("mem")
444 if mem:
445 qemu_cmd += add_mem(help, mem)
447 smp = params.get("smp")
448 if smp:
449 qemu_cmd += add_smp(help, smp)
451 for cdrom in params.objects("cdroms"):
452 cdrom_params = params.object_params(cdrom)
453 iso = cdrom_params.get("cdrom")
454 if iso:
455 qemu_cmd += add_cdrom(help, virt_utils.get_path(root_dir, iso),
456 cdrom_params.get("drive_index"))
458 # We may want to add {floppy_otps} parameter for -fda
459 # {fat:floppy:}/path/. However vvfat is not usually recommended.
460 floppy = params.get("floppy")
461 if floppy:
462 floppy = virt_utils.get_path(root_dir, floppy)
463 qemu_cmd += add_floppy(help, floppy)
465 tftp = params.get("tftp")
466 if tftp:
467 tftp = virt_utils.get_path(root_dir, tftp)
468 qemu_cmd += add_tftp(help, tftp)
470 bootp = params.get("bootp")
471 if bootp:
472 qemu_cmd += add_bootp(help, bootp)
474 kernel = params.get("kernel")
475 if kernel:
476 kernel = virt_utils.get_path(root_dir, kernel)
477 qemu_cmd += add_kernel(help, kernel)
479 kernel_cmdline = params.get("kernel_cmdline")
480 if kernel_cmdline:
481 qemu_cmd += add_kernel_cmdline(help, kernel_cmdline)
483 initrd = params.get("initrd")
484 if initrd:
485 initrd = virt_utils.get_path(root_dir, initrd)
486 qemu_cmd += add_initrd(help, initrd)
488 for host_port, guest_port in redirs:
489 qemu_cmd += add_tcp_redir(help, host_port, guest_port)
491 if params.get("display") == "vnc":
492 qemu_cmd += add_vnc(help, vm.vnc_port)
493 elif params.get("display") == "sdl":
494 qemu_cmd += add_sdl(help)
495 elif params.get("display") == "nographic":
496 qemu_cmd += add_nographic(help)
497 elif params.get("display") == "spice":
498 qemu_cmd += add_spice(help, self.spice_port, params.get("spice"))
500 qxl = ""
501 vga = ""
502 if params.get("qxl"):
503 qxl = params.get("qxl")
504 if params.get("vga"):
505 vga = params.get("vga")
506 if qxl or vga:
507 if params.get("display") == "spice":
508 qxl_dev_nr = params.get("qxl_dev_nr", None)
509 qemu_cmd += add_qxl_vga(help, qxl, vga, qxl_dev_nr)
511 if params.get("uuid") == "random":
512 qemu_cmd += add_uuid(help, vm.uuid)
513 elif params.get("uuid"):
514 qemu_cmd += add_uuid(help, params.get("uuid"))
516 if params.get("testdev") == "yes":
517 qemu_cmd += add_testdev(help, vm.get_testlog_filename())
519 if params.get("disable_hpet") == "yes":
520 qemu_cmd += add_no_hpet(help)
522 # If the PCI assignment step went OK, add each one of the PCI assigned
523 # devices to the qemu command line.
524 if vm.pci_assignable:
525 for pci_id in vm.pa_pci_ids:
526 qemu_cmd += add_pcidevice(help, pci_id)
528 extra_params = params.get("extra_params")
529 if extra_params:
530 qemu_cmd += " %s" % extra_params
532 return qemu_cmd
535 @error.context_aware
536 def create(self, name=None, params=None, root_dir=None, timeout=5.0,
537 migration_mode=None, mac_source=None):
539 Start the VM by running a qemu command.
540 All parameters are optional. If name, params or root_dir are not
541 supplied, the respective values stored as class attributes are used.
543 @param name: The name of the object
544 @param params: A dict containing VM params
545 @param root_dir: Base directory for relative filenames
546 @param migration_mode: If supplied, start VM for incoming migration
547 using this protocol (either 'tcp', 'unix' or 'exec')
548 @param migration_exec_cmd: Command to embed in '-incoming "exec: ..."'
549 (e.g. 'gzip -c -d filename') if migration_mode is 'exec'
550 @param mac_source: A VM object from which to copy MAC addresses. If not
551 specified, new addresses will be generated.
553 @raise VMCreateError: If qemu terminates unexpectedly
554 @raise VMKVMInitError: If KVM initialization fails
555 @raise VMHugePageError: If hugepage initialization fails
556 @raise VMImageMissingError: If a CD image is missing
557 @raise VMHashMismatchError: If a CD image hash has doesn't match the
558 expected hash
559 @raise VMBadPATypeError: If an unsupported PCI assignment type is
560 requested
561 @raise VMPAError: If no PCI assignable devices could be assigned
562 @raise TAPCreationError: If fail to create tap fd
563 @raise BRAddIfError: If fail to add a tap to a bridge
564 @raise TAPBringUpError: If fail to bring up a tap
565 @raise PrivateBridgeError: If fail to bring the private bridge
567 error.context("creating '%s'" % self.name)
568 self.destroy(free_mac_addresses=False)
570 if name is not None:
571 self.name = name
572 if params is not None:
573 self.params = params
574 if root_dir is not None:
575 self.root_dir = root_dir
576 name = self.name
577 params = self.params
578 root_dir = self.root_dir
580 # Verify the md5sum of the ISO images
581 for cdrom in params.objects("cdroms"):
582 cdrom_params = params.object_params(cdrom)
583 iso = cdrom_params.get("cdrom")
584 if iso:
585 iso = virt_utils.get_path(root_dir, iso)
586 if not os.path.exists(iso):
587 raise virt_vm.VMImageMissingError(iso)
588 compare = False
589 if cdrom_params.get("md5sum_1m"):
590 logging.debug("Comparing expected MD5 sum with MD5 sum of "
591 "first MB of ISO file...")
592 actual_hash = utils.hash_file(iso, 1048576, method="md5")
593 expected_hash = cdrom_params.get("md5sum_1m")
594 compare = True
595 elif cdrom_params.get("md5sum"):
596 logging.debug("Comparing expected MD5 sum with MD5 sum of "
597 "ISO file...")
598 actual_hash = utils.hash_file(iso, method="md5")
599 expected_hash = cdrom_params.get("md5sum")
600 compare = True
601 elif cdrom_params.get("sha1sum"):
602 logging.debug("Comparing expected SHA1 sum with SHA1 sum "
603 "of ISO file...")
604 actual_hash = utils.hash_file(iso, method="sha1")
605 expected_hash = cdrom_params.get("sha1sum")
606 compare = True
607 if compare:
608 if actual_hash == expected_hash:
609 logging.debug("Hashes match")
610 else:
611 raise virt_vm.VMHashMismatchError(actual_hash,
612 expected_hash)
614 # Make sure the following code is not executed by more than one thread
615 # at the same time
616 lockfile = open("/tmp/kvm-autotest-vm-create.lock", "w+")
617 fcntl.lockf(lockfile, fcntl.LOCK_EX)
619 try:
620 # Handle port redirections
621 redir_names = params.objects("redirs")
622 host_ports = virt_utils.find_free_ports(5000, 6000, len(redir_names))
623 self.redirs = {}
624 for i in range(len(redir_names)):
625 redir_params = params.object_params(redir_names[i])
626 guest_port = int(redir_params.get("guest_port"))
627 self.redirs[guest_port] = host_ports[i]
629 # Generate netdev IDs for all NICs and create TAP fd
630 self.netdev_id = []
631 self.tapfds = []
632 vlan = 0
633 for nic in params.objects("nics"):
634 self.netdev_id.append(virt_utils.generate_random_id())
635 self.device_id.append(virt_utils.generate_random_id())
636 nic_params = params.object_params(nic)
637 if nic_params.get("nic_mode") == "tap":
638 ifname = self.get_ifname(vlan)
639 brname = nic_params.get("bridge")
640 if brname == "private":
641 brname = virt_test_setup.PrivateBridgeConfig().brname
642 tapfd = virt_utils.open_tap("/dev/net/tun", ifname)
643 virt_utils.add_to_bridge(ifname, brname)
644 virt_utils.bring_up_ifname(ifname)
645 self.tapfds.append(tapfd)
646 vlan += 1
648 # Find available VNC port, if needed
649 if params.get("display") == "vnc":
650 self.vnc_port = virt_utils.find_free_port(5900, 6100)
652 # Find available spice port, if needed
653 if params.get("spice"):
654 self.spice_port = virt_utils.find_free_port(8000, 8100)
656 # Find random UUID if specified 'uuid = random' in config file
657 if params.get("uuid") == "random":
658 f = open("/proc/sys/kernel/random/uuid")
659 self.uuid = f.read().strip()
660 f.close()
662 # Generate or copy MAC addresses for all NICs
663 num_nics = len(params.objects("nics"))
664 for vlan in range(num_nics):
665 nic_name = params.objects("nics")[vlan]
666 nic_params = params.object_params(nic_name)
667 mac = (nic_params.get("nic_mac") or
668 mac_source and mac_source.get_mac_address(vlan))
669 if mac:
670 virt_utils.set_mac_address(self.instance, vlan, mac)
671 else:
672 virt_utils.generate_mac_address(self.instance, vlan)
674 # Assign a PCI assignable device
675 self.pci_assignable = None
676 pa_type = params.get("pci_assignable")
677 if pa_type and pa_type != "no":
678 pa_devices_requested = params.get("devices_requested")
680 # Virtual Functions (VF) assignable devices
681 if pa_type == "vf":
682 self.pci_assignable = virt_utils.PciAssignable(
683 type=pa_type,
684 driver=params.get("driver"),
685 driver_option=params.get("driver_option"),
686 devices_requested=pa_devices_requested)
687 # Physical NIC (PF) assignable devices
688 elif pa_type == "pf":
689 self.pci_assignable = virt_utils.PciAssignable(
690 type=pa_type,
691 names=params.get("device_names"),
692 devices_requested=pa_devices_requested)
693 # Working with both VF and PF
694 elif pa_type == "mixed":
695 self.pci_assignable = virt_utils.PciAssignable(
696 type=pa_type,
697 driver=params.get("driver"),
698 driver_option=params.get("driver_option"),
699 names=params.get("device_names"),
700 devices_requested=pa_devices_requested)
701 else:
702 raise virt_vm.VMBadPATypeError(pa_type)
704 self.pa_pci_ids = self.pci_assignable.request_devs()
706 if self.pa_pci_ids:
707 logging.debug("Successfuly assigned devices: %s",
708 self.pa_pci_ids)
709 else:
710 raise virt_vm.VMPAError(pa_type)
712 # Make qemu command
713 qemu_command = self.__make_qemu_command()
715 # Add migration parameters if required
716 if migration_mode == "tcp":
717 self.migration_port = virt_utils.find_free_port(5200, 6000)
718 qemu_command += " -incoming tcp:0:%d" % self.migration_port
719 elif migration_mode == "unix":
720 self.migration_file = "/tmp/migration-unix-%s" % self.instance
721 qemu_command += " -incoming unix:%s" % self.migration_file
722 elif migration_mode == "exec":
723 self.migration_port = virt_utils.find_free_port(5200, 6000)
724 qemu_command += (' -incoming "exec:nc -l %s"' %
725 self.migration_port)
727 logging.info("Running qemu command:\n%s", qemu_command)
728 self.process = aexpect.run_bg(qemu_command, None,
729 logging.info, "(qemu) ")
730 for tapfd in self.tapfds:
731 try:
732 os.close(tapfd)
733 # File descriptor is already closed
734 except OSError:
735 pass
737 # Make sure the process was started successfully
738 if not self.process.is_alive():
739 e = virt_vm.VMCreateError(qemu_command,
740 self.process.get_status(),
741 self.process.get_output())
742 self.destroy()
743 raise e
745 # Establish monitor connections
746 self.monitors = []
747 for monitor_name in params.objects("monitors"):
748 monitor_params = params.object_params(monitor_name)
749 # Wait for monitor connection to succeed
750 end_time = time.time() + timeout
751 while time.time() < end_time:
752 try:
753 if monitor_params.get("monitor_type") == "qmp":
754 # Add a QMP monitor
755 monitor = kvm_monitor.QMPMonitor(
756 monitor_name,
757 self.get_monitor_filename(monitor_name))
758 else:
759 # Add a "human" monitor
760 monitor = kvm_monitor.HumanMonitor(
761 monitor_name,
762 self.get_monitor_filename(monitor_name))
763 monitor.verify_responsive()
764 break
765 except kvm_monitor.MonitorError, e:
766 logging.warn(e)
767 time.sleep(1)
768 else:
769 self.destroy()
770 raise e
771 # Add this monitor to the list
772 self.monitors += [monitor]
774 # Get the output so far, to see if we have any problems with
775 # KVM modules or with hugepage setup.
776 output = self.process.get_output()
778 if re.search("Could not initialize KVM", output, re.IGNORECASE):
779 e = virt_vm.VMKVMInitError(qemu_command, self.process.get_output())
780 self.destroy()
781 raise e
783 if "alloc_mem_area" in output:
784 e = virt_vm.VMHugePageError(qemu_command, self.process.get_output())
785 self.destroy()
786 raise e
788 logging.debug("VM appears to be alive with PID %s", self.get_pid())
790 # Establish a session with the serial console -- requires a version
791 # of netcat that supports -U
792 self.serial_console = aexpect.ShellSession(
793 "nc -U %s" % self.get_serial_console_filename(),
794 auto_close=False,
795 output_func=virt_utils.log_line,
796 output_params=("serial-%s.log" % name,))
798 finally:
799 fcntl.lockf(lockfile, fcntl.LOCK_UN)
800 lockfile.close()
803 def destroy(self, gracefully=True, free_mac_addresses=True):
805 Destroy the VM.
807 If gracefully is True, first attempt to shutdown the VM with a shell
808 command. Then, attempt to destroy the VM via the monitor with a 'quit'
809 command. If that fails, send SIGKILL to the qemu process.
811 @param gracefully: If True, an attempt will be made to end the VM
812 using a shell command before trying to end the qemu process
813 with a 'quit' or a kill signal.
814 @param free_mac_addresses: If True, the MAC addresses used by the VM
815 will be freed.
817 try:
818 # Is it already dead?
819 if self.is_dead():
820 return
822 logging.debug("Destroying VM with PID %s...", self.get_pid())
824 if gracefully and self.params.get("shutdown_command"):
825 # Try to destroy with shell command
826 logging.debug("Trying to shutdown VM with shell command...")
827 try:
828 session = self.login()
829 except (virt_utils.LoginError, virt_vm.VMError), e:
830 logging.debug(e)
831 else:
832 try:
833 # Send the shutdown command
834 session.sendline(self.params.get("shutdown_command"))
835 logging.debug("Shutdown command sent; waiting for VM "
836 "to go down...")
837 if virt_utils.wait_for(self.is_dead, 60, 1, 1):
838 logging.debug("VM is down")
839 return
840 finally:
841 session.close()
843 if self.monitor:
844 # Try to destroy with a monitor command
845 logging.debug("Trying to kill VM with monitor command...")
846 try:
847 self.monitor.quit()
848 except kvm_monitor.MonitorError, e:
849 logging.warn(e)
850 else:
851 # Wait for the VM to be really dead
852 if virt_utils.wait_for(self.is_dead, 5, 0.5, 0.5):
853 logging.debug("VM is down")
854 return
856 # If the VM isn't dead yet...
857 logging.debug("Cannot quit normally; sending a kill to close the "
858 "deal...")
859 virt_utils.kill_process_tree(self.process.get_pid(), 9)
860 # Wait for the VM to be really dead
861 if virt_utils.wait_for(self.is_dead, 5, 0.5, 0.5):
862 logging.debug("VM is down")
863 return
865 logging.error("Process %s is a zombie!", self.process.get_pid())
867 finally:
868 self.monitors = []
869 if self.pci_assignable:
870 self.pci_assignable.release_devs()
871 if self.process:
872 self.process.close()
873 if self.serial_console:
874 self.serial_console.close()
875 for f in ([self.get_testlog_filename(),
876 self.get_serial_console_filename()] +
877 self.get_monitor_filenames()):
878 try:
879 os.unlink(f)
880 except OSError:
881 pass
882 if hasattr(self, "migration_file"):
883 try:
884 os.unlink(self.migration_file)
885 except OSError:
886 pass
887 if free_mac_addresses:
888 num_nics = len(self.params.objects("nics"))
889 for vlan in range(num_nics):
890 self.free_mac_address(vlan)
893 @property
894 def monitor(self):
896 Return the main monitor object, selected by the parameter main_monitor.
897 If main_monitor isn't defined, return the first monitor.
898 If no monitors exist, or if main_monitor refers to a nonexistent
899 monitor, return None.
901 for m in self.monitors:
902 if m.name == self.params.get("main_monitor"):
903 return m
904 if self.monitors and not self.params.get("main_monitor"):
905 return self.monitors[0]
908 def get_monitor_filename(self, monitor_name):
910 Return the filename corresponding to a given monitor name.
912 return "/tmp/monitor-%s-%s" % (monitor_name, self.instance)
915 def get_monitor_filenames(self):
917 Return a list of all monitor filenames (as specified in the VM's
918 params).
920 return [self.get_monitor_filename(m) for m in
921 self.params.objects("monitors")]
924 def get_address(self, index=0):
926 Return the address of a NIC of the guest, in host space.
928 If port redirection is used, return 'localhost' (the NIC has no IP
929 address of its own). Otherwise return the NIC's IP address.
931 @param index: Index of the NIC whose address is requested.
932 @raise VMMACAddressMissingError: If no MAC address is defined for the
933 requested NIC
934 @raise VMIPAddressMissingError: If no IP address is found for the the
935 NIC's MAC address
936 @raise VMAddressVerificationError: If the MAC-IP address mapping cannot
937 be verified (using arping)
939 nics = self.params.objects("nics")
940 nic_name = nics[index]
941 nic_params = self.params.object_params(nic_name)
942 if nic_params.get("nic_mode") == "tap":
943 mac = self.get_mac_address(index).lower()
944 # Get the IP address from the cache
945 ip = self.address_cache.get(mac)
946 if not ip:
947 raise virt_vm.VMIPAddressMissingError(mac)
948 # Make sure the IP address is assigned to this guest
949 macs = [self.get_mac_address(i) for i in range(len(nics))]
950 if not virt_utils.verify_ip_address_ownership(ip, macs):
951 raise virt_vm.VMAddressVerificationError(mac, ip)
952 return ip
953 else:
954 return "localhost"
957 def get_port(self, port, nic_index=0):
959 Return the port in host space corresponding to port in guest space.
961 @param port: Port number in host space.
962 @param nic_index: Index of the NIC.
963 @return: If port redirection is used, return the host port redirected
964 to guest port port. Otherwise return port.
965 @raise VMPortNotRedirectedError: If an unredirected port is requested
966 in user mode
968 nic_name = self.params.objects("nics")[nic_index]
969 nic_params = self.params.object_params(nic_name)
970 if nic_params.get("nic_mode") == "tap":
971 return port
972 else:
973 try:
974 return self.redirs[port]
975 except KeyError:
976 raise virt_vm.VMPortNotRedirectedError(port)
979 def get_peer(self, netid):
981 Return the peer of netdev or network deivce.
983 @param netid: id of netdev or device
984 @return: id of the peer device otherwise None
986 network_info = self.monitor.info("network")
987 try:
988 return re.findall("%s:.*peer=(.*)" % netid, network_info)[0]
989 except IndexError:
990 return None
993 def get_ifname(self, nic_index=0):
995 Return the ifname of a tap device associated with a NIC.
997 @param nic_index: Index of the NIC
999 nics = self.params.objects("nics")
1000 nic_name = nics[nic_index]
1001 nic_params = self.params.object_params(nic_name)
1002 if nic_params.get("nic_ifname"):
1003 return nic_params.get("nic_ifname")
1004 else:
1005 return "t%d-%s" % (nic_index, self.instance[-11:])
1008 def get_mac_address(self, nic_index=0):
1010 Return the MAC address of a NIC.
1012 @param nic_index: Index of the NIC
1013 @raise VMMACAddressMissingError: If no MAC address is defined for the
1014 requested NIC
1016 nic_name = self.params.objects("nics")[nic_index]
1017 nic_params = self.params.object_params(nic_name)
1018 mac = (nic_params.get("nic_mac") or
1019 virt_utils.get_mac_address(self.instance, nic_index))
1020 if not mac:
1021 raise virt_vm.VMMACAddressMissingError(nic_index)
1022 return mac
1025 def free_mac_address(self, nic_index=0):
1027 Free a NIC's MAC address.
1029 @param nic_index: Index of the NIC
1031 virt_utils.free_mac_address(self.instance, nic_index)
1034 def get_pid(self):
1036 Return the VM's PID. If the VM is dead return None.
1038 @note: This works under the assumption that self.process.get_pid()
1039 returns the PID of the parent shell process.
1041 try:
1042 children = commands.getoutput("ps --ppid=%d -o pid=" %
1043 self.process.get_pid()).split()
1044 return int(children[0])
1045 except (TypeError, IndexError, ValueError):
1046 return None
1049 def get_shell_pid(self):
1051 Return the PID of the parent shell process.
1053 @note: This works under the assumption that self.process.get_pid()
1054 returns the PID of the parent shell process.
1056 return self.process.get_pid()
1059 def get_shared_meminfo(self):
1061 Returns the VM's shared memory information.
1063 @return: Shared memory used by VM (MB)
1065 if self.is_dead():
1066 logging.error("Could not get shared memory info from dead VM.")
1067 return None
1069 filename = "/proc/%d/statm" % self.get_pid()
1070 shm = int(open(filename).read().split()[2])
1071 # statm stores informations in pages, translate it to MB
1072 return shm * 4.0 / 1024
1075 @error.context_aware
1076 def migrate(self, timeout=3600, protocol="tcp", cancel_delay=None,
1077 offline=False, stable_check=False, clean=True,
1078 save_path="/tmp", dest_host="localhost", remote_port=None):
1080 Migrate the VM.
1082 If the migration is local, the VM object's state is switched with that
1083 of the destination VM. Otherwise, the state is switched with that of
1084 a dead VM (returned by self.clone()).
1086 @param timeout: Time to wait for migration to complete.
1087 @param protocol: Migration protocol (as defined in MIGRATION_PROTOS)
1088 @param cancel_delay: If provided, specifies a time duration after which
1089 migration will be canceled. Used for testing migrate_cancel.
1090 @param offline: If True, pause the source VM before migration.
1091 @param stable_check: If True, compare the VM's state after migration to
1092 its state before migration and raise an exception if they
1093 differ.
1094 @param clean: If True, delete the saved state files (relevant only if
1095 stable_check is also True).
1096 @save_path: The path for state files.
1097 @param dest_host: Destination host (defaults to 'localhost').
1098 @param remote_port: Port to use for remote migration.
1100 if protocol not in self.MIGRATION_PROTOS:
1101 raise virt_vm.VMMigrateProtoUnsupportedError
1103 error.base_context("migrating '%s'" % self.name)
1105 def mig_finished():
1106 o = self.monitor.info("migrate")
1107 if isinstance(o, str):
1108 return "status: active" not in o
1109 else:
1110 return o.get("status") != "active"
1112 def mig_succeeded():
1113 o = self.monitor.info("migrate")
1114 if isinstance(o, str):
1115 return "status: completed" in o
1116 else:
1117 return o.get("status") == "completed"
1119 def mig_failed():
1120 o = self.monitor.info("migrate")
1121 if isinstance(o, str):
1122 return "status: failed" in o
1123 else:
1124 return o.get("status") == "failed"
1126 def mig_cancelled():
1127 o = self.monitor.info("migrate")
1128 if isinstance(o, str):
1129 return ("Migration status: cancelled" in o or
1130 "Migration status: canceled" in o)
1131 else:
1132 return (o.get("status") == "cancelled" or
1133 o.get("status") == "canceled")
1135 def wait_for_migration():
1136 if not virt_utils.wait_for(mig_finished, timeout, 2, 2,
1137 "Waiting for migration to complete"):
1138 raise virt_vm.VMMigrateTimeoutError("Timeout expired while waiting "
1139 "for migration to finish")
1141 local = dest_host == "localhost"
1143 clone = self.clone()
1144 if local:
1145 error.context("creating destination VM")
1146 if stable_check:
1147 # Pause the dest vm after creation
1148 extra_params = clone.params.get("extra_params", "") + " -S"
1149 clone.params["extra_params"] = extra_params
1150 clone.create(migration_mode=protocol, mac_source=self)
1151 error.context()
1153 try:
1154 if protocol == "tcp":
1155 if local:
1156 uri = "tcp:localhost:%d" % clone.migration_port
1157 else:
1158 uri = "tcp:%s:%d" % (dest_host, remote_port)
1159 elif protocol == "unix":
1160 uri = "unix:%s" % clone.migration_file
1161 elif protocol == "exec":
1162 uri = '"exec:nc localhost %s"' % clone.migration_port
1164 if offline:
1165 self.monitor.cmd("stop")
1167 logging.info("Migrating to %s", uri)
1168 self.monitor.migrate(uri)
1170 if cancel_delay:
1171 time.sleep(cancel_delay)
1172 self.monitor.cmd("migrate_cancel")
1173 if not virt_utils.wait_for(mig_cancelled, 60, 2, 2,
1174 "Waiting for migration "
1175 "cancellation"):
1176 raise virt_vm.VMMigrateCancelError("Cannot cancel migration")
1177 return
1179 wait_for_migration()
1181 # Report migration status
1182 if mig_succeeded():
1183 logging.info("Migration completed successfully")
1184 elif mig_failed():
1185 raise virt_vm.VMMigrateFailedError("Migration failed")
1186 else:
1187 raise virt_vm.VMMigrateFailedError("Migration ended with "
1188 "unknown status")
1190 # Switch self <-> clone
1191 temp = self.clone(copy_state=True)
1192 self.__dict__ = clone.__dict__
1193 clone = temp
1195 # From now on, clone is the source VM that will soon be destroyed
1196 # and self is the destination VM that will remain alive. If this
1197 # is remote migration, self is a dead VM object.
1199 error.context("after migration")
1200 if local:
1201 time.sleep(1)
1202 self.verify_alive()
1204 if local and stable_check:
1205 try:
1206 save1 = os.path.join(save_path, "src-" + clone.instance)
1207 save2 = os.path.join(save_path, "dst-" + self.instance)
1208 clone.save_to_file(save1)
1209 self.save_to_file(save2)
1210 # Fail if we see deltas
1211 md5_save1 = utils.hash_file(save1)
1212 md5_save2 = utils.hash_file(save2)
1213 if md5_save1 != md5_save2:
1214 raise virt_vm.VMMigrateStateMismatchError(md5_save1,
1215 md5_save2)
1216 finally:
1217 if clean:
1218 if os.path.isfile(save1):
1219 os.remove(save1)
1220 if os.path.isfile(save2):
1221 os.remove(save2)
1223 finally:
1224 # If we're doing remote migration and it's completed successfully,
1225 # self points to a dead VM object
1226 if self.is_alive():
1227 self.monitor.cmd("cont")
1228 clone.destroy(gracefully=False)
1231 @error.context_aware
1232 def reboot(self, session=None, method="shell", nic_index=0, timeout=240):
1234 Reboot the VM and wait for it to come back up by trying to log in until
1235 timeout expires.
1237 @param session: A shell session object or None.
1238 @param method: Reboot method. Can be "shell" (send a shell reboot
1239 command) or "system_reset" (send a system_reset monitor command).
1240 @param nic_index: Index of NIC to access in the VM, when logging in
1241 after rebooting.
1242 @param timeout: Time to wait for login to succeed (after rebooting).
1243 @return: A new shell session object.
1245 error.base_context("rebooting '%s'" % self.name, logging.info)
1246 error.context("before reboot")
1247 session = session or self.login()
1248 error.context()
1250 if method == "shell":
1251 session.sendline(self.params.get("reboot_command"))
1252 elif method == "system_reset":
1253 # Clear the event list of all QMP monitors
1254 qmp_monitors = [m for m in self.monitors if m.protocol == "qmp"]
1255 for m in qmp_monitors:
1256 m.clear_events()
1257 # Send a system_reset monitor command
1258 self.monitor.cmd("system_reset")
1259 # Look for RESET QMP events
1260 time.sleep(1)
1261 for m in qmp_monitors:
1262 if m.get_event("RESET"):
1263 logging.info("RESET QMP event received")
1264 else:
1265 raise virt_vm.VMRebootError("RESET QMP event not received "
1266 "after system_reset "
1267 "(monitor '%s')" % m.name)
1268 else:
1269 raise virt_vm.VMRebootError("Unknown reboot method: %s" % method)
1271 error.context("waiting for guest to go down", logging.info)
1272 if not virt_utils.wait_for(lambda:
1273 not session.is_responsive(timeout=30),
1274 120, 0, 1):
1275 raise virt_vm.VMRebootError("Guest refuses to go down")
1276 session.close()
1278 error.context("logging in after reboot", logging.info)
1279 return self.wait_for_login(nic_index, timeout=timeout)
1282 def send_key(self, keystr):
1284 Send a key event to the VM.
1286 @param: keystr: A key event string (e.g. "ctrl-alt-delete")
1288 # For compatibility with versions of QEMU that do not recognize all
1289 # key names: replace keyname with the hex value from the dict, which
1290 # QEMU will definitely accept
1291 dict = {"comma": "0x33",
1292 "dot": "0x34",
1293 "slash": "0x35"}
1294 for key, value in dict.items():
1295 keystr = keystr.replace(key, value)
1296 self.monitor.sendkey(keystr)
1297 time.sleep(0.2)
1300 # should this really be expected from VMs of all hypervisor types?
1301 def screendump(self, filename):
1302 try:
1303 if self.monitor:
1304 self.monitor.screendump(filename=filename)
1305 except kvm_monitor.MonitorError, e:
1306 logging.warn(e)
1309 def save_to_file(self, path):
1311 Save the state of virtual machine to a file through migrate to
1312 exec
1314 # Make sure we only get one iteration
1315 self.monitor.cmd("migrate_set_speed 1000g")
1316 self.monitor.cmd("migrate_set_downtime 100000000")
1317 self.monitor.migrate('"exec:cat>%s"' % path)
1318 # Restore the speed and downtime of migration
1319 self.monitor.cmd("migrate_set_speed %d" % (32<<20))
1320 self.monitor.cmd("migrate_set_downtime 0.03")
1323 def needs_restart(self, name, params, basedir):
1325 Verifies whether the current qemu commandline matches the requested
1326 one, based on the test parameters.
1328 return (self.__make_qemu_command() !=
1329 self.__make_qemu_command(name, params, basedir))