From patchwork Mon Aug 3 22:38:52 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lucas Meneghel Rodrigues X-Patchwork-Id: 39026 Received: from vger.kernel.org (vger.kernel.org [209.132.176.167]) by demeter.kernel.org (8.14.2/8.14.2) with ESMTP id n73MdHZV017617 for ; Mon, 3 Aug 2009 22:39:17 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755077AbZHCWjL (ORCPT ); Mon, 3 Aug 2009 18:39:11 -0400 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1753305AbZHCWjL (ORCPT ); Mon, 3 Aug 2009 18:39:11 -0400 Received: from mx2.redhat.com ([66.187.237.31]:60582 "EHLO mx2.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753320AbZHCWjG (ORCPT ); Mon, 3 Aug 2009 18:39:06 -0400 Received: from int-mx2.corp.redhat.com (int-mx2.corp.redhat.com [172.16.27.26]) by mx2.redhat.com (8.13.8/8.13.8) with ESMTP id n73Md7Ok002393; Mon, 3 Aug 2009 18:39:07 -0400 Received: from ns3.rdu.redhat.com (ns3.rdu.redhat.com [10.11.255.199]) by int-mx2.corp.redhat.com (8.13.1/8.13.1) with ESMTP id n73Md67F026955; Mon, 3 Aug 2009 18:39:06 -0400 Received: from localhost.localdomain (vpn-10-57.bos.redhat.com [10.16.10.57]) by ns3.rdu.redhat.com (8.13.8/8.13.8) with ESMTP id n73MctNi025566; Mon, 3 Aug 2009 18:39:04 -0400 From: Lucas Meneghel Rodrigues To: autotest@test.kernel.org Cc: kvm@vger.kernel.org, dhuff@redhat.com, Lucas Meneghel Rodrigues Subject: [PATCH 4/6] KVM test: Add unattended install script Date: Mon, 3 Aug 2009 19:38:52 -0300 Message-Id: <1249339134-5600-5-git-send-email-lmr@redhat.com> In-Reply-To: <1249339134-5600-4-git-send-email-lmr@redhat.com> References: <1249339134-5600-1-git-send-email-lmr@redhat.com> <1249339134-5600-2-git-send-email-lmr@redhat.com> <1249339134-5600-3-git-send-email-lmr@redhat.com> <1249339134-5600-4-git-send-email-lmr@redhat.com> X-Scanned-By: MIMEDefang 2.58 on 172.16.27.26 Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org 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 --- client/tests/kvm/scripts/unattended.py | 223 ++++++++++++++++++++++++++++++++ 1 files changed, 223 insertions(+), 0 deletions(-) create mode 100755 client/tests/kvm/scripts/unattended.py 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()