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