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