Merge remote branch 'cros/upstream' into tempbranch2
Merged to trunk@4816.
BUG=
TEST=we will build a new autotest server instance, and keep cautotest running and then later do a cname switch.
Review URL: http://codereview.chromium.org/3511003
Change-Id: Iee5f52f45f28f84927d6c6f9a74edc370d40288a
diff --git a/client/tests/kvm/scripts/unattended.py b/client/tests/kvm/scripts/unattended.py
index a630fbc..ba7d80b 100755
--- a/client/tests/kvm/scripts/unattended.py
+++ b/client/tests/kvm/scripts/unattended.py
@@ -3,10 +3,14 @@
Simple script to setup unattended installs on KVM guests.
"""
# -*- coding: utf-8 -*-
-import os, sys, shutil, tempfile, re
+import os, sys, shutil, tempfile, re, ConfigParser, glob, inspect
import common
+SCRIPT_DIR = os.path.dirname(sys.modules[__name__].__file__)
+KVM_TEST_DIR = os.path.abspath(os.path.join(SCRIPT_DIR, ".."))
+
+
class SetupError(Exception):
"""
Simple wrapper for the builtin Exception class.
@@ -14,6 +18,227 @@
pass
+def find_command(cmd):
+ """
+ Searches for a command on common paths, error if it can't find it.
+
+ @param cmd: Command to be found.
+ """
+ for dir in ["/usr/local/sbin", "/usr/local/bin",
+ "/usr/sbin", "/usr/bin", "/sbin", "/bin"]:
+ file = os.path.join(dir, cmd)
+ if os.path.exists(file):
+ return file
+ raise ValueError('Missing command: %s' % cmd)
+
+
+def run(cmd, info=None):
+ """
+ Run a command and throw an exception if it fails.
+ Optionally, you can provide additional contextual info.
+
+ @param cmd: Command string.
+ @param reason: Optional string that explains the context of the failure.
+
+ @raise: SetupError if command fails.
+ """
+ print "Running '%s'" % cmd
+ cmd_name = cmd.split(' ')[0]
+ find_command(cmd_name)
+ if os.system(cmd):
+ e_msg = 'Command failed: %s' % cmd
+ if info is not None:
+ e_msg += '. %s' % info
+ raise SetupError(e_msg)
+
+
+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.
+ """
+ print "Cleaning up directory %s" % dir
+ if os.path.ismount(dir):
+ os.system('fuser -k %s' % dir)
+ run('umount %s' % dir, info='Could not unmount %s' % dir)
+ if os.path.isdir(dir):
+ shutil.rmtree(dir)
+
+
+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.
+ """
+ if os.path.exists(image):
+ mtab = open('/etc/mtab', 'r')
+ mtab_contents = mtab.read()
+ mtab.close()
+ if image in mtab_contents:
+ os.system('fuser -k %s' % image)
+ os.system('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 setup_answer_file(self, filename, contents):
+ answer_file = open(os.path.join(self.mount, filename), 'w')
+ answer_file.write(contents)
+ answer_file.close()
+
+
+ def copy_to(self, 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)
+ print "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.
+ """
+ def __init__(self, path):
+ print "Creating floppy unattended image %s" % path
+ try:
+ qemu_img_binary = os.environ['KVM_TEST_qemu_img_binary']
+ except KeyError:
+ qemu_img_binary = os.path.join(KVM_TEST_DIR, qemu_img_binary)
+ if not os.path.exists(qemu_img_binary):
+ raise SetupError('The qemu-img binary that is supposed to be used '
+ '(%s) does not exist. Please verify your '
+ 'configuration' % qemu_img_binary)
+
+ self.mount = tempfile.mkdtemp(prefix='floppy_', dir='/tmp')
+ 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)
+ run(c_cmd, info='Could not create floppy image')
+ f_cmd = 'mkfs.msdos -s 1 %s' % path
+ run(f_cmd, info='Error formatting floppy image')
+ m_cmd = 'mount -o loop,rw %s %s' % (path, self.mount)
+ run(m_cmd, info='Could not mount floppy image')
+ except:
+ cleanup(self.mount)
+
+
+ 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='/tmp')
+
+ pwd = os.getcwd()
+ try:
+ m_cmd = 'mount -o loop %s %s' % (virtio_floppy, virtio_mount)
+ run(m_cmd, info='Could not mount virtio floppy driver')
+ 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 SetupError('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 SetupError('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.
+ Process:
+
+ 1) Copy the virtio drivers on the virtio floppy to the install floppy
+ """
+ self._copy_virtio_drivers(virtio_floppy)
+
+
+class CdromDisk(Disk):
+ """
+ Represents a CDROM disk that we can master according to our needs.
+ """
+ def __init__(self, path):
+ print "Creating ISO unattended image %s" % path
+ self.mount = tempfile.mkdtemp(prefix='cdrom_unattended_', dir='/tmp')
+ self.path = path
+ clean_old_image(path)
+ if not os.path.isdir(os.path.dirname(path)):
+ os.makedirs(os.path.dirname(path))
+
+
+ def close(self):
+ g_cmd = ('mkisofs -o %s -max-iso9660-filenames '
+ '-relaxed-filenames -D --input-charset iso8859-1 '
+ '%s' % (self.path, self.mount))
+ run(g_cmd, info='Could not generate iso with answer file')
+
+ os.chmod(self.path, 0755)
+ cleanup(self.mount)
+ print "Disk %s successfuly set" % self.path
+
+
class UnattendedInstall(object):
"""
Creates a floppy disk image that will contain a config file for unattended
@@ -25,144 +250,188 @@
"""
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')
- self.deps_dir = os.path.join(kvm_test_dir, 'deps')
- self.unattended_dir = os.path.join(kvm_test_dir, 'unattended')
+ images_dir = os.path.join(KVM_TEST_DIR, 'images')
+ self.deps_dir = os.path.join(KVM_TEST_DIR, 'deps')
+ self.unattended_dir = os.path.join(KVM_TEST_DIR, 'unattended')
- tftp_root = os.environ.get('KVM_TEST_tftp', '')
- if tftp_root:
- self.tftp_root = os.path.join(kvm_test_dir, tftp_root)
- if not os.path.isdir(self.tftp_root):
- os.makedirs(self.tftp_root)
- else:
- self.tftp_root = tftp_root
+ attributes = ['kernel_args', 'finish_program', 'cdrom_cd1',
+ 'unattended_file', 'medium', 'url', 'kernel', 'initrd',
+ 'nfs_server', 'nfs_dir', 'pxe_dir', 'pxe_image',
+ 'pxe_initrd', 'install_virtio', 'tftp',
+ 'floppy', 'cdrom_unattended']
+ for a in attributes:
+ self._setattr(a)
- self.kernel_args = os.environ.get('KVM_TEST_kernel_args', '')
- self.finish_program= os.environ.get('KVM_TEST_finish_program', '')
- cdrom_iso = os.environ.get('KVM_TEST_cdrom_cd1')
- self.unattended_file = os.environ.get('KVM_TEST_unattended_file')
+ 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:
+ self._setattr(va)
- self.qemu_img_bin = os.environ.get('KVM_TEST_qemu_img_binary')
- if not os.path.isabs(self.qemu_img_bin):
- self.qemu_img_bin = os.path.join(kvm_test_dir, self.qemu_img_bin)
- self.cdrom_iso = os.path.join(kvm_test_dir, cdrom_iso)
- self.floppy_mount = tempfile.mkdtemp(prefix='floppy_', dir='/tmp')
- self.cdrom_mount = tempfile.mkdtemp(prefix='cdrom_', dir='/tmp')
- self.nfs_mount = tempfile.mkdtemp(prefix='nfs_', dir='/tmp')
- floppy_name = os.environ['KVM_TEST_floppy']
- self.floppy_img = os.path.join(kvm_test_dir, floppy_name)
- floppy_dir = os.path.dirname(self.floppy_img)
- if not os.path.isdir(floppy_dir):
- os.makedirs(floppy_dir)
+ # Silly attribution just to calm pylint down...
+ self.tftp = self.tftp
+ if self.tftp:
+ self.tftp = os.path.join(KVM_TEST_DIR, self.tftp)
+ if not os.path.isdir(self.tftp):
+ os.makedirs(self.tftp)
- self.pxe_dir = os.environ.get('KVM_TEST_pxe_dir', '')
- self.pxe_image = os.environ.get('KVM_TEST_pxe_image', '')
- self.pxe_initrd = os.environ.get('KVM_TEST_pxe_initrd', '')
+ self.cdrom_cd1 = os.path.join(KVM_TEST_DIR, self.cdrom_cd1)
+ self.cdrom_cd1_mount = tempfile.mkdtemp(prefix='cdrom_cd1_', dir='/tmp')
+ if self.medium == 'nfs':
+ self.nfs_mount = tempfile.mkdtemp(prefix='nfs_', dir='/tmp')
- self.medium = os.environ.get('KVM_TEST_medium', '')
- self.url = os.environ.get('KVM_TEST_url', '')
- self.kernel = os.environ.get('KVM_TEST_kernel', '')
- self.initrd = os.environ.get('KVM_TEST_initrd', '')
- self.nfs_server = os.environ.get('KVM_TEST_nfs_server', '')
- self.nfs_dir = os.environ.get('KVM_TEST_nfs_dir', '')
- self.image_path = kvm_test_dir
+ self.floppy = os.path.join(KVM_TEST_DIR, self.floppy)
+ if not os.path.isdir(os.path.dirname(self.floppy)):
+ os.makedirs(os.path.dirname(self.floppy))
+
+ self.image_path = KVM_TEST_DIR
self.kernel_path = os.path.join(self.image_path, self.kernel)
self.initrd_path = os.path.join(self.image_path, self.initrd)
- def create_boot_floppy(self):
+ def _setattr(self, key):
"""
- 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.
+ Populate class attributes with contents of environment variables.
+
+ Example: KVM_TEST_medium will populate self.medium.
+
+ @param key: Name of the class attribute we desire to have.
"""
- print "Creating boot floppy"
+ env_name = 'KVM_TEST_%s' % key
+ value = os.environ.get(env_name, '')
+ setattr(self, key, value)
- if os.path.exists(self.floppy_img):
- os.remove(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.')
-
- try:
- 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'):
- # Red Hat kickstart install
- dest_fname = 'ks.cfg'
- elif self.unattended_file.endswith('.xml'):
- if self.tftp_root is '':
- # Windows unattended install
- dest_fname = "autounattend.xml"
- else:
- # SUSE autoyast install
- dest_fname = "autoinst.xml"
-
- dest = os.path.join(self.floppy_mount, dest_fname)
-
- # 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.
- unattended_contents = open(self.unattended_file).read()
- dummy_cdkey_re = r'\bKVM_TEST_CDKEY\b'
- real_cdkey = os.environ.get('KVM_TEST_cdkey')
- if re.search(dummy_cdkey_re, unattended_contents):
- if real_cdkey:
- unattended_contents = re.sub(dummy_cdkey_re, real_cdkey,
- unattended_contents)
- else:
- print ("WARNING: 'cdkey' required but not specified for "
- "this unattended installation")
-
- dummy_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)
+ def render_answer_file(self):
+ # 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.
+ unattended_contents = open(self.unattended_file).read()
+ dummy_cdkey_re = r'\bKVM_TEST_CDKEY\b'
+ real_cdkey = os.environ.get('KVM_TEST_cdkey')
+ if re.search(dummy_cdkey_re, unattended_contents):
+ if real_cdkey:
+ unattended_contents = re.sub(dummy_cdkey_re, real_cdkey,
+ unattended_contents)
else:
- raise SetupError("Unexpected installation medium %s" % self.url)
+ print ("WARNING: 'cdkey' required but not specified for "
+ "this unattended installation")
- unattended_contents = re.sub(dummy_re, content, unattended_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 SetupError("Unexpected installation medium %s" % self.url)
- print
- print "Unattended install %s contents:" % dest_fname
- print unattended_contents
- # Write the unattended file contents to 'dest'
- open(dest, 'w').write(unattended_contents)
+ unattended_contents = re.sub(dummy_medium_re, content,
+ unattended_contents)
- 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)
+ def replace_virtio_key(contents, dummy_re, env):
+ """
+ Replace a virtio dummy string with contents.
- finally:
- u_cmd = 'umount %s' % self.floppy_mount
- if os.system(u_cmd):
- raise SetupError('Could not unmount floppy at %s.' %
- self.floppy_mount)
- self.cleanup(self.floppy_mount)
+ If install_virtio is not set, replace it with a dummy string.
- os.chmod(self.floppy_img, 0755)
+ @param contents: Contents of the unattended file
+ @param dummy_re: Regular expression used to search on the.
+ unattended file contents.
+ @param env: Name of the environment variable.
+ """
+ dummy_path = "C:"
+ driver = os.environ.get(env, '')
- print "Boot floppy created successfuly"
+ if re.search(dummy_re, contents):
+ if self.install_virtio == "yes":
+ if driver.endswith("msi"):
+ driver = 'msiexec /passive /package ' + driver
+ else:
+ try:
+ # Let's escape windows style paths properly
+ drive, path = driver.split(":")
+ driver = drive + ":" + re.escape(path)
+ except:
+ pass
+ contents = re.sub(dummy_re, driver, contents)
+ else:
+ contents = re.sub(dummy_re, dummy_path, contents)
+ return contents
+
+ vdict = {r'\bKVM_TEST_STORAGE_DRIVER_PATH\b':
+ 'KVM_TEST_virtio_storage_path',
+ r'\bKVM_TEST_NETWORK_DRIVER_PATH\b':
+ 'KVM_TEST_virtio_network_path',
+ r'\bKVM_TEST_VIRTIO_NETWORK_INSTALLER\b':
+ 'KVM_TEST_virtio_network_installer_path'}
+
+ for vkey in vdict:
+ unattended_contents = replace_virtio_key(unattended_contents,
+ vkey, vdict[vkey])
+
+ print "Unattended install contents:"
+ print unattended_contents
+ return unattended_contents
+
+
+ def setup_boot_disk(self):
+ answer_contents = self.render_answer_file()
+
+ if self.unattended_file.endswith('.sif'):
+ dest_fname = 'winnt.sif'
+ setup_file = 'winnt.bat'
+ boot_disk = FloppyDisk(self.floppy)
+ boot_disk.setup_answer_file(dest_fname, answer_contents)
+ 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)
+ elif self.floppy:
+ boot_disk = FloppyDisk(self.floppy)
+ else:
+ raise SetupError("Neither cdrom_unattended nor floppy set "
+ "on the config file, please verify")
+ boot_disk.setup_answer_file(dest_fname, answer_contents)
+
+ elif self.unattended_file.endswith('.xml'):
+ if self.tftp:
+ # SUSE autoyast install
+ dest_fname = "autoinst.xml"
+ if self.cdrom_unattended:
+ boot_disk = CdromDisk(self.cdrom_unattended)
+ elif self.floppy:
+ boot_disk = FloppyDisk(self.floppy)
+ else:
+ raise SetupError("Neither cdrom_unattended nor floppy set "
+ "on the config file, please verify")
+ boot_disk.setup_answer_file(dest_fname, answer_contents)
+
+ else:
+ # Windows unattended install
+ dest_fname = "autounattend.xml"
+ boot_disk = FloppyDisk(self.floppy)
+ boot_disk.setup_answer_file(dest_fname, answer_contents)
+ if self.install_virtio == "yes":
+ boot_disk.setup_virtio_win2008(self.virtio_floppy)
+ boot_disk.copy_to(self.finish_program)
+
+ else:
+ raise SetupError('Unknown answer file %s' %
+ self.unattended_file)
+
+ boot_disk.close()
def setup_pxe_boot(self):
@@ -173,7 +442,7 @@
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
+ print "Setting up PXE boot using TFTP root %s" % self.tftp
pxe_file = None
pxe_paths = ['/usr/lib/syslinux/pxelinux.0',
@@ -188,17 +457,15 @@
'sure pxelinux or equivalent package for your '
'distro is installed.')
- pxe_dest = os.path.join(self.tftp_root, 'pxelinux.0')
+ pxe_dest = os.path.join(self.tftp, 'pxelinux.0')
shutil.copyfile(pxe_file, pxe_dest)
try:
- m_cmd = 'mount -t iso9660 -v -o loop,ro %s %s' % (self.cdrom_iso,
- self.cdrom_mount)
- if os.system(m_cmd):
- raise SetupError('Could not mount CD image %s.' %
- self.cdrom_iso)
+ m_cmd = ('mount -t iso9660 -v -o loop,ro %s %s' %
+ (self.cdrom_cd1, self.cdrom_cd1_mount))
+ run(m_cmd, info='Could not mount CD image %s.' % self.cdrom_cd1)
- pxe_dir = os.path.join(self.cdrom_mount, self.pxe_dir)
+ pxe_dir = os.path.join(self.cdrom_cd1_mount, self.pxe_dir)
pxe_image = os.path.join(pxe_dir, self.pxe_image)
pxe_initrd = os.path.join(pxe_dir, self.pxe_initrd)
@@ -213,19 +480,15 @@
'or a initrd.img file. Cannot find a PXE '
'image to proceed.' % self.pxe_dir)
- tftp_image = os.path.join(self.tftp_root, 'vmlinuz')
- tftp_initrd = os.path.join(self.tftp_root, 'initrd.img')
+ tftp_image = os.path.join(self.tftp, 'vmlinuz')
+ tftp_initrd = os.path.join(self.tftp, 'initrd.img')
shutil.copyfile(pxe_image, tftp_image)
shutil.copyfile(pxe_initrd, tftp_initrd)
finally:
- u_cmd = 'umount %s' % self.cdrom_mount
- if os.system(u_cmd):
- raise SetupError('Could not unmount CD at %s.' %
- self.cdrom_mount)
- self.cleanup(self.cdrom_mount)
+ cleanup(self.cdrom_cd1_mount)
- pxe_config_dir = os.path.join(self.tftp_root, 'pxelinux.cfg')
+ pxe_config_dir = os.path.join(self.tftp, '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')
@@ -245,7 +508,7 @@
def setup_url(self):
"""
- Download the vmlinuz and initrd.img from URL
+ Download the vmlinuz and initrd.img from URL.
"""
print "Downloading the vmlinuz and initrd.img"
os.chdir(self.image_path)
@@ -258,12 +521,11 @@
if os.path.exists(self.initrd):
os.unlink(self.initrd)
- if os.system(kernel_fetch_cmd) != 0:
- raise SetupError("Could not fetch vmlinuz from %s" % self.url)
- if os.system(initrd_fetch_cmd) != 0:
- raise SetupError("Could not fetch initrd.img from %s" % self.url)
+ run(kernel_fetch_cmd, info="Could not fetch vmlinuz from %s" % self.url)
+ run(initrd_fetch_cmd, info=("Could not fetch initrd.img from %s" %
+ self.url))
+ print "Download of vmlinuz and initrd.img finished"
- print "Downloading finish"
def setup_nfs(self):
"""
@@ -271,71 +533,44 @@
"""
print "Copying the vmlinuz and initrd.img from nfs"
- m_cmd = "mount %s:%s %s -o ro" % (self.nfs_server, self.nfs_dir, self.nfs_mount)
- if os.system(m_cmd):
- raise SetupError('Could not mount nfs server.')
-
- kernel_fetch_cmd = "cp %s/isolinux/%s %s" % (self.nfs_mount,
- self.kernel,
- self.image_path)
- initrd_fetch_cmd = "cp %s/isolinux/%s %s" % (self.nfs_mount,
- self.initrd,
- self.image_path)
+ m_cmd = ("mount %s:%s %s -o ro" %
+ (self.nfs_server, self.nfs_dir, self.nfs_mount))
+ run(m_cmd, info='Could not mount nfs server')
try:
- if os.system(kernel_fetch_cmd):
- raise SetupError("Could not copy the vmlinuz from %s" %
- self.nfs_mount)
- if os.system(initrd_fetch_cmd):
- raise SetupError("Could not copy the initrd.img from %s" %
- self.nfs_mount)
+ kernel_fetch_cmd = ("cp %s/isolinux/%s %s" %
+ (self.nfs_mount, self.kernel, self.image_path))
+ run(kernel_fetch_cmd, info=("Could not copy the vmlinuz from %s" %
+ self.nfs_mount))
+ initrd_fetch_cmd = ("cp %s/isolinux/%s %s" %
+ (self.nfs_mount, self.initrd, self.image_path))
+ run(initrd_fetch_cmd, info=("Could not copy the initrd.img from "
+ "%s" % self.nfs_mount))
finally:
- u_cmd = "umount %s" % self.nfs_mount
- if os.system(u_cmd):
- raise SetupError("Could not unmont nfs at %s" % self.nfs_mount)
- self.cleanup(self.nfs_mount)
-
- def cleanup(self, mount):
- """
- Clean up a previously used mountpoint.
-
- @param mount: Mountpoint to be cleaned up.
- """
- 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)
+ cleanup(self.nfs_mount)
def setup(self):
+ """
+ Configure the environment for unattended install.
+
+ Uses an appropriate strategy according to each install model.
+ """
print "Starting unattended install setup"
+ print
print "Variables set:"
- print " medium: " + str(self.medium)
- 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)
- print " pxe_dir: " + str(self.pxe_dir)
- print " pxe_image: " + str(self.pxe_image)
- print " pxe_initrd: " + str(self.pxe_initrd)
- print " url: " + str(self.url)
- print " kernel: " + str(self.kernel)
- print " initrd: " + str(self.initrd)
- print " nfs_server: " + str(self.nfs_server)
- print " nfs_dir: " + str(self.nfs_dir)
- print " nfs_mount: " + str(self.nfs_mount)
+ for member in inspect.getmembers(self):
+ name, value = member
+ attribute = getattr(self, name)
+ if not (name.startswith("__") or callable(attribute) or not value):
+ print " %s: %s" % (name, value)
+ print
- if self.unattended_file and self.floppy_img is not None:
- self.create_boot_floppy()
+ if self.unattended_file and (self.floppy or self.cdrom_unattended):
+ self.setup_boot_disk()
if self.medium == "cdrom":
- if self.tftp_root:
+ if self.tftp:
self.setup_pxe_boot()
elif self.medium == "url":
self.setup_url()
@@ -343,7 +578,7 @@
self.setup_nfs()
else:
raise SetupError("Unexpected installation method %s" %
- self.medium)
+ self.medium)
print "Unattended install setup finished successfuly"
diff --git a/client/tests/kvm/scripts/virtio_guest.py b/client/tests/kvm/scripts/virtio_guest.py
new file mode 100644
index 0000000..4862ef2
--- /dev/null
+++ b/client/tests/kvm/scripts/virtio_guest.py
@@ -0,0 +1,513 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+"""
+Auxiliary script used to send data between ports on guests.
+
+@copyright: 2008-2009 Red Hat Inc.
+@author: Jiri Zupka (jzupka@redhat.com)
+@author: Lukas Doktor (ldoktor@redhat.com)
+"""
+#from _pydev_SimpleXMLRPCServer import fcntl
+
+"""
+TODO:
+virt.init([consoles]) # sysfs, udev, OK
+virt.open(name)
+virt.close(name)
+virt.poll(name, eventmask, timeout) # poll.register(), poll.poll(),
+return event
+virt.send(name, length) # host disconnected
+virt.recv(name, length) # host disconnected
+virt.blocking(name, true) # true = blocking, false = nonblocking
+virt.loopback(in_names, out_names, type="None") # use select/poll
+"""
+
+import threading
+from threading import Thread
+import os, time, select, re, random, sys, array, fcntl, array, subprocess
+
+DEBUGPATH = "/sys/kernel/debug"
+SYSFSPATH = "/sys/class/virtio-ports/"
+
+
+class virtio_guest():
+
+ LOOP_NONE = 0
+ LOOP_POLL = 1
+ LOOP_SELECT = 2
+
+ def __init__(self):
+ self.files = {}
+ self.exit_thread = threading.Event()
+ self.threads = []
+ self.ports = {}
+
+
+ def _readfile(self, name):
+ """
+ Read file and return content as string
+
+ @param name: Name of file
+ @return: Content of file as string
+ """
+ out = ""
+ try:
+ f = open(name, "r")
+ out = f.read()
+ f.close()
+ except:
+ print "FAIL: Cannot open file %s" % (name)
+
+ return out
+
+
+ def _get_port_status(self):
+ """
+ Get info about ports from kernel debugfs.
+
+ @return: Ports dictionary of port properties
+ """
+ ports = {}
+ not_present_msg = "FAIL: There's no virtio-ports dir in debugfs"
+ if (not os.path.ismount(DEBUGPATH)):
+ os.system('mount -t debugfs none %s' % (DEBUGPATH))
+ try:
+ if not os.path.isdir('%s/virtio-ports' % (DEBUGPATH)):
+ print not_present_msg
+ except:
+ print not_present_msg
+ else:
+ viop_names = os.listdir('%s/virtio-ports' % (DEBUGPATH))
+ for name in viop_names:
+ f = open("%s/virtio-ports/%s" % (DEBUGPATH, name), 'r')
+ port = {}
+ for line in iter(f):
+ m = re.match("(\S+): (\S+)", line)
+ port[m.group(1)] = m.group(2)
+
+ if (port['is_console'] == "yes"):
+ port["path"] = "/dev/hvc%s" % (port["console_vtermno"])
+ # Console works like a serialport
+ else:
+ port["path"] = "/dev/%s" % name
+
+ if (not os.path.exists(port['path'])):
+ print "FAIL: %s not exist" % port['path']
+
+ sysfspath = SYSFSPATH + name
+ if (not os.path.isdir(sysfspath)):
+ print "FAIL: %s not exist" % (sysfspath)
+
+ info_name = sysfspath + "/name"
+ port_name = self._readfile(info_name).strip()
+ if (port_name != port["name"]):
+ print ("FAIL: Port info not match \n%s - %s\n%s - %s" %
+ (info_name , port_name,
+ "%s/virtio-ports/%s" % (DEBUGPATH, name),
+ port["name"]))
+
+ ports[port['name']] = port
+ f.close()
+
+ return ports
+
+
+ def init(self, in_files):
+ """
+ Init and check port properties.
+ """
+ self.ports = self._get_port_status()
+
+ for item in in_files:
+ if (item[1] != self.ports[item[0]]["is_console"]):
+ print self.ports
+ print "FAIL: Host console is not like console on guest side\n"
+ print "PASS: Init and check virtioconsole files in system."
+
+
+ class switch(Thread):
+ """
+ Thread that sends data between ports.
+ """
+ def __init__ (self, in_files, out_files, event,
+ cachesize=1024, method=0):
+ """
+ @param in_files: Array of input files.
+ @param out_files: Array of output files.
+ @param method: Method of read/write access.
+ @param cachesize: Block to receive and send.
+ """
+ Thread.__init__(self)
+
+ self.in_files = in_files
+ self.out_files = out_files
+ self.exit_thread = event
+ self.method = method
+
+ self.cachesize = cachesize
+
+
+ def _none_mode(self):
+ """
+ Read and write to device in blocking mode
+ """
+ data = ""
+ while not self.exit_thread.isSet():
+ data = ""
+ for desc in self.in_files:
+ data += os.read(desc, self.cachesize)
+ if data != "":
+ for desc in self.out_files:
+ os.write(desc, data)
+
+
+ def _poll_mode(self):
+ """
+ Read and write to device in polling mode.
+ """
+
+ pi = select.poll()
+ po = select.poll()
+
+ for fd in self.in_files:
+ pi.register(fd, select.POLLIN)
+
+ for fd in self.out_files:
+ po.register(fd, select.POLLOUT)
+
+ while not self.exit_thread.isSet():
+ data = ""
+ t_out = self.out_files
+
+ readyf = pi.poll(1.0)
+ for i in readyf:
+ data += os.read(i[0], self.cachesize)
+
+ if data != "":
+ while ((len(t_out) != len(readyf)) and not
+ self.exit_thread.isSet()):
+ readyf = po.poll(1.0)
+ for desc in t_out:
+ os.write(desc, data)
+
+
+ def _select_mode(self):
+ """
+ Read and write to device in selecting mode.
+ """
+ while not self.exit_thread.isSet():
+ ret = select.select(self.in_files, [], [], 1.0)
+ data = ""
+ if ret[0] != []:
+ for desc in ret[0]:
+ data += os.read(desc, self.cachesize)
+ if data != "":
+ ret = select.select([], self.out_files, [], 1.0)
+ while ((len(self.out_files) != len(ret[1])) and not
+ self.exit_thread.isSet()):
+ ret = select.select([], self.out_files, [], 1.0)
+ for desc in ret[1]:
+ os.write(desc, data)
+
+
+ def run(self):
+ if (self.method == virtio_guest.LOOP_POLL):
+ self._poll_mode()
+ elif (self.method == virtio_guest.LOOP_SELECT):
+ self._select_mode()
+ else:
+ self._none_mode()
+
+
+ class sender(Thread):
+ """
+ Creates a thread which sends random blocks of data to dst port.
+ """
+ def __init__(self, port, event, length):
+ """
+ @param port: Destination port
+ @param length: Length of the random data block
+ """
+ Thread.__init__(self)
+ self.port = port
+ self.exit_thread = event
+ self.data = array.array('L')
+ for i in range(max(length / self.data.itemsize, 1)):
+ self.data.append(random.randrange(sys.maxint))
+
+ def run(self):
+ while not self.exit_thread.isSet():
+ os.write(self.port, self.data)
+
+
+ def _open(self, in_files):
+ """
+ Open devices and return array of descriptors
+
+ @param in_files: Files array
+ @return: Array of descriptor
+ """
+ f = []
+
+ for item in in_files:
+ name = self.ports[item]["path"]
+ if (name in self.files):
+ f.append(self.files[name])
+ else:
+ try:
+ self.files[name] = os.open(name, os.O_RDWR)
+ if (self.ports[item]["is_console"] == "yes"):
+ print os.system("stty -F %s raw -echo" % (name))
+ print os.system("stty -F %s -a" % (name))
+ f.append(self.files[name])
+ except Exception as inst:
+ print "FAIL: Failed to open file %s" % (name)
+ raise inst
+ return f
+
+
+ def poll(self, port, expected, timeout=500):
+ """
+ Pool event from device and print event like text.
+
+ @param file: Device.
+ """
+ in_f = self._open([port])
+
+ p = select.poll()
+ p.register(in_f[0])
+
+ mask = p.poll(timeout)
+
+ str = ""
+ if (mask[0][1] & select.POLLIN):
+ str += "IN "
+ if (mask[0][1] & select.POLLPRI):
+ str += "PRI IN "
+ if (mask[0][1] & select.POLLOUT):
+ str += "OUT "
+ if (mask[0][1] & select.POLLERR):
+ str += "ERR "
+ if (mask[0][1] & select.POLLHUP):
+ str += "HUP "
+ if (mask[0][1] & select.POLLMSG):
+ str += "MSG "
+
+ if (mask[0][1] & expected) == expected:
+ print "PASS: Events: " + str
+ else:
+ print "FAIL: Events: " + str
+
+
+ def blocking(self, port, mode=False):
+ """
+ Set port function mode blocking/nonblocking
+
+ @param port: port to set mode
+ @param mode: False to set nonblock mode, True for block mode
+ """
+ path = self.ports[port]["path"]
+ fd = self.files[path]
+
+ try:
+ fl = fcntl.fcntl(fd, fcntl.F_GETFL)
+ if not mode:
+ fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
+ else:
+ fcntl.fcntl(fd, fcntl.F_SETFL, fl & ~os.O_NONBLOCK)
+
+ except Exception as inst:
+ print "FAIL: Setting (non)blocking mode: " + str(inst)
+ return
+
+ print ("PASS: set blocking mode to %s mode" %
+ ("blocking" if mode else "nonblocking"))
+
+
+ def close(self, file):
+ """
+ Close open port.
+
+ @param file: File to close.
+ """
+ descriptor = None
+ path = self.ports[file]["path"]
+ if path != None:
+ if path in self.files.keys():
+ descriptor = self.files[path]
+ del self.files[path]
+ try:
+ os.close(descriptor)
+ except Exception as inst:
+ print "FAIL: Closing the file: " + str(inst)
+ return
+ print "PASS: Close"
+
+
+ def open(self, in_files):
+ """
+ Direct open devices.
+
+ @param in_files: Array of files.
+ @return: Array of descriptors.
+ """
+ name = self.ports[in_files]["path"]
+ try:
+ self.files[name] = os.open(name, os.O_RDWR)
+ print "PASS: Open all filles correctly."
+ except Exception as inst:
+ print "%s\nFAIL: Failed open file %s" % (str(inst), name)
+
+
+ def loopback(self, in_files, out_files, cachesize=1024, mode=LOOP_NONE):
+ """
+ Start a switch thread.
+
+ (There is a problem with multiple opens of a single file).
+
+ @param in_files: Array of input files.
+ @param out_files: Array of output files.
+ @param cachesize: Cachesize.
+ """
+ self.ports = self._get_port_status()
+
+ in_f = self._open(in_files)
+ out_f = self._open(out_files)
+
+ s = self.switch(in_f, out_f, self.exit_thread, cachesize, mode)
+ s.start()
+ self.threads.append(s)
+ print "PASS: Start switch"
+
+
+ def exit_threads(self):
+ """
+ Function end all running data switch.
+ """
+ self.exit_thread.set()
+ for th in self.threads:
+ print "join"
+ th.join()
+ self.exit_thread.clear()
+
+ del self.threads[:]
+ for desc in self.files.itervalues():
+ os.close(desc)
+ self.files.clear()
+ print "PASS: All threads finished."
+
+
+ def die(self):
+ """
+ Quit consoleswitch.
+ """
+ self.exit_threads()
+ exit()
+
+
+ def send_loop_init(self, port, length):
+ """
+ Prepares the sender thread. Requires clean thread structure.
+ """
+ self.ports = self._get_port_status()
+ in_f = self._open([port])
+
+ self.threads.append(self.sender(in_f[0], self.exit_thread, length))
+ print "PASS: Sender prepare"
+
+
+ def send_loop(self):
+ """
+ Start sender data transfer. Requires senderprepare run first.
+ """
+ self.threads[0].start()
+ print "PASS: Sender start"
+
+
+ def send(self, port, length=1, mode=True):
+ """
+ Send a data of some length
+
+ @param port: Port to write data
+ @param length: Length of data
+ @param mode: True = loop mode, False = one shoot mode
+ """
+ in_f = self._open([port])
+
+ data = ""
+ while len(data) < length:
+ data += "%c" % random.randrange(255)
+ try:
+ writes = os.write(in_f[0], data)
+ except Exception as inst:
+ print inst
+ if not writes:
+ writes = 0
+ if mode:
+ while (writes < length):
+ try:
+ writes += os.write(in_f[0], data)
+ except Exception as inst:
+ print inst
+ if writes >= length:
+ print "PASS: Send data length %d" % writes
+ else:
+ print ("FAIL: Partial send: desired %d, transfered %d" %
+ (length, writes))
+
+
+ def recv(self, port, length=1, buffer=1024, mode=True):
+ """
+ Recv a data of some length
+
+ @param port: Port to write data
+ @param length: Length of data
+ @param mode: True = loop mode, False = one shoot mode
+ """
+ in_f = self._open([port])
+
+ recvs = ""
+ try:
+ recvs = os.read(in_f[0], buffer)
+ except Exception as inst:
+ print inst
+ if mode:
+ while (len(recvs) < length):
+ try:
+ recvs += os.read(in_f[0], buffer)
+ except Exception as inst:
+ print inst
+ if len(recvs) >= length:
+ print "PASS: Recv data length %d" % len(recvs)
+ else:
+ print ("FAIL: Partial recv: desired %d, transfered %d" %
+ (length, len(recvs)))
+
+
+def compile():
+ """
+ Compile virtio_guest.py to speed up.
+ """
+ import py_compile
+ py_compile.compile(sys.path[0] + "/virtio_guest.py")
+ print "PASS: compile"
+ exit(0)
+
+
+def main():
+ """
+ Main (infinite) loop of virtio_guest.
+ """
+ if (len(sys.argv) > 1) and (sys.argv[1] == "-c"):
+ compile()
+
+ virt = virtio_guest()
+ print "PASS: Start"
+
+ while True:
+ str = raw_input()
+ exec str
+
+
+if __name__ == "__main__":
+ main()