Source code for virttest.tests.unattended_install

import logging
import time
import re
import os
import tempfile
import ConfigParser
import threading
import shutil
import xml.dom.minidom
from autotest.client.shared import error, iso9660
from autotest.client import utils
from virttest import virt_vm, utils_misc, utils_disk
from virttest import qemu_monitor, remote, syslog_server
from virttest import http_server, data_dir, utils_net, utils_test
from virttest import funcatexit, storage


# Whether to print all shell commands called
DEBUG = False

_url_auto_content_server_thread = None
_url_auto_content_server_thread_event = None

_unattended_server_thread = None
_unattended_server_thread_event = None

_syslog_server_thread = None
_syslog_server_thread_event = None


[docs]def start_auto_content_server_thread(port, path): global _url_auto_content_server_thread global _url_auto_content_server_thread_event if _url_auto_content_server_thread is None: _url_auto_content_server_thread_event = threading.Event() _url_auto_content_server_thread = threading.Thread( target=http_server.http_server, args=(port, path, terminate_auto_content_server_thread)) _url_auto_content_server_thread.start()
[docs]def start_unattended_server_thread(port, path): global _unattended_server_thread global _unattended_server_thread_event if _unattended_server_thread is None: _unattended_server_thread_event = threading.Event() _unattended_server_thread = threading.Thread( target=http_server.http_server, args=(port, path, terminate_unattended_server_thread)) _unattended_server_thread.start()
[docs]def terminate_auto_content_server_thread(): global _url_auto_content_server_thread global _url_auto_content_server_thread_event if _url_auto_content_server_thread is None: return False if _url_auto_content_server_thread_event is None: return False if _url_auto_content_server_thread_event.isSet(): return True return False
[docs]def terminate_unattended_server_thread(): global _unattended_server_thread, _unattended_server_thread_event if _unattended_server_thread is None: return False if _unattended_server_thread_event is None: return False if _unattended_server_thread_event.isSet(): return True return False
[docs]class RemoteInstall(object): """ Represents a install http server that we can master according to our needs. """ def __init__(self, path, ip, port, filename): self.path = path utils_disk.cleanup(self.path) os.makedirs(self.path) self.ip = ip self.port = port self.filename = filename start_unattended_server_thread(self.port, self.path)
[docs] def get_url(self): return 'http://%s:%s/%s' % (self.ip, self.port, self.filename)
[docs] def get_answer_file_path(self, filename): return os.path.join(self.path, filename)
[docs] def close(self): os.chmod(self.path, 0755) logging.debug("unattended http server %s successfully created", self.get_url())
[docs]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, vm): """ Sets class attributes from test parameters. :param test: QEMU test object. :param params: Dictionary with test parameters. """ root_dir = data_dir.get_data_dir() self.deps_dir = os.path.join(test.virtdir, 'deps') self.unattended_dir = os.path.join(test.virtdir, 'unattended') self.results_dir = test.debugdir self.params = params self.attributes = ['kernel_args', 'finish_program', 'cdrom_cd1', 'unattended_file', 'medium', 'url', 'kernel', 'initrd', 'nfs_server', 'nfs_dir', 'install_virtio', 'floppy_name', 'cdrom_unattended', 'boot_path', 'kernel_params', 'extra_params', 'qemu_img_binary', 'cdkey', 'finish_program', 'vm_type', 'process_check', 'vfd_size', 'cdrom_mount_point', 'floppy_mount_point', 'cdrom_virtio', 'virtio_floppy', 're_driver_match', 're_hardware_id', 'driver_in_floppy', 'vga'] for a in self.attributes: setattr(self, a, params.get(a, '')) # Will setup the virtio attributes v_attributes = ['virtio_floppy', 'virtio_scsi_path', 'virtio_storage_path', 'virtio_network_path', 'virtio_oemsetup_id', 'virtio_network_installer_path', 'virtio_balloon_installer_path', 'virtio_qxl_installer_path'] for va in v_attributes: setattr(self, va, params.get(va, '')) self.tmpdir = test.tmpdir self.qemu_img_binary = utils_misc.get_qemu_img_binary(params) if getattr(self, 'unattended_file'): self.unattended_file = os.path.join(test.virtdir, self.unattended_file) if params.get('use_ovmf_autounattend'): self.unattended_file = re.sub("\.", "_ovmf.", self.unattended_file) if getattr(self, 'finish_program'): self.finish_program = os.path.join(test.virtdir, self.finish_program) 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 getattr(self, 'cdrom_unattended'): self.cdrom_unattended = os.path.join(root_dir, self.cdrom_unattended) if getattr(self, 'virtio_floppy'): self.virtio_floppy = os.path.join(root_dir, self.virtio_floppy) if getattr(self, 'cdrom_virtio'): self.cdrom_virtio = os.path.join(root_dir, self.cdrom_virtio) if getattr(self, 'kernel'): self.kernel = os.path.join(root_dir, self.kernel) if getattr(self, 'initrd'): self.initrd = os.path.join(root_dir, self.initrd) if self.medium == 'nfs': self.nfs_mount = tempfile.mkdtemp(prefix='nfs_', dir=self.tmpdir) setattr(self, 'floppy', self.floppy_name) 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) # Content server params # lookup host ip address for first nic by interface name try: auto_ip = utils_net.get_ip_address_by_interface( vm.virtnet[0].netdst) except utils_net.NetError: auto_ip = None self.url_auto_content_ip = params.get('url_auto_ip', auto_ip) self.url_auto_content_port = None # Kickstart server params # use the same IP as url_auto_content_ip, but a different port self.unattended_server_port = None # Embedded Syslog Server self.syslog_server_enabled = params.get('syslog_server_enabled', 'no') self.syslog_server_ip = params.get('syslog_server_ip', auto_ip) self.syslog_server_port = int(params.get('syslog_server_port', 5140)) self.syslog_server_tcp = params.get('syslog_server_proto', 'tcp') == 'tcp' self.vm = vm @error.context_aware def get_driver_hardware_id(self, driver, run_cmd=True): """ Get windows driver's hardware id from inf files. :param dirver: Configurable driver name. :param run_cmd: Use hardware id in windows cmd command or not. :return: Windows driver's hardware id """ if not os.path.exists(self.cdrom_mount_point): os.mkdir(self.cdrom_mount_point) if not os.path.exists(self.floppy_mount_point): os.mkdir(self.floppy_mount_point) if not os.path.ismount(self.cdrom_mount_point): utils.system("mount %s %s -o loop" % (self.cdrom_virtio, self.cdrom_mount_point), timeout=60) if not os.path.ismount(self.floppy_mount_point): utils.system("mount %s %s -o loop" % (self.virtio_floppy, self.floppy_mount_point), timeout=60) drivers_d = [] driver_link = None if self.driver_in_floppy is not None: driver_in_floppy = self.driver_in_floppy drivers_d = driver_in_floppy.split() else: drivers_d.append('qxl.inf') for driver_d in drivers_d: if driver_d in driver: driver_link = os.path.join(self.floppy_mount_point, driver) if driver_link is None: driver_link = os.path.join(self.cdrom_mount_point, driver) try: txt = open(driver_link, "r").read() hwid = re.findall(self.re_hardware_id, txt)[-1].rstrip() if run_cmd: hwid = '^&'.join(hwid.split('&')) return hwid except Exception, e: logging.error("Fail to get hardware id with exception: %s" % e) @error.context_aware def update_driver_hardware_id(self, driver): """ Update driver string with the hardware id get from inf files @driver: driver string :return: new driver string """ if 'hwid' in driver: if 'hwidcmd' in driver: run_cmd = True else: run_cmd = False if self.re_driver_match is not None: d_str = self.re_driver_match else: d_str = "(\S+)\s*hwid" drivers_in_floppy = [] if self.driver_in_floppy is not None: drivers_in_floppy = self.driver_in_floppy.split() mount_point = self.cdrom_mount_point storage_path = self.cdrom_virtio for driver_in_floppy in drivers_in_floppy: if driver_in_floppy in driver: mount_point = self.floppy_mount_point storage_path = self.virtio_floppy break d_link = re.findall(d_str, driver)[0].split(":")[1] d_link = "/".join(d_link.split("\\\\")[1:]) hwid = utils_test.get_driver_hardware_id(d_link, mount_point, storage_path, run_cmd=run_cmd) if hwid: driver = driver.replace("hwidcmd", hwid.strip()) else: raise error.TestError("Can not find hwid from the driver" " inf file") return driver
[docs] 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) dummy_logging_re = r'\bKVM_TEST_LOGGING\b' if re.search(dummy_logging_re, contents): if self.syslog_server_enabled == 'yes': l = 'logging --host=%s --port=%s --level=debug' l = l % (self.syslog_server_ip, self.syslog_server_port) else: l = '' contents = re.sub(dummy_logging_re, l, contents) dummy_graphical_re = re.compile('GRAPHICAL_OR_TEXT') if dummy_graphical_re.search(contents): if not self.vga or self.vga.lower() == "none": contents = dummy_graphical_re.sub('text', contents) else: contents = dummy_graphical_re.sub('graphical', contents) logging.debug("Unattended install contents:") for line in contents.splitlines(): logging.debug(line) utils.open_write_close(answer_path, contents)
[docs] 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_network_path) else: parser.remove_option('Unattended', 'OemPnPDriversPath') dummy_re_dirver = {'KVM_TEST_VIRTIO_NETWORK_INSTALLER': 'virtio_network_installer_path', 'KVM_TEST_VIRTIO_BALLOON_INSTALLER': 'virtio_balloon_installer_path', 'KVM_TEST_VIRTIO_QXL_INSTALLER': 'virtio_qxl_installer_path'} dummy_re = "" for dummy in dummy_re_dirver: if dummy_re: dummy_re += "|%s" % dummy else: dummy_re = dummy # Replace the process check in finish command dummy_process_re = r'\bPROCESS_CHECK\b' for opt in parser.options('GuiRunOnce'): check = parser.get('GuiRunOnce', opt) if re.search(dummy_process_re, check): process_check = re.sub(dummy_process_re, "%s" % self.process_check, check) parser.set('GuiRunOnce', opt, process_check) elif re.findall(dummy_re, check): dummy = re.findall(dummy_re, check)[0] driver = getattr(self, dummy_re_dirver[dummy]) if driver.endswith("msi"): driver = 'msiexec /passive /package ' + driver elif 'INSTALLER' in dummy: driver = self.update_driver_hardware_id(driver) elif driver is None: driver = 'dir' check = re.sub(dummy, driver, check) parser.set('GuiRunOnce', opt, check) # Now, writing the in memory config state to the unattended file fp = open(answer_path, 'w') parser.write(fp) fp.close() # Let's read it so we can debug print the contents fp = open(answer_path, 'r') contents = fp.read() fp.close() logging.debug("Unattended install contents:") for line in contents.splitlines(): logging.debug(line)
[docs] 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] if product_key.getElementsByTagName('Key'): key = product_key.getElementsByTagName('Key')[0] key_text = key.childNodes[0] else: key_text = product_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_scsi_path, self.virtio_storage_path, self.virtio_network_path] for path, value in zip(paths, values): path_text = path.childNodes[0] assert path_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 # And process check in finish command command_lines = doc.getElementsByTagName("CommandLine") dummy_re_dirver = {'KVM_TEST_VIRTIO_NETWORK_INSTALLER': 'virtio_network_installer_path', 'KVM_TEST_VIRTIO_BALLOON_INSTALLER': 'virtio_balloon_installer_path', 'KVM_TEST_VIRTIO_QXL_INSTALLER': 'virtio_qxl_installer_path'} process_check_re = 'PROCESS_CHECK' dummy_re = "" for dummy in dummy_re_dirver: if dummy_re: dummy_re += "|%s" % dummy else: dummy_re = dummy for command_line in command_lines: command_line_text = command_line.childNodes[0] assert command_line_text.nodeType == doc.TEXT_NODE if re.findall(dummy_re, command_line_text.data): dummy = re.findall(dummy_re, command_line_text.data)[0] driver = getattr(self, dummy_re_dirver[dummy]) if driver.endswith("msi"): driver = 'msiexec /passive /package ' + driver elif 'INSTALLER' in dummy: driver = self.update_driver_hardware_id(driver) t = command_line_text.data t = re.sub(dummy_re, driver, t) command_line_text.data = t if process_check_re in command_line_text.data: t = command_line_text.data t = re.sub(process_check_re, self.process_check, 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) fp.close()
[docs] 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) fp.close()
[docs] def preseed_initrd(self): """ Puts a preseed file inside a gz compressed initrd file. Debian and Ubuntu use preseed as the OEM install mechanism. The only way to get fully automated setup without resorting to kernel params is to add a preseed.cfg file at the root of the initrd image. """ logging.debug("Remastering initrd.gz file with preseed file") dest_fname = 'preseed.cfg' remaster_path = os.path.join(self.image_path, "initrd_remaster") if not os.path.isdir(remaster_path): os.makedirs(remaster_path) base_initrd = os.path.basename(self.initrd) os.chdir(remaster_path) utils.run("gzip -d < ../%s | fakeroot cpio --extract --make-directories " "--no-absolute-filenames" % base_initrd, verbose=DEBUG) utils.run("cp %s %s" % (self.unattended_file, dest_fname), verbose=DEBUG) # For libvirt initrd.gz will be renamed to initrd.img in setup_cdrom() utils.run("find . | fakeroot cpio -H newc --create | gzip -9 > ../%s" % base_initrd, verbose=DEBUG) os.chdir(self.image_path) utils.run("rm -rf initrd_remaster", verbose=DEBUG) contents = open(self.unattended_file).read() logging.debug("Unattended install contents:") for line in contents.splitlines(): logging.debug(line)
[docs] def setup_unattended_http_server(self): ''' Setup a builtin http server for serving the kickstart file Does nothing if unattended file is not a kickstart file ''' if self.unattended_file.endswith('.ks'): # Red Hat kickstart install dest_fname = 'ks.cfg' answer_path = os.path.join(self.tmpdir, dest_fname) self.answer_kickstart(answer_path) if self.unattended_server_port is None: self.unattended_server_port = utils_misc.find_free_port( 8000, 8099, self.url_auto_content_ip) start_unattended_server_thread(self.unattended_server_port, self.tmpdir) # Point installation to this kickstart url ks_param = 'ks=http://%s:%s/%s' % (self.url_auto_content_ip, self.unattended_server_port, dest_fname) if 'ks=' in self.kernel_params: kernel_params = re.sub('ks\=[\w\d\:\.\/]+', ks_param, self.kernel_params) else: kernel_params = '%s %s' % (self.kernel_params, ks_param) # reflect change on params self.kernel_params = kernel_params
[docs] def setup_boot_disk(self): if self.unattended_file.endswith('.sif'): dest_fname = 'winnt.sif' setup_file = 'winnt.bat' boot_disk = utils_disk.FloppyDisk(self.floppy, self.qemu_img_binary, self.tmpdir, self.vfd_size) 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.params.get('unattended_delivery_method') == 'integrated': ks_param = 'ks=cdrom:/dev/sr0:/isolinux/%s' % dest_fname kernel_params = self.kernel_params if 'ks=' in kernel_params: kernel_params = re.sub('ks\=[\w\d\:\.\/]+', ks_param, kernel_params) else: kernel_params = '%s %s' % (kernel_params, ks_param) # Standard setting is kickstart disk in /dev/sr0 and # install cdrom in /dev/sr1. As we merge them together, # we need to change repo configuration to /dev/sr0 if 'repo=cdrom' in kernel_params: kernel_params = re.sub('repo\=cdrom[\:\w\d\/]*', 'repo=cdrom:/dev/sr0', kernel_params) self.kernel_params = None boot_disk = utils_disk.CdromInstallDisk( self.cdrom_unattended, self.tmpdir, self.cdrom_cd1_mount, kernel_params) elif self.params.get('unattended_delivery_method') == 'url': if self.unattended_server_port is None: self.unattended_server_port = utils_misc.find_free_port( 8000, 8099, self.url_auto_content_ip) path = os.path.join(os.path.dirname(self.cdrom_unattended), 'ks') boot_disk = RemoteInstall(path, self.url_auto_content_ip, self.unattended_server_port, dest_fname) ks_param = 'ks=%s' % boot_disk.get_url() kernel_params = self.kernel_params if 'ks=' in kernel_params: kernel_params = re.sub('ks\=[\w\d\:\.\/]+', ks_param, kernel_params) else: kernel_params = '%s %s' % (kernel_params, ks_param) # Standard setting is kickstart disk in /dev/sr0 and # install cdrom in /dev/sr1. When we get ks via http, # we need to change repo configuration to /dev/sr0 kernel_params = re.sub('repo\=cdrom[\:\w\d\/]*', 'repo=cdrom:/dev/sr0', kernel_params) self.kernel_params = kernel_params elif self.params.get('unattended_delivery_method') == 'cdrom': boot_disk = utils_disk.CdromDisk(self.cdrom_unattended, self.tmpdir) elif self.params.get('unattended_delivery_method') == 'floppy': boot_disk = utils_disk.FloppyDisk(self.floppy, self.qemu_img_binary, self.tmpdir, self.vfd_size) ks_param = 'ks=floppy' kernel_params = self.kernel_params if 'ks=' in kernel_params: # Reading ks from floppy directly doesn't work in some OS, # options 'ks=hd:/dev/fd0' can reading ks from mounted # floppy, so skip repace it; if not re.search("fd\d+", kernel_params): kernel_params = re.sub('ks\=[\w\d\:\.\/]+', ks_param, kernel_params) else: kernel_params = '%s %s' % (kernel_params, ks_param) kernel_params = re.sub('repo\=cdrom[\:\w\d\/]*', 'repo=cdrom:/dev/sr0', kernel_params) self.kernel_params = kernel_params 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.kernel_params: # SUSE autoyast install dest_fname = "autoinst.xml" if (self.cdrom_unattended and self.params.get('unattended_delivery_method') == 'cdrom'): boot_disk = utils_disk.CdromDisk(self.cdrom_unattended, self.tmpdir) elif self.floppy: autoyast_param = 'autoyast=device://fd0/autoinst.xml' kernel_params = self.kernel_params if 'autoyast=' in kernel_params: kernel_params = re.sub('autoyast\=[\w\d\:\.\/]+', autoyast_param, kernel_params) else: kernel_params = '%s %s' % ( kernel_params, autoyast_param) self.kernel_params = kernel_params boot_disk = utils_disk.FloppyDisk(self.floppy, self.qemu_img_binary, self.tmpdir, self.vfd_size) 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" if self.params.get('unattended_delivery_method') == 'cdrom': boot_disk = utils_disk.CdromDisk(self.cdrom_unattended, self.tmpdir) if self.install_virtio == "yes": boot_disk.setup_virtio_win2008(self.virtio_floppy, self.cdrom_virtio) else: self.cdrom_virtio = None else: boot_disk = utils_disk.FloppyDisk(self.floppy, self.qemu_img_binary, self.tmpdir, self.vfd_size) if self.install_virtio == "yes": boot_disk.setup_virtio_win2008(self.virtio_floppy) answer_path = boot_disk.get_answer_file_path(dest_fname) self.answer_windows_xml(answer_path) 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) if not os.path.isdir(self.image_path): os.makedirs(self.image_path) if (self.params.get('unattended_delivery_method') in ['integrated', 'url']): i = iso9660.Iso9660Mount(self.cdrom_cd1) self.cdrom_cd1_mount = i.mnt_dir else: i = iso9660.iso9660(self.cdrom_cd1) if i is None: raise error.TestFail("Could not instantiate an iso9660 class") i.copy(os.path.join(self.boot_path, os.path.basename(self.kernel)), self.kernel) assert(os.path.getsize(self.kernel) > 0) i.copy(os.path.join(self.boot_path, os.path.basename(self.initrd)), self.initrd) assert(os.path.getsize(self.initrd) > 0) if self.unattended_file.endswith('.preseed'): self.preseed_initrd() if self.params.get("vm_type") == "libvirt": if self.vm.driver_type == 'qemu': # Virtinstall command needs files "vmlinuz" and "initrd.img" os.chdir(self.image_path) base_kernel = os.path.basename(self.kernel) base_initrd = os.path.basename(self.initrd) if base_kernel != 'vmlinuz': utils.run("mv %s vmlinuz" % base_kernel, verbose=DEBUG) if base_initrd != 'initrd.img': utils.run("mv %s initrd.img" % base_initrd, verbose=DEBUG) if (self.params.get('unattended_delivery_method') != 'integrated'): i.close() utils_disk.cleanup(self.cdrom_cd1_mount) elif ((self.vm.driver_type == 'xen') and (self.params.get('hvm_or_pv') == 'pv')): logging.debug("starting unattended content web server") self.url_auto_content_port = utils_misc.find_free_port(8100, 8199, self.url_auto_content_ip) start_auto_content_server_thread(self.url_auto_content_port, self.cdrom_cd1_mount) self.medium = 'url' self.url = ('http://%s:%s' % (self.url_auto_content_ip, self.url_auto_content_port)) pxe_path = os.path.join( os.path.dirname(self.image_path), 'xen') if not os.path.isdir(pxe_path): os.makedirs(pxe_path) pxe_kernel = os.path.join(pxe_path, os.path.basename(self.kernel)) pxe_initrd = os.path.join(pxe_path, os.path.basename(self.initrd)) utils.run("cp %s %s" % (self.kernel, pxe_kernel)) utils.run("cp %s %s" % (self.initrd, pxe_initrd)) if 'repo=cdrom' in self.kernel_params: # Red Hat self.kernel_params = re.sub('repo\=[\:\w\d\/]*', 'repo=http://%s:%s' % (self.url_auto_content_ip, self.url_auto_content_port), self.kernel_params) @error.context_aware def setup_url_auto(self): """ Configures the builtin web server for serving content """ auto_content_url = 'http://%s:%s' % (self.url_auto_content_ip, self.url_auto_content_port) self.params['auto_content_url'] = auto_content_url @error.context_aware def setup_url(self): """ Download the vmlinuz and initrd.img from URL. """ # it's only necessary to download kernel/initrd if running bare qemu if self.vm_type == 'qemu': error.context("downloading vmlinuz/initrd.img from %s" % self.url) if not os.path.exists(self.image_path): os.mkdir(self.image_path) os.chdir(self.image_path) kernel_cmd = "wget -q %s/%s/%s" % (self.url, self.boot_path, os.path.basename(self.kernel)) initrd_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_cmd, verbose=DEBUG) utils.run(initrd_cmd, verbose=DEBUG) if 'repo=cdrom' in self.kernel_params: # Red Hat self.kernel_params = re.sub('repo\=[\:\w\d\/]*', 'repo=%s' % self.url, self.kernel_params) elif 'autoyast=' in self.kernel_params: # SUSE self.kernel_params = (self.kernel_params + " ip=dhcp install=" + self.url) elif self.vm_type == 'libvirt': logging.info("Not downloading vmlinuz/initrd.img from %s, " "letting virt-install do it instead") else: logging.info("No action defined/needed for the current virt " "type: '%s'" % self.vm_type)
[docs] 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, verbose=DEBUG) 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, verbose=DEBUG) 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, verbose=DEBUG) finally: utils_disk.cleanup(self.nfs_mount) if 'autoyast=' in self.kernel_params: # SUSE self.kernel_params = (self.kernel_params + " ip=dhcp " "install=nfs://" + self.nfs_server + ":" + self.nfs_dir)
[docs] def setup_import(self): self.unattended_file = None self.kernel_params = None
[docs] def setup(self): """ Configure the environment for unattended install. Uses an appropriate strategy according to each install model. """ logging.info("Starting unattended install setup") if DEBUG: utils_misc.display_attributes(self) if self.syslog_server_enabled == 'yes': start_syslog_server_thread(self.syslog_server_ip, self.syslog_server_port, self.syslog_server_tcp) 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() elif self.medium == "import": self.setup_import() else: raise ValueError("Unexpected installation method %s" % self.medium) if self.unattended_file and (self.floppy or self.cdrom_unattended): self.setup_boot_disk() if self.params.get("store_boot_disk") == "yes": logging.info("Sotre the boot disk to result directory for" " further debug") src_dir = self.floppy or self.cdrom_unattended dst_dir = self.results_dir shutil.copy(src_dir, dst_dir) # Update params dictionary as some of the values could be updated for a in self.attributes: self.params[a] = getattr(self, a)
[docs]def start_syslog_server_thread(address, port, tcp): global _syslog_server_thread global _syslog_server_thread_event syslog_server.set_default_format('[UnattendedSyslog ' '(%s.%s)] %s') if _syslog_server_thread is None: _syslog_server_thread_event = threading.Event() _syslog_server_thread = threading.Thread( target=syslog_server.syslog_server, args=(address, port, tcp, terminate_syslog_server_thread)) _syslog_server_thread.start()
[docs]def terminate_syslog_server_thread(): global _syslog_server_thread, _syslog_server_thread_event if _syslog_server_thread is None: return False if _syslog_server_thread_event is None: return False if _syslog_server_thread_event.isSet(): return True return False
[docs]def copy_file_from_nfs(src, dst, mount_point, image_name): logging.info("Test failed before the install process start." " So just copy a good image from nfs for following tests.") utils_misc.mount(src, mount_point, "nfs", perm="ro") image_src = utils_misc.get_path(mount_point, image_name) shutil.copy(image_src, dst) utils_misc.umount(src, mount_point, "nfs")
@error.context_aware def run(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: QEMU test object. :param params: Dictionary with the test parameters. :param env: Dictionary with test environment. """ @error.context_aware def copy_images(): error.base_context("Copy image from NFS after installation failure") image_copy_on_error = params.get("image_copy_on_error", "no") if image_copy_on_error == "yes": logging.info("Running image_copy to copy pristine image from NFS.") try: error.context("Quit qemu-kvm before copying guest image") vm.monitor.quit() except Exception, e: logging.warn(e) from virttest import utils_test error.context("Copy image from NFS Server") utils_test.run_image_copy(test, params, env) src = params.get('images_good') base_dir = params.get("images_base_dir", data_dir.get_data_dir()) dst = storage.get_image_filename(params, base_dir) if params.get("storage_type") == "iscsi": dd_cmd = "dd if=/dev/zero of=%s bs=1M count=1" % dst txt = "iscsi used, need destroy data in %s" % dst txt += " by command: %s" % dd_cmd logging.info(txt) utils.system(dd_cmd) image_name = os.path.basename(dst) mount_point = params.get("dst_dir") if mount_point and src: funcatexit.register(env, params.get("type"), copy_file_from_nfs, src, dst, mount_point, image_name) vm = env.get_vm(params["main_vm"]) local_dir = params.get("local_dir") if local_dir: local_dir = utils_misc.get_path(test.bindir, local_dir) else: local_dir = test.bindir if params.get("copy_to_local"): for param in params.get("copy_to_local").split(): l_value = params.get(param) if l_value: need_copy = True nfs_link = utils_misc.get_path(test.bindir, l_value) i_name = os.path.basename(l_value) local_link = os.path.join(local_dir, i_name) if os.path.isfile(local_link): file_hash = utils.hash_file(local_link, "md5") expected_hash = utils.hash_file(nfs_link, "md5") if file_hash == expected_hash: need_copy = False if need_copy: msg = "Copy %s to %s in local host." % (i_name, local_link) error.context(msg, logging.info) utils.get_file(nfs_link, local_link) params[param] = local_link unattended_install_config = UnattendedInstallConfig(test, params, vm) unattended_install_config.setup() # params passed explicitly, because they may have been updated by # unattended install config code, such as when params['url'] == auto vm.create(params=params) post_finish_str = params.get("post_finish_str", "Post set up finished") install_timeout = int(params.get("install_timeout", 4800)) 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() try: serial_name = vm.serial_ports[0] except IndexError: raise virt_vm.VMConfigMissingError(vm.name, "serial") log_file = utils_misc.get_path(test.debugdir, "serial-%s-%s.log" % (serial_name, vm.name)) logging.debug("Monitoring serial console log for completion message: %s", log_file) serial_log_msg = "" serial_read_fails = 0 # As the the install process start. We may need collect informations from # the image. So use the test case instead this simple function in the # following code. if mount_point and src: funcatexit.unregister(env, params.get("type"), copy_file_from_nfs, src, dst, mount_point, image_name) send_key_timeout = int(params.get("send_key_timeout", 60)) while (time.time() - start_time) < install_timeout: try: vm.verify_alive() if (params.get("send_key_at_install") and (time.time() - start_time) < send_key_timeout): vm.send_key(params.get("send_key_at_install")) # Due to a race condition, sometimes we might get a MonitorError # before the VM gracefully shuts down, so let's capture MonitorErrors. except (virt_vm.VMDeadError, qemu_monitor.MonitorError), e: if params.get("wait_no_ack", "no") == "yes": break else: # Print out the original exception before copying images. logging.error(e) copy_images() raise e try: test.verify_background_errors() except Exception, e: copy_images() raise e # To ignore the try:except:finally problem in old version of python try: serial_log_msg = open(log_file, 'r').read() except IOError: # Only make noise after several failed reads serial_read_fails += 1 if serial_read_fails > 10: logging.warn("Can not read from serial log file after %d tries", serial_read_fails) if (params.get("wait_no_ack", "no") == "no" and (post_finish_str in serial_log_msg)): break # Due to libvirt automatically start guest after import # we only need to wait for successful login. if params.get("medium") == "import": try: vm.login() break except (remote.LoginError, Exception), e: pass if migrate_background: vm.migrate(timeout=mig_timeout, protocol=mig_protocol) else: time.sleep(1) else: logging.warn("Timeout elapsed while waiting for install to finish ") copy_images() raise error.TestFail("Timeout elapsed while waiting for install to " "finish") logging.debug('cleaning up threads and mounts that may be active') global _url_auto_content_server_thread global _url_auto_content_server_thread_event if _url_auto_content_server_thread is not None: _url_auto_content_server_thread_event.set() _url_auto_content_server_thread.join(3) _url_auto_content_server_thread = None utils_disk.cleanup(unattended_install_config.cdrom_cd1_mount) global _unattended_server_thread global _unattended_server_thread_event if _unattended_server_thread is not None: _unattended_server_thread_event.set() _unattended_server_thread.join(3) _unattended_server_thread = None global _syslog_server_thread global _syslog_server_thread_event if _syslog_server_thread is not None: _syslog_server_thread_event.set() _syslog_server_thread.join(3) _syslog_server_thread = None 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 params.get("medium", "cdrom") == "import": vm.shutdown() try: if utils_misc.wait_for(vm.is_dead, shutdown_cleanly_timeout, 1, 1): logging.info("Guest managed to shutdown cleanly") except qemu_monitor.MonitorError, e: logging.warning("Guest apparently shut down, but got a " "monitor error: %s", e)