diff mbox

[4/6] KVM test: Add unattended install script

Message ID 1249339134-5600-5-git-send-email-lmr@redhat.com (mailing list archive)
State New, archived
Headers show

Commit Message

Lucas Meneghel Rodrigues Aug. 3, 2009, 10:38 p.m. UTC
In order to make it possible to prepare the environment
for the guests installation, we have to:

 * Prepare a boot floppy for both windows and linux.
The boot floppy contains the unattended file, and in
case of windows, it will also hold a program that
can send to the server a ACK message. For Kickstart,
we just built the client program that sends the
message into the kickstart file.

 * Prepare PXE boot for Linux guests. Will prepare a
location with the pxelinux.0 boot loader + the vmlinuz
and initrd.img images used to do network boot.

As I pointed out, the script is in python due to project
policy. At this point I strongly believe the environment
scripts should be rather libraries that can use full
use of the autotest API, but for a first pass I believe
the script is good enough.

Signed-off-by: Lucas Meneghel Rodrigues <lmr@redhat.com>
---
 client/tests/kvm/scripts/unattended.py |  223 ++++++++++++++++++++++++++++++++
 1 files changed, 223 insertions(+), 0 deletions(-)
 create mode 100755 client/tests/kvm/scripts/unattended.py
diff mbox

Patch

diff --git a/client/tests/kvm/scripts/unattended.py b/client/tests/kvm/scripts/unattended.py
new file mode 100755
index 0000000..5d730d2
--- /dev/null
+++ b/client/tests/kvm/scripts/unattended.py
@@ -0,0 +1,223 @@ 
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+import os, sys, shutil, tempfile, re
+import common
+
+"""
+Simple script to setup unattended installs on KVM guests.
+"""
+
+class SetupError(Exception):
+    """
+    Simple wrapper for the builtin Exception class.
+    """
+    pass
+
+
+class UnattendedInstall:
+    """
+    Creates a floppy disk image that will contain a config file for unattended
+    OS install. Optionally, sets up a PXE install server using qemu built in
+    TFTP and DHCP servers to install a particular operating system. The
+    parameters to the script are retrieved from environment variables.
+    """
+    def __init__(self):
+        """
+        Gets params from environment variables and sets class attributes.
+        """
+        script_dir = os.path.dirname(sys.modules[__name__].__file__)
+        kvm_test_dir = os.path.abspath(os.path.join(script_dir, ".."))
+        images_dir = os.path.join(kvm_test_dir, 'images')
+        isos_dir = os.path.join(kvm_test_dir, 'isos')
+        self.deps_dir = os.path.join(kvm_test_dir, 'deps')
+        self.unattended_dir = os.path.join(kvm_test_dir, 'unattended')
+
+        try:
+            tftp_root = os.environ['KVM_TEST_tftp']
+            self.tftp_root = os.path.join(images_dir, tftp_root)
+            if not os.path.isdir(self.tftp_root):
+                os.makedirs(self.tftp_root)
+        except KeyError:
+            self.tftp_root = ''
+
+        try:
+            self.kernel_args = os.environ['KVM_TEST_kernel_args']
+        except KeyError:
+            self.kernel_args = ''
+
+        try:
+            self.finish_program= os.environ['KVM_TEST_finish_program']
+        except:
+            self.finish_program = None
+
+
+        cdrom_iso = os.environ['KVM_TEST_cdrom']
+        self.unattended_file = os.environ['KVM_TEST_unattended_file']
+
+        self.qemu_img_bin = os.path.join(kvm_test_dir, 'qemu-img')
+        self.cdrom_iso = os.path.join(isos_dir, cdrom_iso)
+        self.floppy_mount = tempfile.mkdtemp(prefix='floppy_', dir='/tmp')
+        self.cdrom_mount = tempfile.mkdtemp(prefix='cdrom_', dir='/tmp')
+        self.floppy_img = os.path.join(images_dir, 'floppy.img')
+
+
+    def create_boot_floppy(self):
+        """
+        Prepares a boot floppy by creating a floppy image file, mounting it and
+        copying an answer file (kickstarts for RH based distros, answer files
+        for windows) to it. After that the image is umounted.
+        """
+        print "Creating boot floppy"
+
+        if self.floppy_img:
+            os.remove(self.floppy_img)
+        #c_cmd = 'bximage -q -fd -size=1.44 %s' % (self.floppy_img)
+        c_cmd = '%s create -f raw %s 1440k' % (self.qemu_img_bin, self.floppy_img)
+        if os.system(c_cmd):
+            raise SetupError('Could not create floppy image.')
+
+        f_cmd = 'mkfs.msdos -s 1 %s' % self.floppy_img
+        if os.system(f_cmd):
+            raise SetupError('Error formatting floppy image.')
+
+        m_cmd = 'mount -o loop %s %s' % (self.floppy_img, self.floppy_mount)
+        if os.system(m_cmd):
+            raise SetupError('Could not mount floppy image.')
+
+        if self.unattended_file.endswith('.sif'):
+            dest_fname = 'winnt.sif'
+            setup_file = 'winnt.bat'
+            setup_file_path = os.path.join(self.unattended_dir, setup_file)
+            setup_file_dest = os.path.join(self.floppy_mount, setup_file)
+            shutil.copyfile(setup_file_path, setup_file_dest)
+        elif self.unattended_file.endswith('.ks'):
+            dest_fname = 'ks.cfg'
+
+        dest = os.path.join(self.floppy_mount, dest_fname)
+        shutil.copyfile(self.unattended_file, dest)
+
+        if self.finish_program:
+            dest_fname = os.path.basename(self.finish_program)
+            dest = os.path.join(self.floppy_mount, dest_fname)
+            shutil.copyfile(self.finish_program, dest)
+
+        u_cmd = 'umount %s' % self.floppy_mount
+        if os.system(u_cmd):
+            raise SetupError('Could not unmount floppy at %s.' %
+                             self.floppy_mount)
+
+        os.chmod(self.floppy_img, 0755)
+
+        print "Boot floppy created successfuly"
+
+
+    def setup_pxe_boot(self):
+        """
+        Sets up a PXE boot environment using the built in qemu TFTP server.
+        Copies the PXE Linux bootloader pxelinux.0 from the host (needs the
+        pxelinux package or equivalent for your distro), and vmlinuz and
+        initrd.img files from the CD to a directory that qemu will serve trough
+        TFTP to the VM.
+        """
+        print "Setting up PXE boot using TFTP root %s" % self.tftp_root
+
+        pxe_file = None
+        pxe_paths = ['/usr/lib/syslinux/pxelinux.0',
+                     '/usr/share/syslinux/pxelinux.0']
+        for path in pxe_paths:
+            if os.path.isfile(path):
+                pxe_file = path
+                break
+
+        if not pxe_file:
+            raise SetupError('Cannot find PXE boot loader pxelinux.0. Make '
+                             'sure pxelinux or equivalent package for your '
+                             'distro is installed.')
+
+        pxe_dest = os.path.join(self.tftp_root, 'pxelinux.0')
+        shutil.copyfile(pxe_file, pxe_dest)
+
+        m_cmd = 'mount -t iso9660 -v -o loop %s %s' % (self.cdrom_iso,
+                                                       self.cdrom_mount)
+        if os.system(m_cmd):
+            raise SetupError('Could not mount CD image %s.' % self.cdrom_iso)
+
+        p = os.path.join('images', 'pxeboot')
+        pxe_dir = os.path.join(self.cdrom_mount, p)
+        pxe_image = os.path.join(pxe_dir, 'vmlinuz')
+        pxe_initrd = os.path.join(pxe_dir, 'initrd.img')
+
+        if not os.path.isdir(pxe_dir):
+            raise SetupError('The ISO image does not have a %s dir. The script '
+                             'assumes that the cd has a %s dir where to search '
+                             'for the vmlinuz image.' % (p, p))
+
+        if not os.path.isfile(pxe_image) or not os.path.isfile(pxe_initrd):
+            raise SetupError('The location %s is lacking either a vmlinuz or a '
+                             'initrd.img file. Cannot find a PXE image to '
+                             'proceed.' % pxe_dir)
+
+        tftp_image = os.path.join(self.tftp_root, 'vmlinuz')
+        tftp_initrd = os.path.join(self.tftp_root, 'initrd.img')
+        shutil.copyfile(pxe_image, tftp_image)
+        shutil.copyfile(pxe_initrd, tftp_initrd)
+
+        u_cmd = 'umount %s' % self.cdrom_mount
+        if os.system(u_cmd):
+            raise SetupError('Could not unmount CD at %s.' % self.cdrom_mount)
+
+        pxe_config_dir = os.path.join(self.tftp_root, 'pxelinux.cfg')
+        if not os.path.isdir(pxe_config_dir):
+            os.makedirs(pxe_config_dir)
+        pxe_config_path = os.path.join(pxe_config_dir, 'default')
+
+        pxe_config = open(pxe_config_path, 'w')
+        pxe_config.write('DEFAULT pxeboot\n')
+        pxe_config.write('TIMEOUT 20\n')
+        pxe_config.write('PROMPT 0\n')
+        pxe_config.write('LABEL pxeboot\n')
+        pxe_config.write('     KERNEL vmlinuz\n')
+        pxe_config.write('     KERNEL vmlinuz\n')
+        pxe_config.write('     APPEND initrd=initrd.img %s\n' %
+                         self.kernel_args)
+        pxe_config.close()
+
+        print "PXE boot successfuly set"
+
+    def cleanup(self):
+        """
+        Clean up previously used mount points.
+        """
+        print "Cleaning up unused mount points"
+        for mount in [self.floppy_mount, self.cdrom_mount]:
+            if os.path.isdir(mount):
+                if os.path.ismount(mount):
+                    print "Path %s is still mounted, please verify" % mount
+                else:
+                    print "Removing mount point %s" % mount
+                    os.rmdir(mount)
+
+
+    def setup(self):
+        print "Starting unattended install setup"
+
+        print "Variables set:"
+        print "    qemu_img_bin: " + str(self.qemu_img_bin)
+        print "    cdrom iso: " + str(self.cdrom_iso)
+        print "    unattended_file: " + str(self.unattended_file)
+        print "    kernel_args: " + str(self.kernel_args)
+        print "    tftp_root: " + str(self.tftp_root)
+        print "    floppy_mount: " + str(self.floppy_mount)
+        print "    floppy_img: " + str(self.floppy_img)
+        print "    finish_program: " + str(self.finish_program)
+
+        self.create_boot_floppy()
+        if self.tftp_root:
+            self.setup_pxe_boot()
+        self.cleanup()
+        print "Unattended install setup finished successfuly"
+
+
+if __name__ == "__main__":
+    os_install = UnattendedInstall()
+    os_install.setup()