Statistics
| Branch: | Tag: | Revision:

root / image_creator / os_type / windows.py @ d4270a48

History | View | Annotate | Download (22.4 kB)

1 121f3bc0 Nikos Skalkotos
# -*- coding: utf-8 -*-
2 121f3bc0 Nikos Skalkotos
#
3 ae48a082 Nikos Skalkotos
# Copyright 2012 GRNET S.A. All rights reserved.
4 ae48a082 Nikos Skalkotos
#
5 ae48a082 Nikos Skalkotos
# Redistribution and use in source and binary forms, with or
6 ae48a082 Nikos Skalkotos
# without modification, are permitted provided that the following
7 ae48a082 Nikos Skalkotos
# conditions are met:
8 ae48a082 Nikos Skalkotos
#
9 ae48a082 Nikos Skalkotos
#   1. Redistributions of source code must retain the above
10 ae48a082 Nikos Skalkotos
#      copyright notice, this list of conditions and the following
11 ae48a082 Nikos Skalkotos
#      disclaimer.
12 ae48a082 Nikos Skalkotos
#
13 ae48a082 Nikos Skalkotos
#   2. Redistributions in binary form must reproduce the above
14 ae48a082 Nikos Skalkotos
#      copyright notice, this list of conditions and the following
15 ae48a082 Nikos Skalkotos
#      disclaimer in the documentation and/or other materials
16 ae48a082 Nikos Skalkotos
#      provided with the distribution.
17 ae48a082 Nikos Skalkotos
#
18 ae48a082 Nikos Skalkotos
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
19 ae48a082 Nikos Skalkotos
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 ae48a082 Nikos Skalkotos
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 ae48a082 Nikos Skalkotos
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
22 ae48a082 Nikos Skalkotos
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 ae48a082 Nikos Skalkotos
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 ae48a082 Nikos Skalkotos
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
25 ae48a082 Nikos Skalkotos
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 ae48a082 Nikos Skalkotos
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 ae48a082 Nikos Skalkotos
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28 ae48a082 Nikos Skalkotos
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 ae48a082 Nikos Skalkotos
# POSSIBILITY OF SUCH DAMAGE.
30 ae48a082 Nikos Skalkotos
#
31 ae48a082 Nikos Skalkotos
# The views and conclusions contained in the software and
32 ae48a082 Nikos Skalkotos
# documentation are those of the authors and should not be
33 ae48a082 Nikos Skalkotos
# interpreted as representing official policies, either expressed
34 ae48a082 Nikos Skalkotos
# or implied, of GRNET S.A.
35 ae48a082 Nikos Skalkotos
36 121f3bc0 Nikos Skalkotos
"""This module hosts OS-specific code common for the various Microsoft
37 121f3bc0 Nikos Skalkotos
Windows OSs."""
38 121f3bc0 Nikos Skalkotos
39 640096fb Nikos Skalkotos
from image_creator.os_type import OSBase, sysprep
40 9ca717c4 Nikos Skalkotos
from image_creator.util import FatalError, check_guestfs_version, get_command
41 aa2062ba Nikos Skalkotos
42 76b200cf Nikos Skalkotos
import hivex
43 76b200cf Nikos Skalkotos
import tempfile
44 76b200cf Nikos Skalkotos
import os
45 9ca717c4 Nikos Skalkotos
import time
46 9ca717c4 Nikos Skalkotos
import random
47 10088af7 Nikos Skalkotos
import string
48 74780ad6 Nikos Skalkotos
import subprocess
49 85e97ae0 Nikos Skalkotos
import struct
50 9ca717c4 Nikos Skalkotos
51 9ca717c4 Nikos Skalkotos
kvm = get_command('kvm')
52 76b200cf Nikos Skalkotos
53 10088af7 Nikos Skalkotos
BOOT_TIMEOUT = 300
54 10088af7 Nikos Skalkotos
55 8c574358 Nikos Skalkotos
56 aa2062ba Nikos Skalkotos
class Windows(OSBase):
57 88f83027 Nikos Skalkotos
    """OS class for Windows"""
58 efa7a61b Nikos Skalkotos
    def __init__(self, image, **kargs):
59 efa7a61b Nikos Skalkotos
        super(Windows, self).__init__(image, **kargs)
60 efa7a61b Nikos Skalkotos
61 efa7a61b Nikos Skalkotos
        device = self.g.part_to_dev(self.root)
62 efa7a61b Nikos Skalkotos
63 efa7a61b Nikos Skalkotos
        self.last_part_num = self.g.part_list(device)[-1]['part_num']
64 efa7a61b Nikos Skalkotos
        self.last_drive = None
65 efa7a61b Nikos Skalkotos
        self.system_drive = None
66 efa7a61b Nikos Skalkotos
67 efa7a61b Nikos Skalkotos
        for drive, partition in self.g.inspect_get_drive_mappings(self.root):
68 efa7a61b Nikos Skalkotos
            if partition == "%s%d" % (device, self.last_part_num):
69 efa7a61b Nikos Skalkotos
                self.last_drive = drive
70 efa7a61b Nikos Skalkotos
            if partition == self.root:
71 efa7a61b Nikos Skalkotos
                self.system_drive = drive
72 efa7a61b Nikos Skalkotos
73 efa7a61b Nikos Skalkotos
        assert self.system_drive
74 76b200cf Nikos Skalkotos
75 74780ad6 Nikos Skalkotos
    def needed_sysprep_params(self):
76 74780ad6 Nikos Skalkotos
        """Returns a list of needed sysprep parameters. Each element in the
77 74780ad6 Nikos Skalkotos
        list is a SysprepParam object.
78 74780ad6 Nikos Skalkotos
        """
79 74780ad6 Nikos Skalkotos
        password = self.SysprepParam(
80 74780ad6 Nikos Skalkotos
            'password', 'Image Administrator Password', 20, lambda x: True)
81 74780ad6 Nikos Skalkotos
82 74780ad6 Nikos Skalkotos
        return [password]
83 74780ad6 Nikos Skalkotos
84 b8d4b14a Nikos Skalkotos
    @sysprep('Disabling IPv6 privacy extensions')
85 b8d4b14a Nikos Skalkotos
    def disable_ipv6_privacy_extensions(self):
86 74780ad6 Nikos Skalkotos
        """Disable IPv6 privacy extensions"""
87 74780ad6 Nikos Skalkotos
88 10088af7 Nikos Skalkotos
        self._guest_exec('netsh interface ipv6 set global '
89 10088af7 Nikos Skalkotos
                         'randomizeidentifiers=disabled store=persistent')
90 74780ad6 Nikos Skalkotos
91 b9e14923 Nikos Skalkotos
    @sysprep('Disabling Teredo interface')
92 b9e14923 Nikos Skalkotos
    def disable_teredo(self):
93 b9e14923 Nikos Skalkotos
        """Disable Teredo interface"""
94 b9e14923 Nikos Skalkotos
95 b9e14923 Nikos Skalkotos
        self._guest_exec('netsh interface teredo set state disabled')
96 b9e14923 Nikos Skalkotos
97 b9e14923 Nikos Skalkotos
    @sysprep('Disabling ISATAP Adapters')
98 b9e14923 Nikos Skalkotos
    def disable_isatap(self):
99 b9e14923 Nikos Skalkotos
        """Disable ISATAP Adapters"""
100 b9e14923 Nikos Skalkotos
101 b9e14923 Nikos Skalkotos
        self._guest_exec('netsh interface isa set state disabled')
102 b9e14923 Nikos Skalkotos
103 b9e14923 Nikos Skalkotos
    @sysprep('Enabling ping responses')
104 b9e14923 Nikos Skalkotos
    def enable_pings(self):
105 b9e14923 Nikos Skalkotos
        """Enable ping responces"""
106 b9e14923 Nikos Skalkotos
107 b9e14923 Nikos Skalkotos
        self._guest_exec('netsh firewall set icmpsetting 8')
108 b9e14923 Nikos Skalkotos
109 811b7149 Nikos Skalkotos
    @sysprep('Disabling hibernation support')
110 811b7149 Nikos Skalkotos
    def disable_hibernation(self):
111 811b7149 Nikos Skalkotos
        """Disable hibernation support and remove the hibernation file"""
112 811b7149 Nikos Skalkotos
113 811b7149 Nikos Skalkotos
        self._guest_exec(r'powercfg.exe /hibernate off')
114 811b7149 Nikos Skalkotos
115 b9e14923 Nikos Skalkotos
    @sysprep('Setting the system clock to UTC')
116 b9e14923 Nikos Skalkotos
    def utc(self):
117 b9e14923 Nikos Skalkotos
        """Set the hardware clock to UTC"""
118 b9e14923 Nikos Skalkotos
119 b9e14923 Nikos Skalkotos
        path = r'HKLM\SYSTEM\CurrentControlSet\Control\TimeZoneInformation'
120 b9e14923 Nikos Skalkotos
        self._guest_exec(
121 b9e14923 Nikos Skalkotos
            r'REG ADD %s /v RealTimeIsUniversal /t REG_DWORD /d 1 /f' % path)
122 b9e14923 Nikos Skalkotos
123 2075a76a Nikos Skalkotos
    @sysprep('Clearing the event logs')
124 2075a76a Nikos Skalkotos
    def clear_logs(self):
125 2075a76a Nikos Skalkotos
        """Clear all the event logs"""
126 2075a76a Nikos Skalkotos
127 2075a76a Nikos Skalkotos
        self._guest_exec(
128 2075a76a Nikos Skalkotos
            r"cmd /q /c for /f %l in ('wevtutil el') do wevtutil cl %l")
129 2075a76a Nikos Skalkotos
130 b8d4b14a Nikos Skalkotos
    @sysprep('Executing sysprep on the image (may take more that 10 minutes)')
131 b8d4b14a Nikos Skalkotos
    def microsoft_sysprep(self):
132 b9e14923 Nikos Skalkotos
        """Run the Microsoft System Preparation Tool. This will remove
133 b9e14923 Nikos Skalkotos
        system-specific data and will make the image ready to be deployed.
134 b9e14923 Nikos Skalkotos
        After this no other task may run.
135 74780ad6 Nikos Skalkotos
        """
136 74780ad6 Nikos Skalkotos
137 10088af7 Nikos Skalkotos
        self._guest_exec(r'C:\Windows\system32\sysprep\sysprep '
138 10088af7 Nikos Skalkotos
                         r'/quiet /generalize /oobe /shutdown')
139 74780ad6 Nikos Skalkotos
        self.syspreped = True
140 9ca717c4 Nikos Skalkotos
141 efa7a61b Nikos Skalkotos
    @sysprep('Shrinking the last filesystem')
142 efa7a61b Nikos Skalkotos
    def shrink(self):
143 efa7a61b Nikos Skalkotos
        """Shrink the last filesystem. Make sure the filesystem is defragged"""
144 d4270a48 Nikos Skalkotos
145 d4270a48 Nikos Skalkotos
        # Query for the maximum number of reclaimable bytes
146 d4270a48 Nikos Skalkotos
        cmd = (
147 d4270a48 Nikos Skalkotos
            r'cmd /Q /C "SET SCRIPT=%TEMP%\QUERYMAX_%RANDOM%.TXT & ' +
148 d4270a48 Nikos Skalkotos
            r'ECHO SELECT DISK 0 > %SCRIPT% & ' +
149 d4270a48 Nikos Skalkotos
            'ECHO SELECT PARTITION %d >> %%SCRIPT%% & ' % self.last_part_num +
150 d4270a48 Nikos Skalkotos
            r'ECHO SHRINK QUERYMAX >> %SCRIPT% & ' +
151 d4270a48 Nikos Skalkotos
            r'ECHO EXIT >> %SCRIPT% & ' +
152 d4270a48 Nikos Skalkotos
            r'DISKPART /S %SCRIPT% & ' +
153 d4270a48 Nikos Skalkotos
            r'IF ERRORLEVEL 1 EXIT /B 1 & ' +
154 d4270a48 Nikos Skalkotos
            r'DEL /Q %SCRIPT%"')
155 d4270a48 Nikos Skalkotos
156 d4270a48 Nikos Skalkotos
        stdout, stderr, rc = self._guest_exec(cmd)
157 d4270a48 Nikos Skalkotos
158 d4270a48 Nikos Skalkotos
        querymax = None
159 d4270a48 Nikos Skalkotos
        for line in stdout.splitlines():
160 d4270a48 Nikos Skalkotos
            # diskpart will return something like this:
161 d4270a48 Nikos Skalkotos
            #
162 d4270a48 Nikos Skalkotos
            #   The maximum number of reclaimable bytes is: xxxx MB
163 d4270a48 Nikos Skalkotos
            #
164 d4270a48 Nikos Skalkotos
            if line.find('reclaimable') >= 0:
165 d4270a48 Nikos Skalkotos
                querymax = line.split(':')[1].split()[0].strip()
166 d4270a48 Nikos Skalkotos
                assert querymax.isdigit(), \
167 d4270a48 Nikos Skalkotos
                    "Number of reclaimable bytes not a number"
168 d4270a48 Nikos Skalkotos
169 d4270a48 Nikos Skalkotos
        if querymax is None:
170 d4270a48 Nikos Skalkotos
            FatalError("Error in shrinking! "
171 d4270a48 Nikos Skalkotos
                       "Couldn't find the max number of reclaimable bytes!")
172 d4270a48 Nikos Skalkotos
173 d4270a48 Nikos Skalkotos
        querymax = int(querymax)
174 d4270a48 Nikos Skalkotos
        # From ntfsresize:
175 d4270a48 Nikos Skalkotos
        # Practically the smallest shrunken size generally is at around
176 d4270a48 Nikos Skalkotos
        # "used space" + (20-200 MB). Please also take into account that
177 d4270a48 Nikos Skalkotos
        # Windows might need about 50-100 MB free space left to boot safely.
178 d4270a48 Nikos Skalkotos
        # I'll give 100MB extra space just to be sure
179 d4270a48 Nikos Skalkotos
        querymax -= 100
180 d4270a48 Nikos Skalkotos
181 d4270a48 Nikos Skalkotos
        if querymax < 0:
182 d4270a48 Nikos Skalkotos
            self.out.warn("Not enought available space to shrink the image!")
183 d4270a48 Nikos Skalkotos
            return
184 d4270a48 Nikos Skalkotos
185 d4270a48 Nikos Skalkotos
        cmd = (
186 d4270a48 Nikos Skalkotos
            r'cmd /Q /C "SET SCRIPT=%TEMP%\QUERYMAX_%RANDOM%.TXT & ' +
187 d4270a48 Nikos Skalkotos
            r'ECHO SELECT DISK 0 > %SCRIPT% & ' +
188 d4270a48 Nikos Skalkotos
            'ECHO SELECT PARTITION %d >> %%SCRIPT%% & ' % self.last_part_num +
189 d4270a48 Nikos Skalkotos
            'ECHO SHRINK DESIRED=%d >> %%SCRIPT%% & ' % querymax +
190 d4270a48 Nikos Skalkotos
            r'ECHO EXIT >> %SCRIPT% & ' +
191 d4270a48 Nikos Skalkotos
            r'DISKPART /S %SCRIPT% & ' +
192 d4270a48 Nikos Skalkotos
            r'IF ERRORLEVEL 1 EXIT /B 1 & ' +
193 d4270a48 Nikos Skalkotos
            r'DEL /Q %SCRIPT%"')
194 d4270a48 Nikos Skalkotos
195 d4270a48 Nikos Skalkotos
        stdout, stderr, rc = self._guest_exec(cmd)
196 d4270a48 Nikos Skalkotos
197 d4270a48 Nikos Skalkotos
        for line in stdout.splitlines():
198 d4270a48 Nikos Skalkotos
            if line.find('shrunk') >= 0:
199 d4270a48 Nikos Skalkotos
                self.out.output(line)
200 efa7a61b Nikos Skalkotos
201 640096fb Nikos Skalkotos
    def do_sysprep(self):
202 640096fb Nikos Skalkotos
        """Prepare system for image creation."""
203 640096fb Nikos Skalkotos
204 640096fb Nikos Skalkotos
        if getattr(self, 'syspreped', False):
205 640096fb Nikos Skalkotos
            raise FatalError("Image is already syspreped!")
206 640096fb Nikos Skalkotos
207 74780ad6 Nikos Skalkotos
        txt = "System preparation parameter: `%s' is needed but missing!"
208 74780ad6 Nikos Skalkotos
        for param in self.needed_sysprep_params():
209 74780ad6 Nikos Skalkotos
            if param[0] not in self.sysprep_params:
210 74780ad6 Nikos Skalkotos
                raise FatalError(txt % param[0])
211 74780ad6 Nikos Skalkotos
212 640096fb Nikos Skalkotos
        self.mount(readonly=False)
213 640096fb Nikos Skalkotos
        try:
214 640096fb Nikos Skalkotos
            disabled_uac = self._update_uac_remote_setting(1)
215 10088af7 Nikos Skalkotos
            token = self._enable_os_monitor()
216 c2113d72 Nikos Skalkotos
217 c2113d72 Nikos Skalkotos
            # disable the firewalls
218 c2113d72 Nikos Skalkotos
            firewall_states = self._update_firewalls(0, 0, 0)
219 c2113d72 Nikos Skalkotos
220 c2113d72 Nikos Skalkotos
            # Delete the pagefile. It will be recreated when the system boots
221 c2113d72 Nikos Skalkotos
            systemroot = self.g.inspect_get_windows_systemroot(self.root)
222 c2113d72 Nikos Skalkotos
            pagefile = "%s/pagefile.sys" % systemroot
223 c2113d72 Nikos Skalkotos
            self.g.rm_rf(self.g.case_sensitive_path(pagefile))
224 c2113d72 Nikos Skalkotos
225 640096fb Nikos Skalkotos
        finally:
226 640096fb Nikos Skalkotos
            self.umount()
227 640096fb Nikos Skalkotos
228 640096fb Nikos Skalkotos
        self.out.output("Shutting down helper VM ...", False)
229 640096fb Nikos Skalkotos
        self.g.sync()
230 640096fb Nikos Skalkotos
        # guestfs_shutdown which is the prefered way to shutdown the backend
231 640096fb Nikos Skalkotos
        # process was introduced in version 1.19.16
232 640096fb Nikos Skalkotos
        if check_guestfs_version(self.g, 1, 19, 16) >= 0:
233 640096fb Nikos Skalkotos
            ret = self.g.shutdown()
234 640096fb Nikos Skalkotos
        else:
235 640096fb Nikos Skalkotos
            ret = self.g.kill_subprocess()
236 640096fb Nikos Skalkotos
237 640096fb Nikos Skalkotos
        self.out.success('done')
238 10088af7 Nikos Skalkotos
239 10088af7 Nikos Skalkotos
        vm = None
240 10088af7 Nikos Skalkotos
        monitor = None
241 640096fb Nikos Skalkotos
        try:
242 9ca717c4 Nikos Skalkotos
            self.out.output("Starting windows VM ...", False)
243 10088af7 Nikos Skalkotos
            monitorfd, monitor = tempfile.mkstemp()
244 10088af7 Nikos Skalkotos
            os.close(monitorfd)
245 10088af7 Nikos Skalkotos
            vm, display = self._create_vm(monitor)
246 10088af7 Nikos Skalkotos
            self.out.success("started (console on vnc display: %d)." % display)
247 10088af7 Nikos Skalkotos
248 10088af7 Nikos Skalkotos
            self.out.output("Waiting for OS to boot ...", False)
249 10088af7 Nikos Skalkotos
            if not self._wait_on_file(monitor, token):
250 10088af7 Nikos Skalkotos
                raise FatalError("Windows booting timed out.")
251 10088af7 Nikos Skalkotos
            else:
252 10088af7 Nikos Skalkotos
                self.out.success('done')
253 10088af7 Nikos Skalkotos
254 efa7a61b Nikos Skalkotos
            time.sleep(5)  # Just to be sure everything is up
255 efa7a61b Nikos Skalkotos
256 10088af7 Nikos Skalkotos
            self.out.output("Disabling automatic logon ...", False)
257 10088af7 Nikos Skalkotos
            self._disable_autologon()
258 9ca717c4 Nikos Skalkotos
            self.out.success('done')
259 74780ad6 Nikos Skalkotos
260 10088af7 Nikos Skalkotos
            self.out.output('Preparing system from image creation:')
261 10088af7 Nikos Skalkotos
262 74780ad6 Nikos Skalkotos
            tasks = self.list_syspreps()
263 74780ad6 Nikos Skalkotos
            enabled = filter(lambda x: x.enabled, tasks)
264 74780ad6 Nikos Skalkotos
            size = len(enabled)
265 74780ad6 Nikos Skalkotos
266 efa7a61b Nikos Skalkotos
            # Make sure shrink runs in the end, before ms sysprep
267 efa7a61b Nikos Skalkotos
            enabled = filter(lambda x: self.sysprep_info(x).name != 'shrink',
268 efa7a61b Nikos Skalkotos
                             enabled)
269 efa7a61b Nikos Skalkotos
270 efa7a61b Nikos Skalkotos
            shrink_enabled = False
271 efa7a61b Nikos Skalkotos
            if len(enabled) != size:
272 efa7a61b Nikos Skalkotos
                enabled.append(self.shrink)
273 efa7a61b Nikos Skalkotos
                shrink_enabled = True
274 efa7a61b Nikos Skalkotos
275 74780ad6 Nikos Skalkotos
            # Make sure the ms sysprep is the last task to run if it is enabled
276 74780ad6 Nikos Skalkotos
            enabled = filter(
277 efa7a61b Nikos Skalkotos
                lambda x: self.sysprep_info(x).name != 'microsoft-sysprep',
278 efa7a61b Nikos Skalkotos
                enabled)
279 74780ad6 Nikos Skalkotos
280 74780ad6 Nikos Skalkotos
            ms_sysprep_enabled = False
281 74780ad6 Nikos Skalkotos
            if len(enabled) != size:
282 b9e14923 Nikos Skalkotos
                enabled.append(self.microsoft_sysprep)
283 74780ad6 Nikos Skalkotos
                ms_sysprep_enabled = True
284 74780ad6 Nikos Skalkotos
285 74780ad6 Nikos Skalkotos
            cnt = 0
286 74780ad6 Nikos Skalkotos
            for task in enabled:
287 74780ad6 Nikos Skalkotos
                cnt += 1
288 74780ad6 Nikos Skalkotos
                self.out.output(('(%d/%d)' % (cnt, size)).ljust(7), False)
289 74780ad6 Nikos Skalkotos
                task()
290 74780ad6 Nikos Skalkotos
                setattr(task.im_func, 'executed', True)
291 74780ad6 Nikos Skalkotos
292 c2113d72 Nikos Skalkotos
            self.out.output("Sending shut down command ...", False)
293 74780ad6 Nikos Skalkotos
            if not ms_sysprep_enabled:
294 74780ad6 Nikos Skalkotos
                self._shutdown()
295 10088af7 Nikos Skalkotos
            self.out.success("done")
296 74780ad6 Nikos Skalkotos
297 c2113d72 Nikos Skalkotos
            self.out.output("Waiting for windows to shut down ...", False)
298 9ca717c4 Nikos Skalkotos
            vm.wait()
299 c2113d72 Nikos Skalkotos
            self.out.success("done")
300 640096fb Nikos Skalkotos
        finally:
301 10088af7 Nikos Skalkotos
            if monitor is not None:
302 10088af7 Nikos Skalkotos
                os.unlink(monitor)
303 10088af7 Nikos Skalkotos
304 10088af7 Nikos Skalkotos
            if vm is not None:
305 10088af7 Nikos Skalkotos
                self._destroy_vm(vm)
306 74780ad6 Nikos Skalkotos
307 640096fb Nikos Skalkotos
            self.out.output("Relaunching helper VM (may take a while) ...",
308 640096fb Nikos Skalkotos
                            False)
309 640096fb Nikos Skalkotos
            self.g.launch()
310 640096fb Nikos Skalkotos
            self.out.success('done')
311 640096fb Nikos Skalkotos
312 c2113d72 Nikos Skalkotos
            self.mount(readonly=False)
313 c2113d72 Nikos Skalkotos
            try:
314 c2113d72 Nikos Skalkotos
                if disabled_uac:
315 c2113d72 Nikos Skalkotos
                    self._update_uac_remote_setting(0)
316 c2113d72 Nikos Skalkotos
317 c2113d72 Nikos Skalkotos
                self._update_firewalls(*firewall_states)
318 c2113d72 Nikos Skalkotos
            finally:
319 c2113d72 Nikos Skalkotos
                self.umount()
320 640096fb Nikos Skalkotos
321 10088af7 Nikos Skalkotos
    def _create_vm(self, monitor):
322 10088af7 Nikos Skalkotos
        """Create a VM with the image attached as the disk
323 10088af7 Nikos Skalkotos

324 10088af7 Nikos Skalkotos
            monitor: a file to be used to monitor when the OS is up
325 10088af7 Nikos Skalkotos
        """
326 10088af7 Nikos Skalkotos
327 10088af7 Nikos Skalkotos
        def random_mac():
328 10088af7 Nikos Skalkotos
            mac = [0x00, 0x16, 0x3e,
329 10088af7 Nikos Skalkotos
                   random.randint(0x00, 0x7f),
330 10088af7 Nikos Skalkotos
                   random.randint(0x00, 0xff),
331 10088af7 Nikos Skalkotos
                   random.randint(0x00, 0xff)]
332 10088af7 Nikos Skalkotos
333 10088af7 Nikos Skalkotos
            return ':'.join(map(lambda x: "%02x" % x, mac))
334 10088af7 Nikos Skalkotos
335 10088af7 Nikos Skalkotos
        # Use ganeti's VNC port range for a random vnc port
336 10088af7 Nikos Skalkotos
        vnc_port = random.randint(11000, 14999)
337 10088af7 Nikos Skalkotos
        display = vnc_port - 5900
338 10088af7 Nikos Skalkotos
339 efa7a61b Nikos Skalkotos
        vm = kvm(
340 efa7a61b Nikos Skalkotos
            '-smp', '1', '-m', '1024', '-drive',
341 efa7a61b Nikos Skalkotos
            'file=%s,format=raw,cache=unsafe,if=virtio' % self.image.device,
342 efa7a61b Nikos Skalkotos
            '-netdev', 'type=user,hostfwd=tcp::445-:445,id=netdev0',
343 efa7a61b Nikos Skalkotos
            '-device', 'virtio-net-pci,mac=%s,netdev=netdev0' % random_mac(),
344 efa7a61b Nikos Skalkotos
            '-vnc', ':%d' % display, '-serial', 'file:%s' % monitor, _bg=True)
345 10088af7 Nikos Skalkotos
346 10088af7 Nikos Skalkotos
        return vm, display
347 10088af7 Nikos Skalkotos
348 10088af7 Nikos Skalkotos
    def _destroy_vm(self, vm):
349 10088af7 Nikos Skalkotos
        """Destroy a VM previously created by _create_vm"""
350 10088af7 Nikos Skalkotos
        if vm.process.alive:
351 10088af7 Nikos Skalkotos
            vm.terminate()
352 10088af7 Nikos Skalkotos
353 74780ad6 Nikos Skalkotos
    def _shutdown(self):
354 74780ad6 Nikos Skalkotos
        """Shuts down the windows VM"""
355 10088af7 Nikos Skalkotos
        self._guest_exec(r'shutdown /s /t 5')
356 74780ad6 Nikos Skalkotos
357 10088af7 Nikos Skalkotos
    def _wait_on_file(self, fname, msg):
358 10088af7 Nikos Skalkotos
        """Wait until a message appears on a file"""
359 74780ad6 Nikos Skalkotos
360 10088af7 Nikos Skalkotos
        for i in range(BOOT_TIMEOUT):
361 10088af7 Nikos Skalkotos
            time.sleep(1)
362 10088af7 Nikos Skalkotos
            with open(fname) as f:
363 10088af7 Nikos Skalkotos
                for line in f:
364 10088af7 Nikos Skalkotos
                    if line.startswith(msg):
365 10088af7 Nikos Skalkotos
                        return True
366 10088af7 Nikos Skalkotos
        return False
367 74780ad6 Nikos Skalkotos
368 10088af7 Nikos Skalkotos
    def _disable_autologon(self):
369 10088af7 Nikos Skalkotos
        """Disable automatic logon on the windows image"""
370 10088af7 Nikos Skalkotos
371 10088af7 Nikos Skalkotos
        winlogon = \
372 10088af7 Nikos Skalkotos
            r'"HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon"'
373 10088af7 Nikos Skalkotos
374 10088af7 Nikos Skalkotos
        self._guest_exec('REG DELETE %s /v DefaultUserName /f' % winlogon)
375 10088af7 Nikos Skalkotos
        self._guest_exec('REG DELETE %s /v DefaultPassword /f' % winlogon)
376 10088af7 Nikos Skalkotos
        self._guest_exec('REG DELETE %s /v AutoAdminLogon /f' % winlogon)
377 640096fb Nikos Skalkotos
378 640096fb Nikos Skalkotos
    def _registry_file_path(self, regfile):
379 640096fb Nikos Skalkotos
        """Retrieves the case sensitive path to a registry file"""
380 640096fb Nikos Skalkotos
381 640096fb Nikos Skalkotos
        systemroot = self.g.inspect_get_windows_systemroot(self.root)
382 640096fb Nikos Skalkotos
        path = "%s/system32/config/%s" % (systemroot, regfile)
383 640096fb Nikos Skalkotos
        try:
384 640096fb Nikos Skalkotos
            path = self.g.case_sensitive_path(path)
385 640096fb Nikos Skalkotos
        except RuntimeError as e:
386 640096fb Nikos Skalkotos
            raise FatalError("Unable to retrieve registry file: %s. Reason: %s"
387 640096fb Nikos Skalkotos
                             % (regfile, str(e)))
388 640096fb Nikos Skalkotos
        return path
389 640096fb Nikos Skalkotos
390 10088af7 Nikos Skalkotos
    def _enable_os_monitor(self):
391 10088af7 Nikos Skalkotos
        """Add a script in the registry that will send a random string to the
392 10088af7 Nikos Skalkotos
        first serial port when the windows image finishes booting.
393 10088af7 Nikos Skalkotos
        """
394 10088af7 Nikos Skalkotos
395 10088af7 Nikos Skalkotos
        token = "".join(random.choice(string.ascii_letters) for x in range(16))
396 10088af7 Nikos Skalkotos
397 10088af7 Nikos Skalkotos
        path = self._registry_file_path('SOFTWARE')
398 10088af7 Nikos Skalkotos
        softwarefd, software = tempfile.mkstemp()
399 10088af7 Nikos Skalkotos
        try:
400 10088af7 Nikos Skalkotos
            os.close(softwarefd)
401 10088af7 Nikos Skalkotos
            self.g.download(path, software)
402 10088af7 Nikos Skalkotos
403 10088af7 Nikos Skalkotos
            h = hivex.Hivex(software, write=True)
404 10088af7 Nikos Skalkotos
405 10088af7 Nikos Skalkotos
            # Enable automatic logon.
406 10088af7 Nikos Skalkotos
            # This is needed because we need to execute a script that we add in
407 10088af7 Nikos Skalkotos
            # the RunOnce registry entry and those programs only get executed
408 10088af7 Nikos Skalkotos
            # when a user logs on. There is a RunServicesOnce registry entry
409 10088af7 Nikos Skalkotos
            # whose keys get executed in the background when the logon dialog
410 10088af7 Nikos Skalkotos
            # box first appears, but they seem to only work with services and
411 10088af7 Nikos Skalkotos
            # not arbitrary command line expressions :-(
412 10088af7 Nikos Skalkotos
            #
413 10088af7 Nikos Skalkotos
            # Instructions on how to turn on automatic logon in Windows can be
414 10088af7 Nikos Skalkotos
            # found here: http://support.microsoft.com/kb/324737
415 10088af7 Nikos Skalkotos
            #
416 10088af7 Nikos Skalkotos
            # Warning: Registry change will not work if the โ€œLogon Bannerโ€ is
417 10088af7 Nikos Skalkotos
            # defined on the server either by a Group Policy object (GPO) or by
418 10088af7 Nikos Skalkotos
            # a local policy.
419 10088af7 Nikos Skalkotos
420 10088af7 Nikos Skalkotos
            winlogon = h.root()
421 10088af7 Nikos Skalkotos
            for child in ('Microsoft', 'Windows NT', 'CurrentVersion',
422 10088af7 Nikos Skalkotos
                          'Winlogon'):
423 10088af7 Nikos Skalkotos
                winlogon = h.node_get_child(winlogon, child)
424 10088af7 Nikos Skalkotos
425 10088af7 Nikos Skalkotos
            h.node_set_value(
426 10088af7 Nikos Skalkotos
                winlogon,
427 10088af7 Nikos Skalkotos
                {'key': 'DefaultUserName', 't': 1,
428 10088af7 Nikos Skalkotos
                 'value': "Administrator".encode('utf-16le')})
429 10088af7 Nikos Skalkotos
            h.node_set_value(
430 10088af7 Nikos Skalkotos
                winlogon,
431 10088af7 Nikos Skalkotos
                {'key': 'DefaultPassword', 't': 1,
432 10088af7 Nikos Skalkotos
                 'value':  self.sysprep_params['password'].encode('utf-16le')})
433 10088af7 Nikos Skalkotos
            h.node_set_value(
434 10088af7 Nikos Skalkotos
                winlogon,
435 10088af7 Nikos Skalkotos
                {'key': 'AutoAdminLogon', 't': 1,
436 10088af7 Nikos Skalkotos
                 'value': "1".encode('utf-16le')})
437 10088af7 Nikos Skalkotos
438 10088af7 Nikos Skalkotos
            key = h.root()
439 10088af7 Nikos Skalkotos
            for child in ('Microsoft', 'Windows', 'CurrentVersion'):
440 10088af7 Nikos Skalkotos
                key = h.node_get_child(key, child)
441 10088af7 Nikos Skalkotos
442 10088af7 Nikos Skalkotos
            runonce = h.node_get_child(key, "RunOnce")
443 10088af7 Nikos Skalkotos
            if runonce is None:
444 10088af7 Nikos Skalkotos
                runonce = h.node_add_child(key, "RunOnce")
445 10088af7 Nikos Skalkotos
446 10088af7 Nikos Skalkotos
            value = (
447 10088af7 Nikos Skalkotos
                r'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe '
448 10088af7 Nikos Skalkotos
                r'-ExecutionPolicy RemoteSigned '
449 10088af7 Nikos Skalkotos
                r'"&{$port=new-Object System.IO.Ports.SerialPort COM1,9600,'
450 10088af7 Nikos Skalkotos
                r'None,8,one;$port.open();$port.WriteLine(\"' + token + r'\");'
451 10088af7 Nikos Skalkotos
                r'$port.Close()}"').encode('utf-16le')
452 10088af7 Nikos Skalkotos
453 10088af7 Nikos Skalkotos
            h.node_set_value(runonce,
454 10088af7 Nikos Skalkotos
                             {'key': "BootMonitor", 't': 1, 'value': value})
455 10088af7 Nikos Skalkotos
456 85e97ae0 Nikos Skalkotos
            value = (
457 85e97ae0 Nikos Skalkotos
                r'REG ADD HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion'
458 85e97ae0 Nikos Skalkotos
                r'\policies\system /v LocalAccountTokenFilterPolicy'
459 85e97ae0 Nikos Skalkotos
                r' /t REG_DWORD /d 1 /f').encode('utf-16le')
460 85e97ae0 Nikos Skalkotos
461 85e97ae0 Nikos Skalkotos
            h.node_set_value(runonce,
462 85e97ae0 Nikos Skalkotos
                             {'key': "UpdateRegistry", 't': 1, 'value': value})
463 85e97ae0 Nikos Skalkotos
464 10088af7 Nikos Skalkotos
            h.commit(None)
465 10088af7 Nikos Skalkotos
466 10088af7 Nikos Skalkotos
            self.g.upload(software, path)
467 10088af7 Nikos Skalkotos
        finally:
468 10088af7 Nikos Skalkotos
            os.unlink(software)
469 10088af7 Nikos Skalkotos
470 10088af7 Nikos Skalkotos
        return token
471 10088af7 Nikos Skalkotos
472 c2113d72 Nikos Skalkotos
    def _update_firewalls(self, domain, public, standard):
473 c2113d72 Nikos Skalkotos
        """Enables or disables the firewall for the Domain, the Public and the
474 c2113d72 Nikos Skalkotos
        Standard profile. Returns a triplete with the old values.
475 c2113d72 Nikos Skalkotos

476 c2113d72 Nikos Skalkotos
        1 will enable a firewall and 0 will disable it
477 c2113d72 Nikos Skalkotos
        """
478 c2113d72 Nikos Skalkotos
479 c2113d72 Nikos Skalkotos
        if domain not in (0, 1):
480 c2113d72 Nikos Skalkotos
            raise ValueError("Valid values for domain parameter are 0 and 1")
481 c2113d72 Nikos Skalkotos
482 c2113d72 Nikos Skalkotos
        if public not in (0, 1):
483 c2113d72 Nikos Skalkotos
            raise ValueError("Valid values for public parameter are 0 and 1")
484 c2113d72 Nikos Skalkotos
485 c2113d72 Nikos Skalkotos
        if standard not in (0, 1):
486 c2113d72 Nikos Skalkotos
            raise ValueError("Valid values for standard parameter are 0 and 1")
487 c2113d72 Nikos Skalkotos
488 c2113d72 Nikos Skalkotos
        path = self._registry_file_path("SYSTEM")
489 c2113d72 Nikos Skalkotos
        systemfd, system = tempfile.mkstemp()
490 c2113d72 Nikos Skalkotos
        try:
491 c2113d72 Nikos Skalkotos
            os.close(systemfd)
492 c2113d72 Nikos Skalkotos
            self.g.download(path, system)
493 c2113d72 Nikos Skalkotos
494 c2113d72 Nikos Skalkotos
            h = hivex.Hivex(system, write=True)
495 c2113d72 Nikos Skalkotos
496 c2113d72 Nikos Skalkotos
            select = h.node_get_child(h.root(), 'Select')
497 c2113d72 Nikos Skalkotos
            current_value = h.node_get_value(select, 'Current')
498 c2113d72 Nikos Skalkotos
499 c2113d72 Nikos Skalkotos
            # expecting a little endian dword
500 c2113d72 Nikos Skalkotos
            assert h.value_type(current_value)[1] == 4
501 c2113d72 Nikos Skalkotos
            current = "%03d" % h.value_dword(current_value)
502 c2113d72 Nikos Skalkotos
503 c2113d72 Nikos Skalkotos
            firewall_policy = h.root()
504 c2113d72 Nikos Skalkotos
            for child in ('ControlSet%s' % current, 'services', 'SharedAccess',
505 c2113d72 Nikos Skalkotos
                          'Parameters', 'FirewallPolicy'):
506 c2113d72 Nikos Skalkotos
                firewall_policy = h.node_get_child(firewall_policy, child)
507 c2113d72 Nikos Skalkotos
508 c2113d72 Nikos Skalkotos
            old_values = []
509 c2113d72 Nikos Skalkotos
            new_values = [domain, public, standard]
510 c2113d72 Nikos Skalkotos
            for profile in ('Domain', 'Public', 'Standard'):
511 c2113d72 Nikos Skalkotos
                node = h.node_get_child(firewall_policy, '%sProfile' % profile)
512 c2113d72 Nikos Skalkotos
513 c2113d72 Nikos Skalkotos
                old_value = h.node_get_value(node, 'EnableFirewall')
514 c2113d72 Nikos Skalkotos
515 c2113d72 Nikos Skalkotos
                # expecting a little endian dword
516 c2113d72 Nikos Skalkotos
                assert h.value_type(old_value)[1] == 4
517 c2113d72 Nikos Skalkotos
                old_values.append(h.value_dword(old_value))
518 c2113d72 Nikos Skalkotos
519 85e97ae0 Nikos Skalkotos
                h.node_set_value(
520 85e97ae0 Nikos Skalkotos
                    node, {'key': 'EnableFirewall', 't': 4L,
521 85e97ae0 Nikos Skalkotos
                           'value': struct.pack("<I", new_values.pop(0))})
522 c2113d72 Nikos Skalkotos
523 c2113d72 Nikos Skalkotos
            h.commit(None)
524 c2113d72 Nikos Skalkotos
            self.g.upload(system, path)
525 c2113d72 Nikos Skalkotos
526 c2113d72 Nikos Skalkotos
        finally:
527 c2113d72 Nikos Skalkotos
            os.unlink(system)
528 c2113d72 Nikos Skalkotos
529 c2113d72 Nikos Skalkotos
        return old_values
530 c2113d72 Nikos Skalkotos
531 640096fb Nikos Skalkotos
    def _update_uac_remote_setting(self, value):
532 640096fb Nikos Skalkotos
        """Updates the registry key value:
533 640096fb Nikos Skalkotos
        [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies
534 640096fb Nikos Skalkotos
        \System]"LocalAccountTokenFilterPolicy"
535 640096fb Nikos Skalkotos

536 640096fb Nikos Skalkotos
        value = 1 will disable the UAC remote restrictions
537 640096fb Nikos Skalkotos
        value = 0 will enable the UAC remote restrictions
538 640096fb Nikos Skalkotos

539 640096fb Nikos Skalkotos
        For more info see here: http://support.microsoft.com/kb/951016
540 640096fb Nikos Skalkotos

541 640096fb Nikos Skalkotos
        Returns:
542 640096fb Nikos Skalkotos
            True if the key is changed
543 640096fb Nikos Skalkotos
            False if the key is unchanged
544 640096fb Nikos Skalkotos
        """
545 640096fb Nikos Skalkotos
546 640096fb Nikos Skalkotos
        if value not in (0, 1):
547 640096fb Nikos Skalkotos
            raise ValueError("Valid values for value parameter are 0 and 1")
548 640096fb Nikos Skalkotos
549 640096fb Nikos Skalkotos
        path = self._registry_file_path('SOFTWARE')
550 640096fb Nikos Skalkotos
        softwarefd, software = tempfile.mkstemp()
551 640096fb Nikos Skalkotos
        try:
552 640096fb Nikos Skalkotos
            os.close(softwarefd)
553 640096fb Nikos Skalkotos
            self.g.download(path, software)
554 640096fb Nikos Skalkotos
555 640096fb Nikos Skalkotos
            h = hivex.Hivex(software, write=True)
556 640096fb Nikos Skalkotos
557 640096fb Nikos Skalkotos
            key = h.root()
558 640096fb Nikos Skalkotos
            for child in ('Microsoft', 'Windows', 'CurrentVersion', 'Policies',
559 640096fb Nikos Skalkotos
                          'System'):
560 640096fb Nikos Skalkotos
                key = h.node_get_child(key, child)
561 640096fb Nikos Skalkotos
562 640096fb Nikos Skalkotos
            policy = None
563 640096fb Nikos Skalkotos
            for val in h.node_values(key):
564 640096fb Nikos Skalkotos
                if h.value_key(val) == "LocalAccountTokenFilterPolicy":
565 640096fb Nikos Skalkotos
                    policy = val
566 640096fb Nikos Skalkotos
567 640096fb Nikos Skalkotos
            if policy is not None:
568 640096fb Nikos Skalkotos
                dword = h.value_dword(policy)
569 640096fb Nikos Skalkotos
                if dword == value:
570 640096fb Nikos Skalkotos
                    return False
571 640096fb Nikos Skalkotos
            elif value == 0:
572 640096fb Nikos Skalkotos
                return False
573 640096fb Nikos Skalkotos
574 85e97ae0 Nikos Skalkotos
            new_value = {'key': "LocalAccountTokenFilterPolicy", 't': 4L,
575 85e97ae0 Nikos Skalkotos
                         'value': struct.pack("<I", value)}
576 640096fb Nikos Skalkotos
577 640096fb Nikos Skalkotos
            h.node_set_value(key, new_value)
578 640096fb Nikos Skalkotos
            h.commit(None)
579 640096fb Nikos Skalkotos
580 640096fb Nikos Skalkotos
            self.g.upload(software, path)
581 640096fb Nikos Skalkotos
582 640096fb Nikos Skalkotos
        finally:
583 640096fb Nikos Skalkotos
            os.unlink(software)
584 640096fb Nikos Skalkotos
585 640096fb Nikos Skalkotos
        return True
586 640096fb Nikos Skalkotos
587 b8c0848c Nikos Skalkotos
    def _do_collect_metadata(self):
588 b8c0848c Nikos Skalkotos
        """Collect metadata about the OS"""
589 b8c0848c Nikos Skalkotos
        super(Windows, self)._do_collect_metadata()
590 76b200cf Nikos Skalkotos
        self.meta["USERS"] = " ".join(self._get_users())
591 76b200cf Nikos Skalkotos
592 76b200cf Nikos Skalkotos
    def _get_users(self):
593 121f3bc0 Nikos Skalkotos
        """Returns a list of users found in the images"""
594 640096fb Nikos Skalkotos
        path = self._registry_file_path('SAM')
595 76b200cf Nikos Skalkotos
        samfd, sam = tempfile.mkstemp()
596 76b200cf Nikos Skalkotos
        try:
597 640096fb Nikos Skalkotos
            os.close(samfd)
598 76b200cf Nikos Skalkotos
            self.g.download(path, sam)
599 76b200cf Nikos Skalkotos
600 76b200cf Nikos Skalkotos
            h = hivex.Hivex(sam)
601 76b200cf Nikos Skalkotos
602 76b200cf Nikos Skalkotos
            key = h.root()
603 76b200cf Nikos Skalkotos
            # Navigate to /SAM/Domains/Account/Users/Names
604 76b200cf Nikos Skalkotos
            for child in ('SAM', 'Domains', 'Account', 'Users', 'Names'):
605 76b200cf Nikos Skalkotos
                key = h.node_get_child(key, child)
606 76b200cf Nikos Skalkotos
607 76b200cf Nikos Skalkotos
            users = [h.node_name(x) for x in h.node_children(key)]
608 76b200cf Nikos Skalkotos
609 76b200cf Nikos Skalkotos
        finally:
610 76b200cf Nikos Skalkotos
            os.unlink(sam)
611 76b200cf Nikos Skalkotos
612 76b200cf Nikos Skalkotos
        # Filter out the guest account
613 76b200cf Nikos Skalkotos
        return filter(lambda x: x != "Guest", users)
614 aa2062ba Nikos Skalkotos
615 10088af7 Nikos Skalkotos
    def _guest_exec(self, command, fatal=True):
616 10088af7 Nikos Skalkotos
        """Execute a command on a windows VM"""
617 10088af7 Nikos Skalkotos
618 74780ad6 Nikos Skalkotos
        user = "Administrator%" + self.sysprep_params['password']
619 74780ad6 Nikos Skalkotos
        addr = 'localhost'
620 74780ad6 Nikos Skalkotos
        runas = '--runas=%s' % user
621 74780ad6 Nikos Skalkotos
        winexe = subprocess.Popen(
622 efa7a61b Nikos Skalkotos
            ['winexe', '-U', user, runas, "//%s" % addr, command],
623 74780ad6 Nikos Skalkotos
            stdout=subprocess.PIPE, stderr=subprocess.PIPE)
624 74780ad6 Nikos Skalkotos
625 10088af7 Nikos Skalkotos
        stdout, stderr = winexe.communicate()
626 74780ad6 Nikos Skalkotos
        rc = winexe.poll()
627 74780ad6 Nikos Skalkotos
628 10088af7 Nikos Skalkotos
        if rc != 0 and fatal:
629 10088af7 Nikos Skalkotos
            reason = stderr if len(stderr) else stdout
630 10088af7 Nikos Skalkotos
            raise FatalError("Command: `%s' failed. Reason: %s" %
631 10088af7 Nikos Skalkotos
                             (command, reason))
632 10088af7 Nikos Skalkotos
633 10088af7 Nikos Skalkotos
        return (stdout, stderr, rc)
634 74780ad6 Nikos Skalkotos
635 aa2062ba Nikos Skalkotos
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :