diff --git a/image_creator/os_type/windows/__init__.py b/image_creator/os_type/windows/__init__.py index 377694b75c09c78f16b33c49c9686a3c5602441f..4928c8ed36b32ceccbb160f0e0e589788eb002c6 100644 --- a/image_creator/os_type/windows/__init__.py +++ b/image_creator/os_type/windows/__init__.py @@ -20,14 +20,13 @@ Windows OSs.""" from image_creator.os_type import OSBase, sysprep, add_sysprep_param from image_creator.util import FatalError -from image_creator.os_type.windows.vm import VM +from image_creator.os_type.windows.vm import VM, RANDOM_TOKEN as TOKEN from image_creator.os_type.windows.registry import Registry from image_creator.os_type.windows.winexe import WinEXE -from image_creator.os_type.windows.powershell import DRVINST, SAFEBOOT +from image_creator.os_type.windows.powershell import DRVINST_HEAD, SAFEBOOT, \ + DRVINST_TAIL import tempfile -import random -import string import re import os @@ -77,8 +76,7 @@ KMS_CLIENT_SETUP_KEYS = { "Windows Server 2008 Standard": "TM24T-X9RMF-VWXK6-X8JC9-BFGM2", "Windows Server 2008 Standard without Hyper-V": "W7VD6-7JFBR-RX26B-YKQ3Y-6FFFJ", - "Windows Server 2008 Enterprise": - "YQGMW-MPWTJ-34KDK-48M3W-X4Q6V", + "Windows Server 2008 Enterprise": "YQGMW-MPWTJ-34KDK-48M3W-X4Q6V", "Windows Server 2008 Enterprise without Hyper-V": "39BXF-X8Q23-P2WWT-38T2F-G3FPG", "Windows Server 2008 HPC": "RCTX3-KWVHP-BR6TB-RB6DM-6X7HP", @@ -128,7 +126,9 @@ DESCR = { "smp": "Number of CPUs to use for the Windows customization VM.", "mem": "Virtual RAM size in MiB for the Windows customization VM.", "admin": "Name of the Administration user.", - "virtio": "Directory hosting the Windows virtio drivers."} + "virtio": "Directory hosting the Windows virtio drivers.", + "virtio_timeout": "Time in seconds to wait for the installation of the " + "VirtIO drivers."} class Windows(OSBase): @@ -142,6 +142,8 @@ class Windows(OSBase): 'shutdown_timeout', "posint", 120, DESCR['shutdown_timeout']) @add_sysprep_param('boot_timeout', "posint", 300, DESCR['boot_timeout']) @add_sysprep_param('virtio', 'dir', "", DESCR['virtio'], virtio_dir_check) + @add_sysprep_param( + 'virtio_timeout', 'posint', 300, DESCR['virtio_timeout']) def __init__(self, image, **kargs): super(Windows, self).__init__(image, **kargs) @@ -375,7 +377,7 @@ class Windows(OSBase): with self.mount(readonly=False, silent=True): v_val = self.registry.reset_passwd(admin) disabled_uac = self.registry.update_uac_remote_setting(1) - token = self._add_boot_scripts() + self._add_boot_scripts() # disable the firewalls firewall_states = self.registry.update_firewalls(0, 0, 0) @@ -398,7 +400,7 @@ class Windows(OSBase): self.vm.display) self.out.output("Waiting for OS to boot ...", False) - if not self.vm.wait_on_serial(token, timeout): + if not self.vm.wait_on_serial(timeout): raise FatalError("Windows VM booting timed out!") self.out.success('done') @@ -485,16 +487,14 @@ class Windows(OSBase): def _add_boot_scripts(self): """Add various scripts in the registry that will be executed during the - next boot. It returns a random string that will be send to the serial - port of the VM when the OS boots. + next boot. """ commands = {} # This script will send a random string to the first serial port. This # can be used to determine when the OS has booted. - token = "".join(random.choice(string.ascii_letters) for x in range(16)) - commands['BootMonitor'] = "cmd /q /a /c echo " + token + " > COM1" + commands['BootMonitor'] = "cmd /q /a /c echo " + TOKEN + " > COM1" # This will update the password of the admin user to self.vm.password commands["UpdatePassword"] = "net user %s %s" % \ @@ -531,8 +531,6 @@ class Windows(OSBase): self.registry.enable_autologon(self.sysprep_params['admin'].value) - return token - def _do_collect_metadata(self): """Collect metadata about the OS""" super(Windows, self)._do_collect_metadata() @@ -613,7 +611,7 @@ class Windows(OSBase): self.registry.enable_autologon(admin) self._upload_virtio_drivers(dirname) - drvs_install = DRVINST.replace('\n', '\r\n') + drvs_install = DRVINST_HEAD.replace('\n', '\r\n') if self.check_version(6, 1) <= 0: self._install_viostor_driver(dirname) @@ -623,7 +621,7 @@ class Windows(OSBase): # need to reboot in safe mode. drvs_install += SAFEBOOT.replace('\n', '\r\n') - drvs_install += "\r\nshutdown /s /t 0\r\n" + drvs_install += DRVINST_TAIL.replace('\n', '\r\n') remotedir = self.image.g.case_sensitive_path("%s/VirtIO" % self.systemroot) @@ -641,6 +639,9 @@ class Windows(OSBase): # (*) to force the program to run even in Safe mode. self.registry.runonce({'*InstallDrivers': cmd}) + timeout = self.sysprep_params['boot_timeout'].value + shutdown_timeout = self.sysprep_params['shutdown_timeout'].value + virtio_timeout = self.sysprep_params['virtio_timeout'].value try: if self.check_version(6, 1) <= 0: self.vm.start() @@ -650,19 +651,38 @@ class Windows(OSBase): self.vm.interface = 'virtio' self.out.success("started (console on VNC display: %d)" % - self.vm.display) + self.vm.display) + self.out.output("Wait while os booting...", False) + if not self.vm.wait_on_serial(timeout): + raise FatalError("Windows VM booting timed out!") + self.out.success('done') + self.out.output("wait while drivers installing...", False) + if not self.vm.wait_on_serial(virtio_timeout): + raise FatalError("Windows VirtIO installation timed out!") + self.out.success('done') + self.out.output('wait while shutting down....', False) + self.vm.wait(shutdown_timeout) + self.out.success('done') finally: - self.vm.stop(6000) + self.vm.stop(1, fatal=False) + + with self.mount(readonly=True, silent=True): + self.virtio_state = self._virtio_state() + viostor_service_found = self.registry.check_viostor_service() + + if not (len(self.virtio_state['viostor']) and viostor_service_found): + raise FatalError("viostor was not successfully installed") if self.check_version(6, 1) > 0: - # Hopefully restart in safe mode. + # Hopefully restart in safe mode. Newer windows will not boot from + # a viostor device unless we initially start them in safe mode try: + self.out.output('Restarting Windows VM in safe mode...', False) self.vm.start() + self.vm.wait(timeout + shutdown_timeout) + self.out.success('done') finally: - self.vm.stop(6000) - - with self.mount(readonly=True, silent=True): - self.virtio_state = self._virtio_state() + self.vm.stop(1, fatal=False) def _install_viostor_driver(self, dirname): """Quick and dirty installation of the VirtIO SCSI controller driver. diff --git a/image_creator/os_type/windows/powershell.py b/image_creator/os_type/windows/powershell.py index 958b6f25de061ceadafc498e6547aa4e9cec9de8..909fbfca3349723d4ef50bf157bb09c9ac2482ac 100644 --- a/image_creator/os_type/windows/powershell.py +++ b/image_creator/os_type/windows/powershell.py @@ -18,8 +18,19 @@ """This module hosts Windows PowerShell scripts that need to be injected into the windows image""" +# Just a random 16 character long token +from image_creator.os_type.windows.vm import RANDOM_TOKEN + +COM1_WRITE = r""" +$port=new-Object System.IO.Ports.SerialPort COM1,9600,None,8,one +$port.open() +$port.WriteLine('""" + RANDOM_TOKEN + """') +$port.Close() + +""" + # Installs drivers found in a directory -DRVINST = r""" +DRVINST_HEAD = r""" #requires -version 2 Param([string]$dirName=$(throw "You need to provide a directory name")) @@ -29,6 +40,8 @@ if (!(Test-Path -PathType Container "$dirName")) { Exit } +""" + COM1_WRITE + """ + foreach ($file in Get-ChildItem "$dirName" -Filter *.cat) { $cert = (Get-AuthenticodeSignature $file.FullName).SignerCertificate $certFile = $dirName + "\" + $file.BaseName + ".cer" @@ -44,6 +57,12 @@ pnputil.exe -a "$dirname\*.inf" """ +DRVINST_TAIL = COM1_WRITE + """ + +shutdown /s /t 0 + +""" + # Reboots system in safe mode SAFEBOOT = r""" bcdedit /set safeboot minimal diff --git a/image_creator/os_type/windows/registry.py b/image_creator/os_type/windows/registry.py index 0add0a1ab986441eb0dbca312339de85df37ce5f..2242cb1c210c4d9f6ca443ae588416d524575490 100644 --- a/image_creator/os_type/windows/registry.py +++ b/image_creator/os_type/windows/registry.py @@ -40,6 +40,7 @@ REG_EXPAND_SZ = lambda k, v: {'key': k, 't': 2L, REG_BINARY = lambda k, v: {'key': k, 't': 3L, 'value': v} REG_DWORD = lambda k, v: {'key': k, 't': 4L, 'value': struct.pack('<I', v)} + def safe_add_node(hive, parent, name): """Add a registry node only if it is not present""" @@ -400,7 +401,6 @@ class Registry(object): hive.node_set_value(node, REG_SZ('ClassGUID', guid)) hive.node_set_value(node, REG_SZ('Service', 'viostor')) - # SYSTEM/CurrentContolSet/Services/viostor services = hive.root() for child in (self.current_control_set, 'Services'): @@ -437,6 +437,17 @@ class Registry(object): hive.commit(None) + def check_viostor_service(self): + """Checks if the viostor service is installed""" + with self.open_hive('SYSTEM', write=False) as hive: + + # SYSTEM/CurrentContolSet/Services/viostor + services = hive.root() + for child in (self.current_control_set, 'Services'): + services = hive.node_get_child(services, child) + + return hive.node_get_child(services, 'viostor') is not None + def update_devices_dirs(self, dirname, append=True): """Update the value of the DevicePath registry key. If the append flag is True, the dirname is appended to the list of devices directories, diff --git a/image_creator/os_type/windows/vm.py b/image_creator/os_type/windows/vm.py index 7e3fd44c96e2d244ca8368f0d27f921aa345033f..5f2cc31b54b302c87ee8ed6209b4c072cab19ccf 100644 --- a/image_creator/os_type/windows/vm.py +++ b/image_creator/os_type/windows/vm.py @@ -28,6 +28,9 @@ from string import lowercase, uppercase, digits from image_creator.util import FatalError, get_kvm_binary from image_creator.os_type.windows.winexe import WinEXE, WinexeTimeout +# Just a random 16 character long token +RANDOM_TOKEN = "".join(random.choice(lowercase + uppercase) for _ in range(16)) + class VM(object): """Windows Virtual Machine""" @@ -38,6 +41,9 @@ class VM(object): self.params = params self.interface = 'virtio' + # expected number of token occurrences in serial port + self._ntokens = 0 + kvm, needed_args = get_kvm_binary() if kvm is None: FatalError("Can't find the kvm binary") @@ -97,6 +103,8 @@ class VM(object): def start(self, **kwargs): """Start the windows VM""" + self._ntokens = 0 + args = [] args.extend(self.kvm) @@ -161,15 +169,20 @@ class VM(object): except FileNotFoundError: pass - def wait_on_serial(self, msg, timeout): - """Wait until a message appears on the VM's serial port""" + def wait_on_serial(self, timeout): + """Wait until the random token appears on the VM's serial port""" + + self._ntokens += 1 for _ in xrange(timeout): time.sleep(1) with open(self.serial) as f: + current = 0 for line in f: - if line.startswith(msg): - return True + if line.startswith(RANDOM_TOKEN): + current += 1 + if current == self._ntokens: + return True if not self.isalive(): raise FatalError("Windows VM died unexpectedly!")