[PATCH 03/11] Moving unattended_install test from kvm test to common virt location

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Signed-off-by: Lucas Meneghel Rodrigues <lmr@xxxxxxxxxx>
---
 client/tests/kvm/tests/unattended_install.py |  632 --------------------------
 client/virt/tests/unattended_install.py      |  632 ++++++++++++++++++++++++++
 2 files changed, 632 insertions(+), 632 deletions(-)
 delete mode 100644 client/tests/kvm/tests/unattended_install.py
 create mode 100644 client/virt/tests/unattended_install.py

diff --git a/client/tests/kvm/tests/unattended_install.py b/client/tests/kvm/tests/unattended_install.py
deleted file mode 100644
index 41a5e60..0000000
--- a/client/tests/kvm/tests/unattended_install.py
+++ /dev/null
@@ -1,632 +0,0 @@
-import logging, time, socket, re, os, shutil, tempfile, glob, ConfigParser
-import xml.dom.minidom
-from autotest_lib.client.common_lib import error
-from autotest_lib.client.bin import utils
-from autotest_lib.client.virt import virt_vm, virt_utils
-
-
-@error.context_aware
-def cleanup(dir):
-    """
-    If dir is a mountpoint, do what is possible to unmount it. Afterwards,
-    try to remove it.
-
-    @param dir: Directory to be cleaned up.
-    """
-    error.context("cleaning up unattended install directory %s" % dir)
-    if os.path.ismount(dir):
-        utils.run('fuser -k %s' % dir, ignore_status=True)
-        utils.run('umount %s' % dir)
-    if os.path.isdir(dir):
-        shutil.rmtree(dir)
-
-
-@error.context_aware
-def clean_old_image(image):
-    """
-    Clean a leftover image file from previous processes. If it contains a
-    mounted file system, do the proper cleanup procedures.
-
-    @param image: Path to image to be cleaned up.
-    """
-    error.context("cleaning up old leftover image %s" % image)
-    if os.path.exists(image):
-        mtab = open('/etc/mtab', 'r')
-        mtab_contents = mtab.read()
-        mtab.close()
-        if image in mtab_contents:
-            utils.run('fuser -k %s' % image, ignore_status=True)
-            utils.run('umount %s' % image)
-        os.remove(image)
-
-
-class Disk(object):
-    """
-    Abstract class for Disk objects, with the common methods implemented.
-    """
-    def __init__(self):
-        self.path = None
-
-
-    def get_answer_file_path(self, filename):
-        return os.path.join(self.mount, filename)
-
-
-    def copy_to(self, src):
-        logging.debug("Copying %s to disk image mount", src)
-        dst = os.path.join(self.mount, os.path.basename(src))
-        if os.path.isdir(src):
-            shutil.copytree(src, dst)
-        elif os.path.isfile(src):
-            shutil.copyfile(src, dst)
-
-
-    def close(self):
-        os.chmod(self.path, 0755)
-        cleanup(self.mount)
-        logging.debug("Disk %s successfuly set", self.path)
-
-
-class FloppyDisk(Disk):
-    """
-    Represents a 1.44 MB floppy disk. We can copy files to it, and setup it in
-    convenient ways.
-    """
-    @error.context_aware
-    def __init__(self, path, qemu_img_binary, tmpdir):
-        error.context("Creating unattended install floppy image %s" % path)
-        self.tmpdir = tmpdir
-        self.mount = tempfile.mkdtemp(prefix='floppy_', dir=self.tmpdir)
-        self.virtio_mount = None
-        self.path = path
-        clean_old_image(path)
-        if not os.path.isdir(os.path.dirname(path)):
-            os.makedirs(os.path.dirname(path))
-
-        try:
-            c_cmd = '%s create -f raw %s 1440k' % (qemu_img_binary, path)
-            utils.run(c_cmd)
-            f_cmd = 'mkfs.msdos -s 1 %s' % path
-            utils.run(f_cmd)
-            m_cmd = 'mount -o loop,rw %s %s' % (path, self.mount)
-            utils.run(m_cmd)
-        except error.CmdError, e:
-            logging.error("Error during floppy initialization: %s" % e)
-            cleanup(self.mount)
-            raise
-
-
-    def _copy_virtio_drivers(self, virtio_floppy):
-        """
-        Copy the virtio drivers on the virtio floppy to the install floppy.
-
-        1) Mount the floppy containing the viostor drivers
-        2) Copy its contents to the root of the install floppy
-        """
-        virtio_mount = tempfile.mkdtemp(prefix='virtio_floppy_',
-                                        dir=self.tmpdir)
-
-        pwd = os.getcwd()
-        try:
-            m_cmd = 'mount -o loop,ro %s %s' % (virtio_floppy, virtio_mount)
-            utils.run(m_cmd)
-            os.chdir(virtio_mount)
-            path_list = glob.glob('*')
-            for path in path_list:
-                self.copy_to(path)
-        finally:
-            os.chdir(pwd)
-            cleanup(virtio_mount)
-
-
-    def setup_virtio_win2003(self, virtio_floppy, virtio_oemsetup_id):
-        """
-        Setup the install floppy with the virtio storage drivers, win2003 style.
-
-        Win2003 and WinXP depend on the file txtsetup.oem file to install
-        the virtio drivers from the floppy, which is a .ini file.
-        Process:
-
-        1) Copy the virtio drivers on the virtio floppy to the install floppy
-        2) Parse the ini file with config parser
-        3) Modify the identifier of the default session that is going to be
-           executed on the config parser object
-        4) Re-write the config file to the disk
-        """
-        self._copy_virtio_drivers(virtio_floppy)
-        txtsetup_oem = os.path.join(self.mount, 'txtsetup.oem')
-        if not os.path.isfile(txtsetup_oem):
-            raise IOError('File txtsetup.oem not found on the install '
-                          'floppy. Please verify if your floppy virtio '
-                          'driver image has this file')
-        parser = ConfigParser.ConfigParser()
-        parser.read(txtsetup_oem)
-        if not parser.has_section('Defaults'):
-            raise ValueError('File txtsetup.oem does not have the session '
-                             '"Defaults". Please check txtsetup.oem')
-        default_driver = parser.get('Defaults', 'SCSI')
-        if default_driver != virtio_oemsetup_id:
-            parser.set('Defaults', 'SCSI', virtio_oemsetup_id)
-            fp = open(txtsetup_oem, 'w')
-            parser.write(fp)
-            fp.close()
-
-
-    def setup_virtio_win2008(self, virtio_floppy):
-        """
-        Setup the install floppy with the virtio storage drivers, win2008 style.
-
-        Win2008, Vista and 7 require people to point out the path to the drivers
-        on the unattended file, so we just need to copy the drivers to the
-        driver floppy disk. Important to note that it's possible to specify
-        drivers from a CDROM, so the floppy driver copy is optional.
-        Process:
-
-        1) Copy the virtio drivers on the virtio floppy to the install floppy,
-           if there is one available
-        """
-        if os.path.isfile(virtio_floppy):
-            self._copy_virtio_drivers(virtio_floppy)
-        else:
-            logging.debug("No virtio floppy present, not needed for this OS anyway")
-
-
-class CdromDisk(Disk):
-    """
-    Represents a CDROM disk that we can master according to our needs.
-    """
-    def __init__(self, path, tmpdir):
-        self.mount = tempfile.mkdtemp(prefix='cdrom_unattended_', dir=tmpdir)
-        self.path = path
-        clean_old_image(path)
-        if not os.path.isdir(os.path.dirname(path)):
-            os.makedirs(os.path.dirname(path))
-
-
-    @error.context_aware
-    def close(self):
-        error.context("Creating unattended install CD image %s" % self.path)
-        g_cmd = ('mkisofs -o %s -max-iso9660-filenames '
-                 '-relaxed-filenames -D --input-charset iso8859-1 '
-                 '%s' % (self.path, self.mount))
-        utils.run(g_cmd)
-
-        os.chmod(self.path, 0755)
-        cleanup(self.mount)
-        logging.debug("unattended install CD image %s successfuly created",
-                      self.path)
-
-
-class UnattendedInstallConfig(object):
-    """
-    Creates a floppy disk image that will contain a config file for unattended
-    OS install. The parameters to the script are retrieved from environment
-    variables.
-    """
-    def __init__(self, test, params):
-        """
-        Sets class atributes from test parameters.
-
-        @param test: KVM test object.
-        @param params: Dictionary with test parameters.
-        """
-        root_dir = test.bindir
-        self.deps_dir = os.path.join(test.virtdir, 'deps')
-        self.unattended_dir = os.path.join(test.virtdir, 'unattended')
-
-        attributes = ['kernel_args', 'finish_program', 'cdrom_cd1',
-                      'unattended_file', 'medium', 'url', 'kernel', 'initrd',
-                      'nfs_server', 'nfs_dir', 'install_virtio', 'floppy',
-                      'cdrom_unattended', 'boot_path', 'extra_params',
-                      'qemu_img_binary', 'cdkey', 'finish_program']
-
-        for a in attributes:
-            setattr(self, a, params.get(a, ''))
-
-        if self.install_virtio == 'yes':
-            v_attributes = ['virtio_floppy', 'virtio_storage_path',
-                            'virtio_network_path', 'virtio_oemsetup_id',
-                            'virtio_network_installer']
-            for va in v_attributes:
-                setattr(self, va, params.get(va, ''))
-
-        self.tmpdir = test.tmpdir
-
-        if getattr(self, 'unattended_file'):
-            self.unattended_file = os.path.join(test.virtdir, self.unattended_file)
-
-        if getattr(self, 'finish_program'):
-            self.finish_program = os.path.join(test.virtdir, self.finish_program)
-
-        if getattr(self, 'qemu_img_binary'):
-            if not os.path.isfile(getattr(self, 'qemu_img_binary')):
-                self.qemu_img_binary = os.path.join(root_dir,
-                                                    self.qemu_img_binary)
-
-        if getattr(self, 'cdrom_cd1'):
-            self.cdrom_cd1 = os.path.join(root_dir, self.cdrom_cd1)
-        self.cdrom_cd1_mount = tempfile.mkdtemp(prefix='cdrom_cd1_',
-                                                dir=self.tmpdir)
-        if self.medium == 'nfs':
-            self.nfs_mount = tempfile.mkdtemp(prefix='nfs_',
-                                              dir=self.tmpdir)
-
-        if getattr(self, 'floppy'):
-            self.floppy = os.path.join(root_dir, self.floppy)
-            if not os.path.isdir(os.path.dirname(self.floppy)):
-                os.makedirs(os.path.dirname(self.floppy))
-
-        self.image_path = os.path.dirname(self.kernel)
-
-
-    def answer_kickstart(self, answer_path):
-        """
-        Replace KVM_TEST_CDKEY (in the unattended file) with the cdkey
-        provided for this test and replace the KVM_TEST_MEDIUM with
-        the tree url or nfs address provided for this test.
-
-        @return: Answer file contents
-        """
-        contents = open(self.unattended_file).read()
-
-        dummy_cdkey_re = r'\bKVM_TEST_CDKEY\b'
-        if re.search(dummy_cdkey_re, contents):
-            if self.cdkey:
-                contents = re.sub(dummy_cdkey_re, self.cdkey, contents)
-
-        dummy_medium_re = r'\bKVM_TEST_MEDIUM\b'
-        if self.medium == "cdrom":
-            content = "cdrom"
-        elif self.medium == "url":
-            content = "url --url %s" % self.url
-        elif self.medium == "nfs":
-            content = "nfs --server=%s --dir=%s" % (self.nfs_server,
-                                                    self.nfs_dir)
-        else:
-            raise ValueError("Unexpected installation medium %s" % self.url)
-
-        contents = re.sub(dummy_medium_re, content, contents)
-
-        logging.debug("Unattended install contents:")
-        for line in contents.splitlines():
-            logging.debug(line)
-
-        utils.open_write_close(answer_path, contents)
-
-
-    def answer_windows_ini(self, answer_path):
-        parser = ConfigParser.ConfigParser()
-        parser.read(self.unattended_file)
-        # First, replacing the CDKEY
-        if self.cdkey:
-            parser.set('UserData', 'ProductKey', self.cdkey)
-        else:
-            logging.error("Param 'cdkey' required but not specified for "
-                          "this unattended installation")
-
-        # Now, replacing the virtio network driver path, under double quotes
-        if self.install_virtio == 'yes':
-            parser.set('Unattended', 'OemPnPDriversPath',
-                       '"%s"' % self.virtio_nework_path)
-        else:
-            parser.remove_option('Unattended', 'OemPnPDriversPath')
-
-        # Last, replace the virtio installer command
-        if self.install_virtio == 'yes':
-            driver = self.virtio_network_installer_path
-        else:
-            driver = 'dir'
-
-        dummy_re = 'KVM_TEST_VIRTIO_NETWORK_INSTALLER'
-        installer = parser.get('GuiRunOnce', 'Command0')
-        if dummy_re in installer:
-            installer = re.sub(dummy_re, driver, installer)
-        parser.set('GuiRunOnce', 'Command0', installer)
-
-        # Now, writing the in memory config state to the unattended file
-        fp = open(answer_path, 'w')
-        parser.write(fp)
-
-        # Let's read it so we can debug print the contents
-        fp = open(answer_path, 'r')
-        contents = fp.read()
-        logging.debug("Unattended install contents:")
-        for line in contents.splitlines():
-            logging.debug(line)
-        fp.close()
-
-
-    def answer_windows_xml(self, answer_path):
-        doc = xml.dom.minidom.parse(self.unattended_file)
-
-        if self.cdkey:
-            # First, replacing the CDKEY
-            product_key = doc.getElementsByTagName('ProductKey')[0]
-            key = product_key.getElementsByTagName('Key')[0]
-            key_text = key.childNodes[0]
-            assert key_text.nodeType == doc.TEXT_NODE
-            key_text.data = self.cdkey
-        else:
-            logging.error("Param 'cdkey' required but not specified for "
-                          "this unattended installation")
-
-        # Now, replacing the virtio driver paths or removing the entire
-        # component PnpCustomizationsWinPE Element Node
-        if self.install_virtio == 'yes':
-            paths = doc.getElementsByTagName("Path")
-            values = [self.virtio_storage_path, self.virtio_network_path]
-            for path, value in zip(paths, values):
-                path_text = path.childNodes[0]
-                assert key_text.nodeType == doc.TEXT_NODE
-                path_text.data = value
-        else:
-            settings = doc.getElementsByTagName("settings")
-            for s in settings:
-                for c in s.getElementsByTagName("component"):
-                    if (c.getAttribute('name') ==
-                        "Microsoft-Windows-PnpCustomizationsWinPE"):
-                        s.removeChild(c)
-
-        # Last but not least important, replacing the virtio installer command
-        command_lines = doc.getElementsByTagName("CommandLine")
-        for command_line in command_lines:
-            command_line_text = command_line.childNodes[0]
-            assert command_line_text.nodeType == doc.TEXT_NODE
-            dummy_re = 'KVM_TEST_VIRTIO_NETWORK_INSTALLER'
-            if (self.install_virtio == 'yes' and
-                hasattr(self, 'virtio_network_installer_path')):
-                driver = self.virtio_network_installer_path
-            else:
-                driver = 'dir'
-            if driver.endswith("msi"):
-                driver = 'msiexec /passive /package ' + driver
-            if dummy_re in command_line_text.data:
-                t = command_line_text.data
-                t = re.sub(dummy_re, driver, t)
-                command_line_text.data = t
-
-        contents = doc.toxml()
-        logging.debug("Unattended install contents:")
-        for line in contents.splitlines():
-            logging.debug(line)
-
-        fp = open(answer_path, 'w')
-        doc.writexml(fp)
-
-
-    def answer_suse_xml(self, answer_path):
-        # There's nothing to replace on SUSE files to date. Yay!
-        doc = xml.dom.minidom.parse(self.unattended_file)
-
-        contents = doc.toxml()
-        logging.debug("Unattended install contents:")
-        for line in contents.splitlines():
-            logging.debug(line)
-
-        fp = open(answer_path, 'w')
-        doc.writexml(fp)
-
-
-    def setup_boot_disk(self):
-        if self.unattended_file.endswith('.sif'):
-            dest_fname = 'winnt.sif'
-            setup_file = 'winnt.bat'
-            boot_disk = FloppyDisk(self.floppy, self.qemu_img_binary,
-                                   self.tmpdir)
-            answer_path = boot_disk.get_answer_file_path(dest_fname)
-            self.answer_windows_ini(answer_path)
-            setup_file_path = os.path.join(self.unattended_dir, setup_file)
-            boot_disk.copy_to(setup_file_path)
-            if self.install_virtio == "yes":
-                boot_disk.setup_virtio_win2003(self.virtio_floppy,
-                                               self.virtio_oemsetup_id)
-            boot_disk.copy_to(self.finish_program)
-
-        elif self.unattended_file.endswith('.ks'):
-            # Red Hat kickstart install
-            dest_fname = 'ks.cfg'
-            if self.cdrom_unattended:
-                boot_disk = CdromDisk(self.cdrom_unattended, self.tmpdir)
-            elif self.floppy:
-                boot_disk = FloppyDisk(self.floppy, self.qemu_img_binary,
-                                       self.tmpdir)
-            else:
-                raise ValueError("Neither cdrom_unattended nor floppy set "
-                                 "on the config file, please verify")
-            answer_path = boot_disk.get_answer_file_path(dest_fname)
-            self.answer_kickstart(answer_path)
-
-        elif self.unattended_file.endswith('.xml'):
-            if "autoyast" in self.extra_params:
-                # SUSE autoyast install
-                dest_fname = "autoinst.xml"
-                if self.cdrom_unattended:
-                    boot_disk = CdromDisk(self.cdrom_unattended, self.tmpdir)
-                elif self.floppy:
-                    boot_disk = FloppyDisk(self.floppy, self.qemu_img_binary,
-                                           self.tmpdir)
-                else:
-                    raise ValueError("Neither cdrom_unattended nor floppy set "
-                                     "on the config file, please verify")
-                answer_path = boot_disk.get_answer_file_path(dest_fname)
-                self.answer_suse_xml(answer_path)
-
-            else:
-                # Windows unattended install
-                dest_fname = "autounattend.xml"
-                boot_disk = FloppyDisk(self.floppy, self.qemu_img_binary,
-                                       self.tmpdir)
-                answer_path = boot_disk.get_answer_file_path(dest_fname)
-                self.answer_windows_xml(answer_path)
-
-                if self.install_virtio == "yes":
-                    boot_disk.setup_virtio_win2008(self.virtio_floppy)
-                boot_disk.copy_to(self.finish_program)
-
-        else:
-            raise ValueError('Unknown answer file type: %s' %
-                             self.unattended_file)
-
-        boot_disk.close()
-
-
-    @error.context_aware
-    def setup_cdrom(self):
-        """
-        Mount cdrom and copy vmlinuz and initrd.img.
-        """
-        error.context("Copying vmlinuz and initrd.img from install cdrom %s" %
-                      self.cdrom_cd1)
-        m_cmd = ('mount -t iso9660 -v -o loop,ro %s %s' %
-                 (self.cdrom_cd1, self.cdrom_cd1_mount))
-        utils.run(m_cmd)
-
-        try:
-            if not os.path.isdir(self.image_path):
-                os.makedirs(self.image_path)
-            kernel_fetch_cmd = ("cp %s/%s/%s %s" %
-                                (self.cdrom_cd1_mount, self.boot_path,
-                                 os.path.basename(self.kernel), self.kernel))
-            utils.run(kernel_fetch_cmd)
-            initrd_fetch_cmd = ("cp %s/%s/%s %s" %
-                                (self.cdrom_cd1_mount, self.boot_path,
-                                 os.path.basename(self.initrd), self.initrd))
-            utils.run(initrd_fetch_cmd)
-        finally:
-            cleanup(self.cdrom_cd1_mount)
-
-
-    @error.context_aware
-    def setup_url(self):
-        """
-        Download the vmlinuz and initrd.img from URL.
-        """
-        error.context("downloading vmlinuz and initrd.img from %s" % self.url)
-        os.chdir(self.image_path)
-        kernel_fetch_cmd = "wget -q %s/%s/%s" % (self.url, self.boot_path,
-                                                 os.path.basename(self.kernel))
-        initrd_fetch_cmd = "wget -q %s/%s/%s" % (self.url, self.boot_path,
-                                                 os.path.basename(self.initrd))
-
-        if os.path.exists(self.kernel):
-            os.remove(self.kernel)
-        if os.path.exists(self.initrd):
-            os.remove(self.initrd)
-
-        utils.run(kernel_fetch_cmd)
-        utils.run(initrd_fetch_cmd)
-
-
-    def setup_nfs(self):
-        """
-        Copy the vmlinuz and initrd.img from nfs.
-        """
-        error.context("copying the vmlinuz and initrd.img from NFS share")
-
-        m_cmd = ("mount %s:%s %s -o ro" %
-                 (self.nfs_server, self.nfs_dir, self.nfs_mount))
-        utils.run(m_cmd)
-
-        try:
-            kernel_fetch_cmd = ("cp %s/%s/%s %s" %
-                                (self.nfs_mount, self.boot_path,
-                                os.path.basename(self.kernel), self.image_path))
-            utils.run(kernel_fetch_cmd)
-            initrd_fetch_cmd = ("cp %s/%s/%s %s" %
-                                (self.nfs_mount, self.boot_path,
-                                os.path.basename(self.initrd), self.image_path))
-            utils.run(initrd_fetch_cmd)
-        finally:
-            cleanup(self.nfs_mount)
-
-
-    def setup(self):
-        """
-        Configure the environment for unattended install.
-
-        Uses an appropriate strategy according to each install model.
-        """
-        logging.info("Starting unattended install setup")
-        virt_utils.display_attributes(self)
-
-        if self.unattended_file and (self.floppy or self.cdrom_unattended):
-            self.setup_boot_disk()
-        if self.medium == "cdrom":
-            if self.kernel and self.initrd:
-                self.setup_cdrom()
-        elif self.medium == "url":
-            self.setup_url()
-        elif self.medium == "nfs":
-            self.setup_nfs()
-        else:
-            raise ValueError("Unexpected installation method %s" %
-                             self.medium)
-
-
-@error.context_aware
-def run_unattended_install(test, params, env):
-    """
-    Unattended install test:
-    1) Starts a VM with an appropriated setup to start an unattended OS install.
-    2) Wait until the install reports to the install watcher its end.
-
-    @param test: KVM test object.
-    @param params: Dictionary with the test parameters.
-    @param env: Dictionary with test environment.
-    """
-    unattended_install_config = UnattendedInstallConfig(test, params)
-    unattended_install_config.setup()
-    vm = env.get_vm(params["main_vm"])
-    vm.create()
-
-    install_timeout = int(params.get("timeout", 3000))
-    port = vm.get_port(int(params.get("guest_port_unattended_install")))
-
-    migrate_background = params.get("migrate_background") == "yes"
-    if migrate_background:
-        mig_timeout = float(params.get("mig_timeout", "3600"))
-        mig_protocol = params.get("migration_protocol", "tcp")
-
-    logging.info("Waiting for installation to finish. Timeout set to %d s "
-                 "(%d min)", install_timeout, install_timeout/60)
-    error.context("waiting for installation to finish")
-
-    start_time = time.time()
-    while (time.time() - start_time) < install_timeout:
-        try:
-            vm.verify_alive()
-        except virt_vm.VMDeadError, e:
-            if params.get("wait_no_ack", "no") == "yes":
-                break
-            else:
-                raise e
-        vm.verify_kernel_crash()
-        if params.get("wait_no_ack", "no") == "no":
-            client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-            try:
-                client.connect((vm.get_address(), port))
-                if client.recv(1024) == "done":
-                    break
-            except (socket.error, virt_vm.VMAddressError):
-                pass
-
-        if migrate_background:
-            vm.migrate(timeout=mig_timeout, protocol=mig_protocol)
-        else:
-            time.sleep(1)
-        if params.get("wait_no_ack", "no") == "no":
-            client.close()
-    else:
-        raise error.TestFail("Timeout elapsed while waiting for install to "
-                             "finish")
-
-    time_elapsed = time.time() - start_time
-    logging.info("Guest reported successful installation after %d s (%d min)",
-                 time_elapsed, time_elapsed/60)
-
-    if params.get("shutdown_cleanly", "yes") == "yes":
-        shutdown_cleanly_timeout = int(params.get("shutdown_cleanly_timeout",
-                                                  120))
-        logging.info("Wait for guest to shutdown cleanly")
-        if virt_utils.wait_for(vm.is_dead, shutdown_cleanly_timeout, 1, 1):
-            logging.info("Guest managed to shutdown cleanly")
diff --git a/client/virt/tests/unattended_install.py b/client/virt/tests/unattended_install.py
new file mode 100644
index 0000000..efa3c10
--- /dev/null
+++ b/client/virt/tests/unattended_install.py
@@ -0,0 +1,632 @@
+import logging, time, socket, re, os, shutil, tempfile, glob, ConfigParser
+import xml.dom.minidom
+from autotest_lib.client.common_lib import error
+from autotest_lib.client.bin import utils
+from autotest_lib.client.virt import virt_vm, virt_utils
+
+
+@error.context_aware
+def cleanup(dir):
+    """
+    If dir is a mountpoint, do what is possible to unmount it. Afterwards,
+    try to remove it.
+
+    @param dir: Directory to be cleaned up.
+    """
+    error.context("cleaning up unattended install directory %s" % dir)
+    if os.path.ismount(dir):
+        utils.run('fuser -k %s' % dir, ignore_status=True)
+        utils.run('umount %s' % dir)
+    if os.path.isdir(dir):
+        shutil.rmtree(dir)
+
+
+@error.context_aware
+def clean_old_image(image):
+    """
+    Clean a leftover image file from previous processes. If it contains a
+    mounted file system, do the proper cleanup procedures.
+
+    @param image: Path to image to be cleaned up.
+    """
+    error.context("cleaning up old leftover image %s" % image)
+    if os.path.exists(image):
+        mtab = open('/etc/mtab', 'r')
+        mtab_contents = mtab.read()
+        mtab.close()
+        if image in mtab_contents:
+            utils.run('fuser -k %s' % image, ignore_status=True)
+            utils.run('umount %s' % image)
+        os.remove(image)
+
+
+class Disk(object):
+    """
+    Abstract class for Disk objects, with the common methods implemented.
+    """
+    def __init__(self):
+        self.path = None
+
+
+    def get_answer_file_path(self, filename):
+        return os.path.join(self.mount, filename)
+
+
+    def copy_to(self, src):
+        logging.debug("Copying %s to disk image mount", src)
+        dst = os.path.join(self.mount, os.path.basename(src))
+        if os.path.isdir(src):
+            shutil.copytree(src, dst)
+        elif os.path.isfile(src):
+            shutil.copyfile(src, dst)
+
+
+    def close(self):
+        os.chmod(self.path, 0755)
+        cleanup(self.mount)
+        logging.debug("Disk %s successfuly set", self.path)
+
+
+class FloppyDisk(Disk):
+    """
+    Represents a 1.44 MB floppy disk. We can copy files to it, and setup it in
+    convenient ways.
+    """
+    @error.context_aware
+    def __init__(self, path, qemu_img_binary, tmpdir):
+        error.context("Creating unattended install floppy image %s" % path)
+        self.tmpdir = tmpdir
+        self.mount = tempfile.mkdtemp(prefix='floppy_', dir=self.tmpdir)
+        self.virtio_mount = None
+        self.path = path
+        clean_old_image(path)
+        if not os.path.isdir(os.path.dirname(path)):
+            os.makedirs(os.path.dirname(path))
+
+        try:
+            c_cmd = '%s create -f raw %s 1440k' % (qemu_img_binary, path)
+            utils.run(c_cmd)
+            f_cmd = 'mkfs.msdos -s 1 %s' % path
+            utils.run(f_cmd)
+            m_cmd = 'mount -o loop,rw %s %s' % (path, self.mount)
+            utils.run(m_cmd)
+        except error.CmdError, e:
+            logging.error("Error during floppy initialization: %s" % e)
+            cleanup(self.mount)
+            raise
+
+
+    def _copy_virtio_drivers(self, virtio_floppy):
+        """
+        Copy the virtio drivers on the virtio floppy to the install floppy.
+
+        1) Mount the floppy containing the viostor drivers
+        2) Copy its contents to the root of the install floppy
+        """
+        virtio_mount = tempfile.mkdtemp(prefix='virtio_floppy_',
+                                        dir=self.tmpdir)
+
+        pwd = os.getcwd()
+        try:
+            m_cmd = 'mount -o loop,ro %s %s' % (virtio_floppy, virtio_mount)
+            utils.run(m_cmd)
+            os.chdir(virtio_mount)
+            path_list = glob.glob('*')
+            for path in path_list:
+                self.copy_to(path)
+        finally:
+            os.chdir(pwd)
+            cleanup(virtio_mount)
+
+
+    def setup_virtio_win2003(self, virtio_floppy, virtio_oemsetup_id):
+        """
+        Setup the install floppy with the virtio storage drivers, win2003 style.
+
+        Win2003 and WinXP depend on the file txtsetup.oem file to install
+        the virtio drivers from the floppy, which is a .ini file.
+        Process:
+
+        1) Copy the virtio drivers on the virtio floppy to the install floppy
+        2) Parse the ini file with config parser
+        3) Modify the identifier of the default session that is going to be
+           executed on the config parser object
+        4) Re-write the config file to the disk
+        """
+        self._copy_virtio_drivers(virtio_floppy)
+        txtsetup_oem = os.path.join(self.mount, 'txtsetup.oem')
+        if not os.path.isfile(txtsetup_oem):
+            raise IOError('File txtsetup.oem not found on the install '
+                          'floppy. Please verify if your floppy virtio '
+                          'driver image has this file')
+        parser = ConfigParser.ConfigParser()
+        parser.read(txtsetup_oem)
+        if not parser.has_section('Defaults'):
+            raise ValueError('File txtsetup.oem does not have the session '
+                             '"Defaults". Please check txtsetup.oem')
+        default_driver = parser.get('Defaults', 'SCSI')
+        if default_driver != virtio_oemsetup_id:
+            parser.set('Defaults', 'SCSI', virtio_oemsetup_id)
+            fp = open(txtsetup_oem, 'w')
+            parser.write(fp)
+            fp.close()
+
+
+    def setup_virtio_win2008(self, virtio_floppy):
+        """
+        Setup the install floppy with the virtio storage drivers, win2008 style.
+
+        Win2008, Vista and 7 require people to point out the path to the drivers
+        on the unattended file, so we just need to copy the drivers to the
+        driver floppy disk. Important to note that it's possible to specify
+        drivers from a CDROM, so the floppy driver copy is optional.
+        Process:
+
+        1) Copy the virtio drivers on the virtio floppy to the install floppy,
+           if there is one available
+        """
+        if os.path.isfile(virtio_floppy):
+            self._copy_virtio_drivers(virtio_floppy)
+        else:
+            logging.debug("No virtio floppy present, not needed for this OS anyway")
+
+
+class CdromDisk(Disk):
+    """
+    Represents a CDROM disk that we can master according to our needs.
+    """
+    def __init__(self, path, tmpdir):
+        self.mount = tempfile.mkdtemp(prefix='cdrom_unattended_', dir=tmpdir)
+        self.path = path
+        clean_old_image(path)
+        if not os.path.isdir(os.path.dirname(path)):
+            os.makedirs(os.path.dirname(path))
+
+
+    @error.context_aware
+    def close(self):
+        error.context("Creating unattended install CD image %s" % self.path)
+        g_cmd = ('mkisofs -o %s -max-iso9660-filenames '
+                 '-relaxed-filenames -D --input-charset iso8859-1 '
+                 '%s' % (self.path, self.mount))
+        utils.run(g_cmd)
+
+        os.chmod(self.path, 0755)
+        cleanup(self.mount)
+        logging.debug("unattended install CD image %s successfuly created",
+                      self.path)
+
+
+class UnattendedInstallConfig(object):
+    """
+    Creates a floppy disk image that will contain a config file for unattended
+    OS install. The parameters to the script are retrieved from environment
+    variables.
+    """
+    def __init__(self, test, params):
+        """
+        Sets class atributes from test parameters.
+
+        @param test: KVM test object.
+        @param params: Dictionary with test parameters.
+        """
+        root_dir = test.bindir
+        self.deps_dir = os.path.join(test.virtdir, 'deps')
+        self.unattended_dir = os.path.join(test.virtdir, 'unattended')
+
+        attributes = ['kernel_args', 'finish_program', 'cdrom_cd1',
+                      'unattended_file', 'medium', 'url', 'kernel', 'initrd',
+                      'nfs_server', 'nfs_dir', 'install_virtio', 'floppy',
+                      'cdrom_unattended', 'boot_path', 'extra_params',
+                      'qemu_img_binary', 'cdkey', 'finish_program']
+
+        for a in attributes:
+            setattr(self, a, params.get(a, ''))
+
+        if self.install_virtio == 'yes':
+            v_attributes = ['virtio_floppy', 'virtio_storage_path',
+                            'virtio_network_path', 'virtio_oemsetup_id',
+                            'virtio_network_installer']
+            for va in v_attributes:
+                setattr(self, va, params.get(va, ''))
+
+        self.tmpdir = test.tmpdir
+
+        if getattr(self, 'unattended_file'):
+            self.unattended_file = os.path.join(test.virtdir, self.unattended_file)
+
+        if getattr(self, 'finish_program'):
+            self.finish_program = os.path.join(test.virtdir, self.finish_program)
+
+        if getattr(self, 'qemu_img_binary'):
+            if not os.path.isfile(getattr(self, 'qemu_img_binary')):
+                self.qemu_img_binary = os.path.join(root_dir,
+                                                    self.qemu_img_binary)
+
+        if getattr(self, 'cdrom_cd1'):
+            self.cdrom_cd1 = os.path.join(root_dir, self.cdrom_cd1)
+        self.cdrom_cd1_mount = tempfile.mkdtemp(prefix='cdrom_cd1_',
+                                                dir=self.tmpdir)
+        if self.medium == 'nfs':
+            self.nfs_mount = tempfile.mkdtemp(prefix='nfs_',
+                                              dir=self.tmpdir)
+
+        if getattr(self, 'floppy'):
+            self.floppy = os.path.join(root_dir, self.floppy)
+            if not os.path.isdir(os.path.dirname(self.floppy)):
+                os.makedirs(os.path.dirname(self.floppy))
+
+        self.image_path = os.path.dirname(self.kernel)
+
+
+    def answer_kickstart(self, answer_path):
+        """
+        Replace KVM_TEST_CDKEY (in the unattended file) with the cdkey
+        provided for this test and replace the KVM_TEST_MEDIUM with
+        the tree url or nfs address provided for this test.
+
+        @return: Answer file contents
+        """
+        contents = open(self.unattended_file).read()
+
+        dummy_cdkey_re = r'\bKVM_TEST_CDKEY\b'
+        if re.search(dummy_cdkey_re, contents):
+            if self.cdkey:
+                contents = re.sub(dummy_cdkey_re, self.cdkey, contents)
+
+        dummy_medium_re = r'\bKVM_TEST_MEDIUM\b'
+        if self.medium in ["cdrom", "kernel_initrd"]:
+            content = "cdrom"
+        elif self.medium == "url":
+            content = "url --url %s" % self.url
+        elif self.medium == "nfs":
+            content = "nfs --server=%s --dir=%s" % (self.nfs_server,
+                                                    self.nfs_dir)
+        else:
+            raise ValueError("Unexpected installation medium %s" % self.url)
+
+        contents = re.sub(dummy_medium_re, content, contents)
+
+        logging.debug("Unattended install contents:")
+        for line in contents.splitlines():
+            logging.debug(line)
+
+        utils.open_write_close(answer_path, contents)
+
+
+    def answer_windows_ini(self, answer_path):
+        parser = ConfigParser.ConfigParser()
+        parser.read(self.unattended_file)
+        # First, replacing the CDKEY
+        if self.cdkey:
+            parser.set('UserData', 'ProductKey', self.cdkey)
+        else:
+            logging.error("Param 'cdkey' required but not specified for "
+                          "this unattended installation")
+
+        # Now, replacing the virtio network driver path, under double quotes
+        if self.install_virtio == 'yes':
+            parser.set('Unattended', 'OemPnPDriversPath',
+                       '"%s"' % self.virtio_nework_path)
+        else:
+            parser.remove_option('Unattended', 'OemPnPDriversPath')
+
+        # Last, replace the virtio installer command
+        if self.install_virtio == 'yes':
+            driver = self.virtio_network_installer_path
+        else:
+            driver = 'dir'
+
+        dummy_re = 'KVM_TEST_VIRTIO_NETWORK_INSTALLER'
+        installer = parser.get('GuiRunOnce', 'Command0')
+        if dummy_re in installer:
+            installer = re.sub(dummy_re, driver, installer)
+        parser.set('GuiRunOnce', 'Command0', installer)
+
+        # Now, writing the in memory config state to the unattended file
+        fp = open(answer_path, 'w')
+        parser.write(fp)
+
+        # Let's read it so we can debug print the contents
+        fp = open(answer_path, 'r')
+        contents = fp.read()
+        logging.debug("Unattended install contents:")
+        for line in contents.splitlines():
+            logging.debug(line)
+        fp.close()
+
+
+    def answer_windows_xml(self, answer_path):
+        doc = xml.dom.minidom.parse(self.unattended_file)
+
+        if self.cdkey:
+            # First, replacing the CDKEY
+            product_key = doc.getElementsByTagName('ProductKey')[0]
+            key = product_key.getElementsByTagName('Key')[0]
+            key_text = key.childNodes[0]
+            assert key_text.nodeType == doc.TEXT_NODE
+            key_text.data = self.cdkey
+        else:
+            logging.error("Param 'cdkey' required but not specified for "
+                          "this unattended installation")
+
+        # Now, replacing the virtio driver paths or removing the entire
+        # component PnpCustomizationsWinPE Element Node
+        if self.install_virtio == 'yes':
+            paths = doc.getElementsByTagName("Path")
+            values = [self.virtio_storage_path, self.virtio_network_path]
+            for path, value in zip(paths, values):
+                path_text = path.childNodes[0]
+                assert key_text.nodeType == doc.TEXT_NODE
+                path_text.data = value
+        else:
+            settings = doc.getElementsByTagName("settings")
+            for s in settings:
+                for c in s.getElementsByTagName("component"):
+                    if (c.getAttribute('name') ==
+                        "Microsoft-Windows-PnpCustomizationsWinPE"):
+                        s.removeChild(c)
+
+        # Last but not least important, replacing the virtio installer command
+        command_lines = doc.getElementsByTagName("CommandLine")
+        for command_line in command_lines:
+            command_line_text = command_line.childNodes[0]
+            assert command_line_text.nodeType == doc.TEXT_NODE
+            dummy_re = 'KVM_TEST_VIRTIO_NETWORK_INSTALLER'
+            if (self.install_virtio == 'yes' and
+                hasattr(self, 'virtio_network_installer_path')):
+                driver = self.virtio_network_installer_path
+            else:
+                driver = 'dir'
+            if driver.endswith("msi"):
+                driver = 'msiexec /passive /package ' + driver
+            if dummy_re in command_line_text.data:
+                t = command_line_text.data
+                t = re.sub(dummy_re, driver, t)
+                command_line_text.data = t
+
+        contents = doc.toxml()
+        logging.debug("Unattended install contents:")
+        for line in contents.splitlines():
+            logging.debug(line)
+
+        fp = open(answer_path, 'w')
+        doc.writexml(fp)
+
+
+    def answer_suse_xml(self, answer_path):
+        # There's nothing to replace on SUSE files to date. Yay!
+        doc = xml.dom.minidom.parse(self.unattended_file)
+
+        contents = doc.toxml()
+        logging.debug("Unattended install contents:")
+        for line in contents.splitlines():
+            logging.debug(line)
+
+        fp = open(answer_path, 'w')
+        doc.writexml(fp)
+
+
+    def setup_boot_disk(self):
+        if self.unattended_file.endswith('.sif'):
+            dest_fname = 'winnt.sif'
+            setup_file = 'winnt.bat'
+            boot_disk = FloppyDisk(self.floppy, self.qemu_img_binary,
+                                   self.tmpdir)
+            answer_path = boot_disk.get_answer_file_path(dest_fname)
+            self.answer_windows_ini(answer_path)
+            setup_file_path = os.path.join(self.unattended_dir, setup_file)
+            boot_disk.copy_to(setup_file_path)
+            if self.install_virtio == "yes":
+                boot_disk.setup_virtio_win2003(self.virtio_floppy,
+                                               self.virtio_oemsetup_id)
+            boot_disk.copy_to(self.finish_program)
+
+        elif self.unattended_file.endswith('.ks'):
+            # Red Hat kickstart install
+            dest_fname = 'ks.cfg'
+            if self.cdrom_unattended:
+                boot_disk = CdromDisk(self.cdrom_unattended, self.tmpdir)
+            elif self.floppy:
+                boot_disk = FloppyDisk(self.floppy, self.qemu_img_binary,
+                                       self.tmpdir)
+            else:
+                raise ValueError("Neither cdrom_unattended nor floppy set "
+                                 "on the config file, please verify")
+            answer_path = boot_disk.get_answer_file_path(dest_fname)
+            self.answer_kickstart(answer_path)
+
+        elif self.unattended_file.endswith('.xml'):
+            if "autoyast" in self.extra_params:
+                # SUSE autoyast install
+                dest_fname = "autoinst.xml"
+                if self.cdrom_unattended:
+                    boot_disk = CdromDisk(self.cdrom_unattended, self.tmpdir)
+                elif self.floppy:
+                    boot_disk = FloppyDisk(self.floppy, self.qemu_img_binary,
+                                           self.tmpdir)
+                else:
+                    raise ValueError("Neither cdrom_unattended nor floppy set "
+                                     "on the config file, please verify")
+                answer_path = boot_disk.get_answer_file_path(dest_fname)
+                self.answer_suse_xml(answer_path)
+
+            else:
+                # Windows unattended install
+                dest_fname = "autounattend.xml"
+                boot_disk = FloppyDisk(self.floppy, self.qemu_img_binary,
+                                       self.tmpdir)
+                answer_path = boot_disk.get_answer_file_path(dest_fname)
+                self.answer_windows_xml(answer_path)
+
+                if self.install_virtio == "yes":
+                    boot_disk.setup_virtio_win2008(self.virtio_floppy)
+                boot_disk.copy_to(self.finish_program)
+
+        else:
+            raise ValueError('Unknown answer file type: %s' %
+                             self.unattended_file)
+
+        boot_disk.close()
+
+
+    @error.context_aware
+    def setup_cdrom(self):
+        """
+        Mount cdrom and copy vmlinuz and initrd.img.
+        """
+        error.context("Copying vmlinuz and initrd.img from install cdrom %s" %
+                      self.cdrom_cd1)
+        m_cmd = ('mount -t iso9660 -v -o loop,ro %s %s' %
+                 (self.cdrom_cd1, self.cdrom_cd1_mount))
+        utils.run(m_cmd)
+
+        try:
+            if not os.path.isdir(self.image_path):
+                os.makedirs(self.image_path)
+            kernel_fetch_cmd = ("cp %s/%s/%s %s" %
+                                (self.cdrom_cd1_mount, self.boot_path,
+                                 os.path.basename(self.kernel), self.kernel))
+            utils.run(kernel_fetch_cmd)
+            initrd_fetch_cmd = ("cp %s/%s/%s %s" %
+                                (self.cdrom_cd1_mount, self.boot_path,
+                                 os.path.basename(self.initrd), self.initrd))
+            utils.run(initrd_fetch_cmd)
+        finally:
+            cleanup(self.cdrom_cd1_mount)
+
+
+    @error.context_aware
+    def setup_url(self):
+        """
+        Download the vmlinuz and initrd.img from URL.
+        """
+        error.context("downloading vmlinuz and initrd.img from %s" % self.url)
+        os.chdir(self.image_path)
+        kernel_fetch_cmd = "wget -q %s/%s/%s" % (self.url, self.boot_path,
+                                                 os.path.basename(self.kernel))
+        initrd_fetch_cmd = "wget -q %s/%s/%s" % (self.url, self.boot_path,
+                                                 os.path.basename(self.initrd))
+
+        if os.path.exists(self.kernel):
+            os.remove(self.kernel)
+        if os.path.exists(self.initrd):
+            os.remove(self.initrd)
+
+        utils.run(kernel_fetch_cmd)
+        utils.run(initrd_fetch_cmd)
+
+
+    def setup_nfs(self):
+        """
+        Copy the vmlinuz and initrd.img from nfs.
+        """
+        error.context("copying the vmlinuz and initrd.img from NFS share")
+
+        m_cmd = ("mount %s:%s %s -o ro" %
+                 (self.nfs_server, self.nfs_dir, self.nfs_mount))
+        utils.run(m_cmd)
+
+        try:
+            kernel_fetch_cmd = ("cp %s/%s/%s %s" %
+                                (self.nfs_mount, self.boot_path,
+                                os.path.basename(self.kernel), self.image_path))
+            utils.run(kernel_fetch_cmd)
+            initrd_fetch_cmd = ("cp %s/%s/%s %s" %
+                                (self.nfs_mount, self.boot_path,
+                                os.path.basename(self.initrd), self.image_path))
+            utils.run(initrd_fetch_cmd)
+        finally:
+            cleanup(self.nfs_mount)
+
+
+    def setup(self):
+        """
+        Configure the environment for unattended install.
+
+        Uses an appropriate strategy according to each install model.
+        """
+        logging.info("Starting unattended install setup")
+        virt_utils.display_attributes(self)
+
+        if self.unattended_file and (self.floppy or self.cdrom_unattended):
+            self.setup_boot_disk()
+        if self.medium in ["cdrom", "kernel_initrd"]:
+            if self.kernel and self.initrd:
+                self.setup_cdrom()
+        elif self.medium == "url":
+            self.setup_url()
+        elif self.medium == "nfs":
+            self.setup_nfs()
+        else:
+            raise ValueError("Unexpected installation method %s" %
+                             self.medium)
+
+
+@error.context_aware
+def run_unattended_install(test, params, env):
+    """
+    Unattended install test:
+    1) Starts a VM with an appropriated setup to start an unattended OS install.
+    2) Wait until the install reports to the install watcher its end.
+
+    @param test: KVM test object.
+    @param params: Dictionary with the test parameters.
+    @param env: Dictionary with test environment.
+    """
+    unattended_install_config = UnattendedInstallConfig(test, params)
+    unattended_install_config.setup()
+    vm = env.get_vm(params["main_vm"])
+    vm.create()
+
+    install_timeout = int(params.get("timeout", 3000))
+    port = vm.get_port(int(params.get("guest_port_unattended_install")))
+
+    migrate_background = params.get("migrate_background") == "yes"
+    if migrate_background:
+        mig_timeout = float(params.get("mig_timeout", "3600"))
+        mig_protocol = params.get("migration_protocol", "tcp")
+
+    logging.info("Waiting for installation to finish. Timeout set to %d s "
+                 "(%d min)", install_timeout, install_timeout/60)
+    error.context("waiting for installation to finish")
+
+    start_time = time.time()
+    while (time.time() - start_time) < install_timeout:
+        try:
+            vm.verify_alive()
+        except virt_vm.VMDeadError, e:
+            if params.get("wait_no_ack", "no") == "yes":
+                break
+            else:
+                raise e
+        vm.verify_kernel_crash()
+        if params.get("wait_no_ack", "no") == "no":
+            client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+            try:
+                client.connect((vm.get_address(), port))
+                if client.recv(1024) == "done":
+                    break
+            except (socket.error, virt_vm.VMAddressError):
+                pass
+
+        if migrate_background:
+            vm.migrate(timeout=mig_timeout, protocol=mig_protocol)
+        else:
+            time.sleep(1)
+        if params.get("wait_no_ack", "no") == "no":
+            client.close()
+    else:
+        raise error.TestFail("Timeout elapsed while waiting for install to "
+                             "finish")
+
+    time_elapsed = time.time() - start_time
+    logging.info("Guest reported successful installation after %d s (%d min)",
+                 time_elapsed, time_elapsed/60)
+
+    if params.get("shutdown_cleanly", "yes") == "yes":
+        shutdown_cleanly_timeout = int(params.get("shutdown_cleanly_timeout",
+                                                  120))
+        logging.info("Wait for guest to shutdown cleanly")
+        if virt_utils.wait_for(vm.is_dead, shutdown_cleanly_timeout, 1, 1):
+            logging.info("Guest managed to shutdown cleanly")
-- 
1.7.6.4

--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]
  Powered by Linux