Statistics
| Branch: | Tag: | Revision:

root / image_creator / os_type / windows.py @ 4246a133

History | View | Annotate | Download (30.6 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 f59ad913 Nikos Skalkotos
from image_creator.os_type import OSBase, sysprep, add_sysprep_param
40 4246a133 Nikos Skalkotos
from image_creator.util import FatalError, check_guestfs_version, \
41 4246a133 Nikos Skalkotos
    get_kvm_binary
42 f5873ed0 Nikos Skalkotos
from image_creator.winexe import WinEXE, WinexeTimeout
43 aa2062ba Nikos Skalkotos
44 76b200cf Nikos Skalkotos
import hivex
45 76b200cf Nikos Skalkotos
import tempfile
46 76b200cf Nikos Skalkotos
import os
47 8bae613f Nikos Skalkotos
import signal
48 9ca717c4 Nikos Skalkotos
import time
49 9ca717c4 Nikos Skalkotos
import random
50 10088af7 Nikos Skalkotos
import string
51 74780ad6 Nikos Skalkotos
import subprocess
52 85e97ae0 Nikos Skalkotos
import struct
53 9ca717c4 Nikos Skalkotos
54 684a5524 Nikos Skalkotos
# For more info see: http://technet.microsoft.com/en-us/library/jj612867.aspx
55 684a5524 Nikos Skalkotos
KMS_CLIENT_SETUP_KEYS = {
56 684a5524 Nikos Skalkotos
    "Windows 8 Professional": "NG4HW-VH26C-733KW-K6F98-J8CK4",
57 684a5524 Nikos Skalkotos
    "Windows 8 Professional N": "XCVCF-2NXM9-723PB-MHCB7-2RYQQ",
58 684a5524 Nikos Skalkotos
    "Windows 8 Enterprise": "32JNW-9KQ84-P47T8-D8GGY-CWCK7",
59 684a5524 Nikos Skalkotos
    "Windows 8 Enterprise N": "JMNMF-RHW7P-DMY6X-RF3DR-X2BQT",
60 684a5524 Nikos Skalkotos
    "Windows Server 2012 Core": "BN3D2-R7TKB-3YPBD-8DRP2-27GG4",
61 684a5524 Nikos Skalkotos
    "Windows Server 2012 Core N": "8N2M2-HWPGY-7PGT9-HGDD8-GVGGY",
62 684a5524 Nikos Skalkotos
    "Windows Server 2012 Core Single Language":
63 684a5524 Nikos Skalkotos
    "2WN2H-YGCQR-KFX6K-CD6TF-84YXQ",
64 684a5524 Nikos Skalkotos
    "Windows Server 2012 Core Country Specific":
65 684a5524 Nikos Skalkotos
    "4K36P-JN4VD-GDC6V-KDT89-DYFKP",
66 684a5524 Nikos Skalkotos
    "Windows Server 2012 Server Standard": "XC9B7-NBPP2-83J2H-RHMBY-92BT4",
67 684a5524 Nikos Skalkotos
    "Windows Server 2012 Standard Core": "XC9B7-NBPP2-83J2H-RHMBY-92BT4",
68 684a5524 Nikos Skalkotos
    "Windows Server 2012 MultiPoint Standard": "HM7DN-YVMH3-46JC3-XYTG7-CYQJJ",
69 684a5524 Nikos Skalkotos
    "Windows Server 2012 MultiPoint Premium": "XNH6W-2V9GX-RGJ4K-Y8X6F-QGJ2G",
70 684a5524 Nikos Skalkotos
    "Windows Server 2012 Datacenter": "48HP8-DN98B-MYWDG-T2DCC-8W83P",
71 684a5524 Nikos Skalkotos
    "Windows Server 2012 Datacenter Core": "48HP8-DN98B-MYWDG-T2DCC-8W83P",
72 684a5524 Nikos Skalkotos
    "Windows 7 Professional": "FJ82H-XT6CR-J8D7P-XQJJ2-GPDD4",
73 684a5524 Nikos Skalkotos
    "Windows 7 Professional N": "MRPKT-YTG23-K7D7T-X2JMM-QY7MG",
74 684a5524 Nikos Skalkotos
    "Windows 7 Professional E": "W82YF-2Q76Y-63HXB-FGJG9-GF7QX",
75 684a5524 Nikos Skalkotos
    "Windows 7 Enterprise": "33PXH-7Y6KF-2VJC9-XBBR8-HVTHH",
76 684a5524 Nikos Skalkotos
    "Windows 7 Enterprise N": "YDRBP-3D83W-TY26F-D46B2-XCKRJ",
77 684a5524 Nikos Skalkotos
    "Windows 7 Enterprise E": "C29WB-22CC8-VJ326-GHFJW-H9DH4",
78 684a5524 Nikos Skalkotos
    "Windows Server 2008 R2 Web": "6TPJF-RBVHG-WBW2R-86QPH-6RTM4",
79 684a5524 Nikos Skalkotos
    "Windows Server 2008 R2 HPC edition": "TT8MH-CG224-D3D7Q-498W2-9QCTX",
80 684a5524 Nikos Skalkotos
    "Windows Server 2008 R2 Standard": "YC6KT-GKW9T-YTKYR-T4X34-R7VHC",
81 684a5524 Nikos Skalkotos
    "Windows Server 2008 R2 Enterprise": "489J6-VHDMP-X63PK-3K798-CPX3Y",
82 684a5524 Nikos Skalkotos
    "Windows Server 2008 R2 Datacenter": "74YFP-3QFB3-KQT8W-PMXWJ-7M648",
83 684a5524 Nikos Skalkotos
    "Windows Server 2008 R2 for Itanium-based Systems":
84 684a5524 Nikos Skalkotos
    "GT63C-RJFQ3-4GMB6-BRFB9-CB83V",
85 684a5524 Nikos Skalkotos
    "Windows Vista Business": "YFKBB-PQJJV-G996G-VWGXY-2V3X8",
86 684a5524 Nikos Skalkotos
    "Windows Vista Business N": "HMBQG-8H2RH-C77VX-27R82-VMQBT",
87 684a5524 Nikos Skalkotos
    "Windows Vista Enterprise": "VKK3X-68KWM-X2YGT-QR4M6-4BWMV",
88 684a5524 Nikos Skalkotos
    "Windows Vista Enterprise N": "VTC42-BM838-43QHV-84HX6-XJXKV",
89 684a5524 Nikos Skalkotos
    "Windows Web Server 2008": "WYR28-R7TFJ-3X2YQ-YCY4H-M249D",
90 684a5524 Nikos Skalkotos
    "Windows Server 2008 Standard": "TM24T-X9RMF-VWXK6-X8JC9-BFGM2",
91 684a5524 Nikos Skalkotos
    "Windows Server 2008 Standard without Hyper-V":
92 684a5524 Nikos Skalkotos
    "W7VD6-7JFBR-RX26B-YKQ3Y-6FFFJ",
93 684a5524 Nikos Skalkotos
    "Windows Server 2008 Enterprise":
94 684a5524 Nikos Skalkotos
    "YQGMW-MPWTJ-34KDK-48M3W-X4Q6V",
95 684a5524 Nikos Skalkotos
    "Windows Server 2008 Enterprise without Hyper-V":
96 684a5524 Nikos Skalkotos
    "39BXF-X8Q23-P2WWT-38T2F-G3FPG",
97 684a5524 Nikos Skalkotos
    "Windows Server 2008 HPC": "RCTX3-KWVHP-BR6TB-RB6DM-6X7HP",
98 684a5524 Nikos Skalkotos
    "Windows Server 2008 Datacenter": "7M67G-PC374-GR742-YH8V4-TCBY3",
99 684a5524 Nikos Skalkotos
    "Windows Server 2008 Datacenter without Hyper-V":
100 684a5524 Nikos Skalkotos
    "22XQ2-VRXRG-P8D42-K34TD-G3QQC",
101 684a5524 Nikos Skalkotos
    "Windows Server 2008 for Itanium-Based Systems":
102 684a5524 Nikos Skalkotos
    "4DWFP-JF3DJ-B7DTH-78FJB-PDRHK"}
103 684a5524 Nikos Skalkotos
104 82e8c357 Nikos Skalkotos
_POSINT = lambda x: type(x) == int and x >= 0
105 82e8c357 Nikos Skalkotos
106 8c574358 Nikos Skalkotos
107 aa2062ba Nikos Skalkotos
class Windows(OSBase):
108 88f83027 Nikos Skalkotos
    """OS class for Windows"""
109 82e8c357 Nikos Skalkotos
    @add_sysprep_param(
110 82e8c357 Nikos Skalkotos
        'shutdown_timeout', int, 120, "Shutdown Timeout (seconds)", _POSINT)
111 82e8c357 Nikos Skalkotos
    @add_sysprep_param(
112 82e8c357 Nikos Skalkotos
        'boot_timeout', int, 300, "Boot Timeout (seconds)", _POSINT)
113 82e8c357 Nikos Skalkotos
    @add_sysprep_param(
114 82e8c357 Nikos Skalkotos
        'connection_retries', int, 5, "Connection Retries", _POSINT)
115 678b382b Nikos Skalkotos
    @add_sysprep_param('password', str, None, 'Image Administrator Password')
116 efa7a61b Nikos Skalkotos
    def __init__(self, image, **kargs):
117 efa7a61b Nikos Skalkotos
        super(Windows, self).__init__(image, **kargs)
118 efa7a61b Nikos Skalkotos
119 efa7a61b Nikos Skalkotos
        device = self.g.part_to_dev(self.root)
120 efa7a61b Nikos Skalkotos
121 efa7a61b Nikos Skalkotos
        self.last_part_num = self.g.part_list(device)[-1]['part_num']
122 efa7a61b Nikos Skalkotos
        self.last_drive = None
123 efa7a61b Nikos Skalkotos
        self.system_drive = None
124 efa7a61b Nikos Skalkotos
125 efa7a61b Nikos Skalkotos
        for drive, partition in self.g.inspect_get_drive_mappings(self.root):
126 efa7a61b Nikos Skalkotos
            if partition == "%s%d" % (device, self.last_part_num):
127 efa7a61b Nikos Skalkotos
                self.last_drive = drive
128 efa7a61b Nikos Skalkotos
            if partition == self.root:
129 efa7a61b Nikos Skalkotos
                self.system_drive = drive
130 efa7a61b Nikos Skalkotos
131 efa7a61b Nikos Skalkotos
        assert self.system_drive
132 76b200cf Nikos Skalkotos
133 684a5524 Nikos Skalkotos
        self.product_name = self.g.inspect_get_product_name(self.root)
134 47505e53 Nikos Skalkotos
        self.syspreped = False
135 684a5524 Nikos Skalkotos
136 b8d4b14a Nikos Skalkotos
    @sysprep('Disabling IPv6 privacy extensions')
137 b8d4b14a Nikos Skalkotos
    def disable_ipv6_privacy_extensions(self):
138 74780ad6 Nikos Skalkotos
        """Disable IPv6 privacy extensions"""
139 74780ad6 Nikos Skalkotos
140 10088af7 Nikos Skalkotos
        self._guest_exec('netsh interface ipv6 set global '
141 10088af7 Nikos Skalkotos
                         'randomizeidentifiers=disabled store=persistent')
142 74780ad6 Nikos Skalkotos
143 b9e14923 Nikos Skalkotos
    @sysprep('Disabling Teredo interface')
144 b9e14923 Nikos Skalkotos
    def disable_teredo(self):
145 b9e14923 Nikos Skalkotos
        """Disable Teredo interface"""
146 b9e14923 Nikos Skalkotos
147 b9e14923 Nikos Skalkotos
        self._guest_exec('netsh interface teredo set state disabled')
148 b9e14923 Nikos Skalkotos
149 b9e14923 Nikos Skalkotos
    @sysprep('Disabling ISATAP Adapters')
150 b9e14923 Nikos Skalkotos
    def disable_isatap(self):
151 b9e14923 Nikos Skalkotos
        """Disable ISATAP Adapters"""
152 b9e14923 Nikos Skalkotos
153 b9e14923 Nikos Skalkotos
        self._guest_exec('netsh interface isa set state disabled')
154 b9e14923 Nikos Skalkotos
155 b9e14923 Nikos Skalkotos
    @sysprep('Enabling ping responses')
156 b9e14923 Nikos Skalkotos
    def enable_pings(self):
157 c53cc8dd Nikos Skalkotos
        """Enable ping responses"""
158 b9e14923 Nikos Skalkotos
159 b9e14923 Nikos Skalkotos
        self._guest_exec('netsh firewall set icmpsetting 8')
160 b9e14923 Nikos Skalkotos
161 811b7149 Nikos Skalkotos
    @sysprep('Disabling hibernation support')
162 811b7149 Nikos Skalkotos
    def disable_hibernation(self):
163 811b7149 Nikos Skalkotos
        """Disable hibernation support and remove the hibernation file"""
164 811b7149 Nikos Skalkotos
165 811b7149 Nikos Skalkotos
        self._guest_exec(r'powercfg.exe /hibernate off')
166 811b7149 Nikos Skalkotos
167 b9e14923 Nikos Skalkotos
    @sysprep('Setting the system clock to UTC')
168 b9e14923 Nikos Skalkotos
    def utc(self):
169 b9e14923 Nikos Skalkotos
        """Set the hardware clock to UTC"""
170 b9e14923 Nikos Skalkotos
171 b9e14923 Nikos Skalkotos
        path = r'HKLM\SYSTEM\CurrentControlSet\Control\TimeZoneInformation'
172 b9e14923 Nikos Skalkotos
        self._guest_exec(
173 b9e14923 Nikos Skalkotos
            r'REG ADD %s /v RealTimeIsUniversal /t REG_DWORD /d 1 /f' % path)
174 b9e14923 Nikos Skalkotos
175 2075a76a Nikos Skalkotos
    @sysprep('Clearing the event logs')
176 2075a76a Nikos Skalkotos
    def clear_logs(self):
177 2075a76a Nikos Skalkotos
        """Clear all the event logs"""
178 2075a76a Nikos Skalkotos
179 2075a76a Nikos Skalkotos
        self._guest_exec(
180 2075a76a Nikos Skalkotos
            r"cmd /q /c for /f %l in ('wevtutil el') do wevtutil cl %l")
181 2075a76a Nikos Skalkotos
182 c53cc8dd Nikos Skalkotos
    @sysprep('Executing Sysprep on the image (may take more that 10 minutes)')
183 b8d4b14a Nikos Skalkotos
    def microsoft_sysprep(self):
184 b9e14923 Nikos Skalkotos
        """Run the Microsoft System Preparation Tool. This will remove
185 b9e14923 Nikos Skalkotos
        system-specific data and will make the image ready to be deployed.
186 b9e14923 Nikos Skalkotos
        After this no other task may run.
187 74780ad6 Nikos Skalkotos
        """
188 74780ad6 Nikos Skalkotos
189 10088af7 Nikos Skalkotos
        self._guest_exec(r'C:\Windows\system32\sysprep\sysprep '
190 10088af7 Nikos Skalkotos
                         r'/quiet /generalize /oobe /shutdown')
191 74780ad6 Nikos Skalkotos
        self.syspreped = True
192 9ca717c4 Nikos Skalkotos
193 684a5524 Nikos Skalkotos
    @sysprep('Converting the image into a KMS client', enabled=False)
194 684a5524 Nikos Skalkotos
    def kms_client_setup(self):
195 684a5524 Nikos Skalkotos
        """Install the appropriate KMS client setup key to the image to convert
196 684a5524 Nikos Skalkotos
        it to a KMS client. Computers that are running volume licensing
197 684a5524 Nikos Skalkotos
        editions of Windows 8, Windows Server 2012, Windows 7, Windows Server
198 684a5524 Nikos Skalkotos
        2008 R2, Windows Vista, and Windows Server 2008 are, by default, KMS
199 684a5524 Nikos Skalkotos
        clients with no additional configuration needed.
200 684a5524 Nikos Skalkotos
        """
201 684a5524 Nikos Skalkotos
        try:
202 684a5524 Nikos Skalkotos
            setup_key = KMS_CLIENT_SETUP_KEYS[self.product_name]
203 684a5524 Nikos Skalkotos
        except KeyError:
204 684a5524 Nikos Skalkotos
            self.out.warn(
205 684a5524 Nikos Skalkotos
                "Don't know the KMS client setup key for product: `%s'" %
206 684a5524 Nikos Skalkotos
                self.product_name)
207 684a5524 Nikos Skalkotos
            return
208 684a5524 Nikos Skalkotos
209 684a5524 Nikos Skalkotos
        self._guest_exec(
210 47505e53 Nikos Skalkotos
            r"cscript \Windows\system32\slmgr.vbs /ipk %s" % setup_key)
211 684a5524 Nikos Skalkotos
212 efa7a61b Nikos Skalkotos
    @sysprep('Shrinking the last filesystem')
213 efa7a61b Nikos Skalkotos
    def shrink(self):
214 efa7a61b Nikos Skalkotos
        """Shrink the last filesystem. Make sure the filesystem is defragged"""
215 d4270a48 Nikos Skalkotos
216 d4270a48 Nikos Skalkotos
        # Query for the maximum number of reclaimable bytes
217 d4270a48 Nikos Skalkotos
        cmd = (
218 23c82433 Nikos Skalkotos
            r'cmd /Q /V:ON /C "SET SCRIPT=%TEMP%\QUERYMAX_%RANDOM%.TXT & ' +
219 d4270a48 Nikos Skalkotos
            r'ECHO SELECT DISK 0 > %SCRIPT% & ' +
220 d4270a48 Nikos Skalkotos
            'ECHO SELECT PARTITION %d >> %%SCRIPT%% & ' % self.last_part_num +
221 d4270a48 Nikos Skalkotos
            r'ECHO SHRINK QUERYMAX >> %SCRIPT% & ' +
222 d4270a48 Nikos Skalkotos
            r'ECHO EXIT >> %SCRIPT% & ' +
223 d4270a48 Nikos Skalkotos
            r'DISKPART /S %SCRIPT% & ' +
224 23c82433 Nikos Skalkotos
            r'IF NOT !ERRORLEVEL! EQU 0 EXIT /B 1 & ' +
225 d4270a48 Nikos Skalkotos
            r'DEL /Q %SCRIPT%"')
226 d4270a48 Nikos Skalkotos
227 d4270a48 Nikos Skalkotos
        stdout, stderr, rc = self._guest_exec(cmd)
228 d4270a48 Nikos Skalkotos
229 d4270a48 Nikos Skalkotos
        querymax = None
230 d4270a48 Nikos Skalkotos
        for line in stdout.splitlines():
231 d4270a48 Nikos Skalkotos
            # diskpart will return something like this:
232 d4270a48 Nikos Skalkotos
            #
233 d4270a48 Nikos Skalkotos
            #   The maximum number of reclaimable bytes is: xxxx MB
234 d4270a48 Nikos Skalkotos
            #
235 d4270a48 Nikos Skalkotos
            if line.find('reclaimable') >= 0:
236 d4270a48 Nikos Skalkotos
                querymax = line.split(':')[1].split()[0].strip()
237 d4270a48 Nikos Skalkotos
                assert querymax.isdigit(), \
238 d4270a48 Nikos Skalkotos
                    "Number of reclaimable bytes not a number"
239 d4270a48 Nikos Skalkotos
240 d4270a48 Nikos Skalkotos
        if querymax is None:
241 d4270a48 Nikos Skalkotos
            FatalError("Error in shrinking! "
242 d4270a48 Nikos Skalkotos
                       "Couldn't find the max number of reclaimable bytes!")
243 d4270a48 Nikos Skalkotos
244 d4270a48 Nikos Skalkotos
        querymax = int(querymax)
245 d4270a48 Nikos Skalkotos
        # From ntfsresize:
246 d4270a48 Nikos Skalkotos
        # Practically the smallest shrunken size generally is at around
247 d4270a48 Nikos Skalkotos
        # "used space" + (20-200 MB). Please also take into account that
248 d4270a48 Nikos Skalkotos
        # Windows might need about 50-100 MB free space left to boot safely.
249 d4270a48 Nikos Skalkotos
        # I'll give 100MB extra space just to be sure
250 d4270a48 Nikos Skalkotos
        querymax -= 100
251 d4270a48 Nikos Skalkotos
252 d4270a48 Nikos Skalkotos
        if querymax < 0:
253 d4270a48 Nikos Skalkotos
            self.out.warn("Not enought available space to shrink the image!")
254 d4270a48 Nikos Skalkotos
            return
255 d4270a48 Nikos Skalkotos
256 23c82433 Nikos Skalkotos
        self.out.output("\tReclaiming %dMB ..." % querymax)
257 23c82433 Nikos Skalkotos
258 d4270a48 Nikos Skalkotos
        cmd = (
259 23c82433 Nikos Skalkotos
            r'cmd /Q /V:ON /C "SET SCRIPT=%TEMP%\QUERYMAX_%RANDOM%.TXT & ' +
260 d4270a48 Nikos Skalkotos
            r'ECHO SELECT DISK 0 > %SCRIPT% & ' +
261 d4270a48 Nikos Skalkotos
            'ECHO SELECT PARTITION %d >> %%SCRIPT%% & ' % self.last_part_num +
262 d4270a48 Nikos Skalkotos
            'ECHO SHRINK DESIRED=%d >> %%SCRIPT%% & ' % querymax +
263 d4270a48 Nikos Skalkotos
            r'ECHO EXIT >> %SCRIPT% & ' +
264 d4270a48 Nikos Skalkotos
            r'DISKPART /S %SCRIPT% & ' +
265 23c82433 Nikos Skalkotos
            r'IF NOT !ERRORLEVEL! EQU 0 EXIT /B 1 & ' +
266 d4270a48 Nikos Skalkotos
            r'DEL /Q %SCRIPT%"')
267 d4270a48 Nikos Skalkotos
268 d4270a48 Nikos Skalkotos
        stdout, stderr, rc = self._guest_exec(cmd)
269 d4270a48 Nikos Skalkotos
270 d4270a48 Nikos Skalkotos
        for line in stdout.splitlines():
271 d4270a48 Nikos Skalkotos
            if line.find('shrunk') >= 0:
272 d4270a48 Nikos Skalkotos
                self.out.output(line)
273 efa7a61b Nikos Skalkotos
274 640096fb Nikos Skalkotos
    def do_sysprep(self):
275 640096fb Nikos Skalkotos
        """Prepare system for image creation."""
276 640096fb Nikos Skalkotos
277 640096fb Nikos Skalkotos
        if getattr(self, 'syspreped', False):
278 640096fb Nikos Skalkotos
            raise FatalError("Image is already syspreped!")
279 640096fb Nikos Skalkotos
280 74780ad6 Nikos Skalkotos
        txt = "System preparation parameter: `%s' is needed but missing!"
281 47505e53 Nikos Skalkotos
        for name, param in self.needed_sysprep_params.items():
282 47505e53 Nikos Skalkotos
            if name not in self.sysprep_params:
283 f59ad913 Nikos Skalkotos
                raise FatalError(txt % param)
284 74780ad6 Nikos Skalkotos
285 640096fb Nikos Skalkotos
        self.mount(readonly=False)
286 640096fb Nikos Skalkotos
        try:
287 640096fb Nikos Skalkotos
            disabled_uac = self._update_uac_remote_setting(1)
288 10088af7 Nikos Skalkotos
            token = self._enable_os_monitor()
289 c2113d72 Nikos Skalkotos
290 c2113d72 Nikos Skalkotos
            # disable the firewalls
291 c2113d72 Nikos Skalkotos
            firewall_states = self._update_firewalls(0, 0, 0)
292 c2113d72 Nikos Skalkotos
293 c2113d72 Nikos Skalkotos
            # Delete the pagefile. It will be recreated when the system boots
294 c2113d72 Nikos Skalkotos
            systemroot = self.g.inspect_get_windows_systemroot(self.root)
295 c2113d72 Nikos Skalkotos
            pagefile = "%s/pagefile.sys" % systemroot
296 c2113d72 Nikos Skalkotos
            self.g.rm_rf(self.g.case_sensitive_path(pagefile))
297 c2113d72 Nikos Skalkotos
298 640096fb Nikos Skalkotos
        finally:
299 640096fb Nikos Skalkotos
            self.umount()
300 640096fb Nikos Skalkotos
301 640096fb Nikos Skalkotos
        self.out.output("Shutting down helper VM ...", False)
302 640096fb Nikos Skalkotos
        self.g.sync()
303 640096fb Nikos Skalkotos
        # guestfs_shutdown which is the prefered way to shutdown the backend
304 640096fb Nikos Skalkotos
        # process was introduced in version 1.19.16
305 640096fb Nikos Skalkotos
        if check_guestfs_version(self.g, 1, 19, 16) >= 0:
306 47505e53 Nikos Skalkotos
            self.g.shutdown()
307 640096fb Nikos Skalkotos
        else:
308 47505e53 Nikos Skalkotos
            self.g.kill_subprocess()
309 640096fb Nikos Skalkotos
310 640096fb Nikos Skalkotos
        self.out.success('done')
311 10088af7 Nikos Skalkotos
312 10088af7 Nikos Skalkotos
        vm = None
313 10088af7 Nikos Skalkotos
        monitor = None
314 640096fb Nikos Skalkotos
        try:
315 9ca717c4 Nikos Skalkotos
            self.out.output("Starting windows VM ...", False)
316 10088af7 Nikos Skalkotos
            monitorfd, monitor = tempfile.mkstemp()
317 10088af7 Nikos Skalkotos
            os.close(monitorfd)
318 82e8c357 Nikos Skalkotos
            vm = _VM(self.image.device, monitor, self.sysprep_params)
319 8bae613f Nikos Skalkotos
            self.out.success("started (console on vnc display: %d)." %
320 8bae613f Nikos Skalkotos
                             vm.display)
321 10088af7 Nikos Skalkotos
322 10088af7 Nikos Skalkotos
            self.out.output("Waiting for OS to boot ...", False)
323 520d991d Nikos Skalkotos
            self._wait_vm_boot(vm, monitor, token)
324 520d991d Nikos Skalkotos
            self.out.success('done')
325 10088af7 Nikos Skalkotos
326 8bae613f Nikos Skalkotos
            self.out.output("Checking connectivity to the VM ...", False)
327 8bae613f Nikos Skalkotos
            self._check_connectivity()
328 8bae613f Nikos Skalkotos
            self.out.success('done')
329 8bae613f Nikos Skalkotos
330 10088af7 Nikos Skalkotos
            self.out.output("Disabling automatic logon ...", False)
331 10088af7 Nikos Skalkotos
            self._disable_autologon()
332 9ca717c4 Nikos Skalkotos
            self.out.success('done')
333 74780ad6 Nikos Skalkotos
334 8bae613f Nikos Skalkotos
            self.out.output('Preparing system for image creation:')
335 10088af7 Nikos Skalkotos
336 74780ad6 Nikos Skalkotos
            tasks = self.list_syspreps()
337 47505e53 Nikos Skalkotos
            enabled = [task for task in tasks if task.enabled]
338 74780ad6 Nikos Skalkotos
            size = len(enabled)
339 74780ad6 Nikos Skalkotos
340 efa7a61b Nikos Skalkotos
            # Make sure shrink runs in the end, before ms sysprep
341 47505e53 Nikos Skalkotos
            enabled = [task for task in enabled if
342 47505e53 Nikos Skalkotos
                       self.sysprep_info(task).name != 'shrink']
343 efa7a61b Nikos Skalkotos
344 efa7a61b Nikos Skalkotos
            if len(enabled) != size:
345 efa7a61b Nikos Skalkotos
                enabled.append(self.shrink)
346 efa7a61b Nikos Skalkotos
347 74780ad6 Nikos Skalkotos
            # Make sure the ms sysprep is the last task to run if it is enabled
348 47505e53 Nikos Skalkotos
            enabled = [task for task in enabled if
349 47505e53 Nikos Skalkotos
                       self.sysprep_info(task).name != 'microsoft-sysprep']
350 74780ad6 Nikos Skalkotos
351 74780ad6 Nikos Skalkotos
            ms_sysprep_enabled = False
352 74780ad6 Nikos Skalkotos
            if len(enabled) != size:
353 b9e14923 Nikos Skalkotos
                enabled.append(self.microsoft_sysprep)
354 74780ad6 Nikos Skalkotos
                ms_sysprep_enabled = True
355 74780ad6 Nikos Skalkotos
356 74780ad6 Nikos Skalkotos
            cnt = 0
357 74780ad6 Nikos Skalkotos
            for task in enabled:
358 74780ad6 Nikos Skalkotos
                cnt += 1
359 74780ad6 Nikos Skalkotos
                self.out.output(('(%d/%d)' % (cnt, size)).ljust(7), False)
360 74780ad6 Nikos Skalkotos
                task()
361 74780ad6 Nikos Skalkotos
                setattr(task.im_func, 'executed', True)
362 74780ad6 Nikos Skalkotos
363 c2113d72 Nikos Skalkotos
            self.out.output("Sending shut down command ...", False)
364 74780ad6 Nikos Skalkotos
            if not ms_sysprep_enabled:
365 74780ad6 Nikos Skalkotos
                self._shutdown()
366 10088af7 Nikos Skalkotos
            self.out.success("done")
367 74780ad6 Nikos Skalkotos
368 c2113d72 Nikos Skalkotos
            self.out.output("Waiting for windows to shut down ...", False)
369 82e8c357 Nikos Skalkotos
            vm.wait(self.sysprep_params['shutdown_timeout'])
370 c2113d72 Nikos Skalkotos
            self.out.success("done")
371 640096fb Nikos Skalkotos
        finally:
372 10088af7 Nikos Skalkotos
            if monitor is not None:
373 10088af7 Nikos Skalkotos
                os.unlink(monitor)
374 10088af7 Nikos Skalkotos
375 c2113d72 Nikos Skalkotos
            try:
376 8bae613f Nikos Skalkotos
                if vm is not None:
377 8bae613f Nikos Skalkotos
                    self.out.output("Destroying windows VM ...", False)
378 8bae613f Nikos Skalkotos
                    vm.destroy()
379 8bae613f Nikos Skalkotos
                    self.out.success("done")
380 c2113d72 Nikos Skalkotos
            finally:
381 8bae613f Nikos Skalkotos
                self.out.output("Relaunching helper VM (may take a while) ...",
382 8bae613f Nikos Skalkotos
                                False)
383 8bae613f Nikos Skalkotos
                self.g.launch()
384 8bae613f Nikos Skalkotos
                self.out.success('done')
385 10088af7 Nikos Skalkotos
386 8bae613f Nikos Skalkotos
                self.mount(readonly=False)
387 8bae613f Nikos Skalkotos
                try:
388 8bae613f Nikos Skalkotos
                    if disabled_uac:
389 8bae613f Nikos Skalkotos
                        self._update_uac_remote_setting(0)
390 10088af7 Nikos Skalkotos
391 8bae613f Nikos Skalkotos
                    self._update_firewalls(*firewall_states)
392 8bae613f Nikos Skalkotos
                finally:
393 8bae613f Nikos Skalkotos
                    self.umount()
394 10088af7 Nikos Skalkotos
395 74780ad6 Nikos Skalkotos
    def _shutdown(self):
396 74780ad6 Nikos Skalkotos
        """Shuts down the windows VM"""
397 10088af7 Nikos Skalkotos
        self._guest_exec(r'shutdown /s /t 5')
398 74780ad6 Nikos Skalkotos
399 520d991d Nikos Skalkotos
    def _wait_vm_boot(self, vm, fname, msg):
400 520d991d Nikos Skalkotos
        """Wait until a message appears on a file or the vm process dies"""
401 74780ad6 Nikos Skalkotos
402 82e8c357 Nikos Skalkotos
        for _ in range(self.sysprep_params['boot_timeout']):
403 10088af7 Nikos Skalkotos
            time.sleep(1)
404 10088af7 Nikos Skalkotos
            with open(fname) as f:
405 10088af7 Nikos Skalkotos
                for line in f:
406 10088af7 Nikos Skalkotos
                    if line.startswith(msg):
407 10088af7 Nikos Skalkotos
                        return True
408 8bae613f Nikos Skalkotos
            if not vm.isalive():
409 520d991d Nikos Skalkotos
                raise FatalError("Windows VM died unexpectedly!")
410 8bae613f Nikos Skalkotos
411 8bae613f Nikos Skalkotos
        raise FatalError("Windows VM booting timed out!")
412 74780ad6 Nikos Skalkotos
413 10088af7 Nikos Skalkotos
    def _disable_autologon(self):
414 10088af7 Nikos Skalkotos
        """Disable automatic logon on the windows image"""
415 10088af7 Nikos Skalkotos
416 10088af7 Nikos Skalkotos
        winlogon = \
417 10088af7 Nikos Skalkotos
            r'"HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon"'
418 10088af7 Nikos Skalkotos
419 10088af7 Nikos Skalkotos
        self._guest_exec('REG DELETE %s /v DefaultUserName /f' % winlogon)
420 10088af7 Nikos Skalkotos
        self._guest_exec('REG DELETE %s /v DefaultPassword /f' % winlogon)
421 10088af7 Nikos Skalkotos
        self._guest_exec('REG DELETE %s /v AutoAdminLogon /f' % winlogon)
422 640096fb Nikos Skalkotos
423 640096fb Nikos Skalkotos
    def _registry_file_path(self, regfile):
424 640096fb Nikos Skalkotos
        """Retrieves the case sensitive path to a registry file"""
425 640096fb Nikos Skalkotos
426 640096fb Nikos Skalkotos
        systemroot = self.g.inspect_get_windows_systemroot(self.root)
427 640096fb Nikos Skalkotos
        path = "%s/system32/config/%s" % (systemroot, regfile)
428 640096fb Nikos Skalkotos
        try:
429 640096fb Nikos Skalkotos
            path = self.g.case_sensitive_path(path)
430 47505e53 Nikos Skalkotos
        except RuntimeError as error:
431 640096fb Nikos Skalkotos
            raise FatalError("Unable to retrieve registry file: %s. Reason: %s"
432 47505e53 Nikos Skalkotos
                             % (regfile, str(error)))
433 640096fb Nikos Skalkotos
        return path
434 640096fb Nikos Skalkotos
435 10088af7 Nikos Skalkotos
    def _enable_os_monitor(self):
436 10088af7 Nikos Skalkotos
        """Add a script in the registry that will send a random string to the
437 10088af7 Nikos Skalkotos
        first serial port when the windows image finishes booting.
438 10088af7 Nikos Skalkotos
        """
439 10088af7 Nikos Skalkotos
440 10088af7 Nikos Skalkotos
        token = "".join(random.choice(string.ascii_letters) for x in range(16))
441 10088af7 Nikos Skalkotos
442 10088af7 Nikos Skalkotos
        path = self._registry_file_path('SOFTWARE')
443 10088af7 Nikos Skalkotos
        softwarefd, software = tempfile.mkstemp()
444 10088af7 Nikos Skalkotos
        try:
445 10088af7 Nikos Skalkotos
            os.close(softwarefd)
446 10088af7 Nikos Skalkotos
            self.g.download(path, software)
447 10088af7 Nikos Skalkotos
448 10088af7 Nikos Skalkotos
            h = hivex.Hivex(software, write=True)
449 10088af7 Nikos Skalkotos
450 10088af7 Nikos Skalkotos
            # Enable automatic logon.
451 10088af7 Nikos Skalkotos
            # This is needed because we need to execute a script that we add in
452 10088af7 Nikos Skalkotos
            # the RunOnce registry entry and those programs only get executed
453 10088af7 Nikos Skalkotos
            # when a user logs on. There is a RunServicesOnce registry entry
454 10088af7 Nikos Skalkotos
            # whose keys get executed in the background when the logon dialog
455 10088af7 Nikos Skalkotos
            # box first appears, but they seem to only work with services and
456 10088af7 Nikos Skalkotos
            # not arbitrary command line expressions :-(
457 10088af7 Nikos Skalkotos
            #
458 10088af7 Nikos Skalkotos
            # Instructions on how to turn on automatic logon in Windows can be
459 10088af7 Nikos Skalkotos
            # found here: http://support.microsoft.com/kb/324737
460 10088af7 Nikos Skalkotos
            #
461 10088af7 Nikos Skalkotos
            # Warning: Registry change will not work if the โ€œLogon Bannerโ€ is
462 10088af7 Nikos Skalkotos
            # defined on the server either by a Group Policy object (GPO) or by
463 10088af7 Nikos Skalkotos
            # a local policy.
464 10088af7 Nikos Skalkotos
465 10088af7 Nikos Skalkotos
            winlogon = h.root()
466 10088af7 Nikos Skalkotos
            for child in ('Microsoft', 'Windows NT', 'CurrentVersion',
467 10088af7 Nikos Skalkotos
                          'Winlogon'):
468 10088af7 Nikos Skalkotos
                winlogon = h.node_get_child(winlogon, child)
469 10088af7 Nikos Skalkotos
470 10088af7 Nikos Skalkotos
            h.node_set_value(
471 10088af7 Nikos Skalkotos
                winlogon,
472 10088af7 Nikos Skalkotos
                {'key': 'DefaultUserName', 't': 1,
473 10088af7 Nikos Skalkotos
                 'value': "Administrator".encode('utf-16le')})
474 10088af7 Nikos Skalkotos
            h.node_set_value(
475 10088af7 Nikos Skalkotos
                winlogon,
476 10088af7 Nikos Skalkotos
                {'key': 'DefaultPassword', 't': 1,
477 10088af7 Nikos Skalkotos
                 'value':  self.sysprep_params['password'].encode('utf-16le')})
478 10088af7 Nikos Skalkotos
            h.node_set_value(
479 10088af7 Nikos Skalkotos
                winlogon,
480 10088af7 Nikos Skalkotos
                {'key': 'AutoAdminLogon', 't': 1,
481 10088af7 Nikos Skalkotos
                 'value': "1".encode('utf-16le')})
482 10088af7 Nikos Skalkotos
483 10088af7 Nikos Skalkotos
            key = h.root()
484 10088af7 Nikos Skalkotos
            for child in ('Microsoft', 'Windows', 'CurrentVersion'):
485 10088af7 Nikos Skalkotos
                key = h.node_get_child(key, child)
486 10088af7 Nikos Skalkotos
487 10088af7 Nikos Skalkotos
            runonce = h.node_get_child(key, "RunOnce")
488 10088af7 Nikos Skalkotos
            if runonce is None:
489 10088af7 Nikos Skalkotos
                runonce = h.node_add_child(key, "RunOnce")
490 10088af7 Nikos Skalkotos
491 10088af7 Nikos Skalkotos
            value = (
492 10088af7 Nikos Skalkotos
                r'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe '
493 10088af7 Nikos Skalkotos
                r'-ExecutionPolicy RemoteSigned '
494 10088af7 Nikos Skalkotos
                r'"&{$port=new-Object System.IO.Ports.SerialPort COM1,9600,'
495 10088af7 Nikos Skalkotos
                r'None,8,one;$port.open();$port.WriteLine(\"' + token + r'\");'
496 10088af7 Nikos Skalkotos
                r'$port.Close()}"').encode('utf-16le')
497 10088af7 Nikos Skalkotos
498 10088af7 Nikos Skalkotos
            h.node_set_value(runonce,
499 10088af7 Nikos Skalkotos
                             {'key': "BootMonitor", 't': 1, 'value': value})
500 10088af7 Nikos Skalkotos
501 85e97ae0 Nikos Skalkotos
            value = (
502 85e97ae0 Nikos Skalkotos
                r'REG ADD HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion'
503 85e97ae0 Nikos Skalkotos
                r'\policies\system /v LocalAccountTokenFilterPolicy'
504 85e97ae0 Nikos Skalkotos
                r' /t REG_DWORD /d 1 /f').encode('utf-16le')
505 85e97ae0 Nikos Skalkotos
506 85e97ae0 Nikos Skalkotos
            h.node_set_value(runonce,
507 85e97ae0 Nikos Skalkotos
                             {'key': "UpdateRegistry", 't': 1, 'value': value})
508 85e97ae0 Nikos Skalkotos
509 10088af7 Nikos Skalkotos
            h.commit(None)
510 10088af7 Nikos Skalkotos
511 10088af7 Nikos Skalkotos
            self.g.upload(software, path)
512 10088af7 Nikos Skalkotos
        finally:
513 10088af7 Nikos Skalkotos
            os.unlink(software)
514 10088af7 Nikos Skalkotos
515 10088af7 Nikos Skalkotos
        return token
516 10088af7 Nikos Skalkotos
517 c2113d72 Nikos Skalkotos
    def _update_firewalls(self, domain, public, standard):
518 c2113d72 Nikos Skalkotos
        """Enables or disables the firewall for the Domain, the Public and the
519 c2113d72 Nikos Skalkotos
        Standard profile. Returns a triplete with the old values.
520 c2113d72 Nikos Skalkotos

521 c2113d72 Nikos Skalkotos
        1 will enable a firewall and 0 will disable it
522 c2113d72 Nikos Skalkotos
        """
523 c2113d72 Nikos Skalkotos
524 c2113d72 Nikos Skalkotos
        if domain not in (0, 1):
525 c2113d72 Nikos Skalkotos
            raise ValueError("Valid values for domain parameter are 0 and 1")
526 c2113d72 Nikos Skalkotos
527 c2113d72 Nikos Skalkotos
        if public not in (0, 1):
528 c2113d72 Nikos Skalkotos
            raise ValueError("Valid values for public parameter are 0 and 1")
529 c2113d72 Nikos Skalkotos
530 c2113d72 Nikos Skalkotos
        if standard not in (0, 1):
531 c2113d72 Nikos Skalkotos
            raise ValueError("Valid values for standard parameter are 0 and 1")
532 c2113d72 Nikos Skalkotos
533 c2113d72 Nikos Skalkotos
        path = self._registry_file_path("SYSTEM")
534 c2113d72 Nikos Skalkotos
        systemfd, system = tempfile.mkstemp()
535 c2113d72 Nikos Skalkotos
        try:
536 c2113d72 Nikos Skalkotos
            os.close(systemfd)
537 c2113d72 Nikos Skalkotos
            self.g.download(path, system)
538 c2113d72 Nikos Skalkotos
539 c2113d72 Nikos Skalkotos
            h = hivex.Hivex(system, write=True)
540 c2113d72 Nikos Skalkotos
541 c2113d72 Nikos Skalkotos
            select = h.node_get_child(h.root(), 'Select')
542 c2113d72 Nikos Skalkotos
            current_value = h.node_get_value(select, 'Current')
543 c2113d72 Nikos Skalkotos
544 c2113d72 Nikos Skalkotos
            # expecting a little endian dword
545 c2113d72 Nikos Skalkotos
            assert h.value_type(current_value)[1] == 4
546 c2113d72 Nikos Skalkotos
            current = "%03d" % h.value_dword(current_value)
547 c2113d72 Nikos Skalkotos
548 c2113d72 Nikos Skalkotos
            firewall_policy = h.root()
549 c2113d72 Nikos Skalkotos
            for child in ('ControlSet%s' % current, 'services', 'SharedAccess',
550 c2113d72 Nikos Skalkotos
                          'Parameters', 'FirewallPolicy'):
551 c2113d72 Nikos Skalkotos
                firewall_policy = h.node_get_child(firewall_policy, child)
552 c2113d72 Nikos Skalkotos
553 c2113d72 Nikos Skalkotos
            old_values = []
554 c2113d72 Nikos Skalkotos
            new_values = [domain, public, standard]
555 c2113d72 Nikos Skalkotos
            for profile in ('Domain', 'Public', 'Standard'):
556 c2113d72 Nikos Skalkotos
                node = h.node_get_child(firewall_policy, '%sProfile' % profile)
557 c2113d72 Nikos Skalkotos
558 c2113d72 Nikos Skalkotos
                old_value = h.node_get_value(node, 'EnableFirewall')
559 c2113d72 Nikos Skalkotos
560 c2113d72 Nikos Skalkotos
                # expecting a little endian dword
561 c2113d72 Nikos Skalkotos
                assert h.value_type(old_value)[1] == 4
562 c2113d72 Nikos Skalkotos
                old_values.append(h.value_dword(old_value))
563 c2113d72 Nikos Skalkotos
564 85e97ae0 Nikos Skalkotos
                h.node_set_value(
565 85e97ae0 Nikos Skalkotos
                    node, {'key': 'EnableFirewall', 't': 4L,
566 85e97ae0 Nikos Skalkotos
                           'value': struct.pack("<I", new_values.pop(0))})
567 c2113d72 Nikos Skalkotos
568 c2113d72 Nikos Skalkotos
            h.commit(None)
569 c2113d72 Nikos Skalkotos
            self.g.upload(system, path)
570 c2113d72 Nikos Skalkotos
571 c2113d72 Nikos Skalkotos
        finally:
572 c2113d72 Nikos Skalkotos
            os.unlink(system)
573 c2113d72 Nikos Skalkotos
574 c2113d72 Nikos Skalkotos
        return old_values
575 c2113d72 Nikos Skalkotos
576 640096fb Nikos Skalkotos
    def _update_uac_remote_setting(self, value):
577 640096fb Nikos Skalkotos
        """Updates the registry key value:
578 640096fb Nikos Skalkotos
        [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies
579 640096fb Nikos Skalkotos
        \System]"LocalAccountTokenFilterPolicy"
580 640096fb Nikos Skalkotos

581 640096fb Nikos Skalkotos
        value = 1 will disable the UAC remote restrictions
582 640096fb Nikos Skalkotos
        value = 0 will enable the UAC remote restrictions
583 640096fb Nikos Skalkotos

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

586 640096fb Nikos Skalkotos
        Returns:
587 640096fb Nikos Skalkotos
            True if the key is changed
588 640096fb Nikos Skalkotos
            False if the key is unchanged
589 640096fb Nikos Skalkotos
        """
590 640096fb Nikos Skalkotos
591 640096fb Nikos Skalkotos
        if value not in (0, 1):
592 640096fb Nikos Skalkotos
            raise ValueError("Valid values for value parameter are 0 and 1")
593 640096fb Nikos Skalkotos
594 640096fb Nikos Skalkotos
        path = self._registry_file_path('SOFTWARE')
595 640096fb Nikos Skalkotos
        softwarefd, software = tempfile.mkstemp()
596 640096fb Nikos Skalkotos
        try:
597 640096fb Nikos Skalkotos
            os.close(softwarefd)
598 640096fb Nikos Skalkotos
            self.g.download(path, software)
599 640096fb Nikos Skalkotos
600 640096fb Nikos Skalkotos
            h = hivex.Hivex(software, write=True)
601 640096fb Nikos Skalkotos
602 640096fb Nikos Skalkotos
            key = h.root()
603 640096fb Nikos Skalkotos
            for child in ('Microsoft', 'Windows', 'CurrentVersion', 'Policies',
604 640096fb Nikos Skalkotos
                          'System'):
605 640096fb Nikos Skalkotos
                key = h.node_get_child(key, child)
606 640096fb Nikos Skalkotos
607 640096fb Nikos Skalkotos
            policy = None
608 640096fb Nikos Skalkotos
            for val in h.node_values(key):
609 640096fb Nikos Skalkotos
                if h.value_key(val) == "LocalAccountTokenFilterPolicy":
610 640096fb Nikos Skalkotos
                    policy = val
611 640096fb Nikos Skalkotos
612 640096fb Nikos Skalkotos
            if policy is not None:
613 640096fb Nikos Skalkotos
                dword = h.value_dword(policy)
614 640096fb Nikos Skalkotos
                if dword == value:
615 640096fb Nikos Skalkotos
                    return False
616 640096fb Nikos Skalkotos
            elif value == 0:
617 640096fb Nikos Skalkotos
                return False
618 640096fb Nikos Skalkotos
619 85e97ae0 Nikos Skalkotos
            new_value = {'key': "LocalAccountTokenFilterPolicy", 't': 4L,
620 85e97ae0 Nikos Skalkotos
                         'value': struct.pack("<I", value)}
621 640096fb Nikos Skalkotos
622 640096fb Nikos Skalkotos
            h.node_set_value(key, new_value)
623 640096fb Nikos Skalkotos
            h.commit(None)
624 640096fb Nikos Skalkotos
625 640096fb Nikos Skalkotos
            self.g.upload(software, path)
626 640096fb Nikos Skalkotos
627 640096fb Nikos Skalkotos
        finally:
628 640096fb Nikos Skalkotos
            os.unlink(software)
629 640096fb Nikos Skalkotos
630 640096fb Nikos Skalkotos
        return True
631 640096fb Nikos Skalkotos
632 b8c0848c Nikos Skalkotos
    def _do_collect_metadata(self):
633 b8c0848c Nikos Skalkotos
        """Collect metadata about the OS"""
634 b8c0848c Nikos Skalkotos
        super(Windows, self)._do_collect_metadata()
635 76b200cf Nikos Skalkotos
        self.meta["USERS"] = " ".join(self._get_users())
636 76b200cf Nikos Skalkotos
637 76b200cf Nikos Skalkotos
    def _get_users(self):
638 121f3bc0 Nikos Skalkotos
        """Returns a list of users found in the images"""
639 76b200cf Nikos Skalkotos
        samfd, sam = tempfile.mkstemp()
640 76b200cf Nikos Skalkotos
        try:
641 640096fb Nikos Skalkotos
            os.close(samfd)
642 47505e53 Nikos Skalkotos
            self.g.download(self._registry_file_path('SAM'), sam)
643 76b200cf Nikos Skalkotos
644 76b200cf Nikos Skalkotos
            h = hivex.Hivex(sam)
645 76b200cf Nikos Skalkotos
646 759e5d5b Nikos Skalkotos
            # Navigate to /SAM/Domains/Account/Users
647 759e5d5b Nikos Skalkotos
            users_node = h.root()
648 759e5d5b Nikos Skalkotos
            for child in ('SAM', 'Domains', 'Account', 'Users'):
649 759e5d5b Nikos Skalkotos
                users_node = h.node_get_child(users_node, child)
650 759e5d5b Nikos Skalkotos
651 76b200cf Nikos Skalkotos
            # Navigate to /SAM/Domains/Account/Users/Names
652 759e5d5b Nikos Skalkotos
            names_node = h.node_get_child(users_node, 'Names')
653 76b200cf Nikos Skalkotos
654 759e5d5b Nikos Skalkotos
            # HKEY_LOCAL_MACHINE\SAM\SAM\Domains\Account\Users\%RID%
655 759e5d5b Nikos Skalkotos
            # HKEY_LOCAL_MACHINE\SAM\SAM\Domains\Account\Users\Names\%Username%
656 759e5d5b Nikos Skalkotos
            #
657 759e5d5b Nikos Skalkotos
            # The RID (relative identifier) of each user is stored as the type!
658 759e5d5b Nikos Skalkotos
            # (not the value) of the default key of the node under Names whose
659 759e5d5b Nikos Skalkotos
            # name is the user's username. Under the RID node, there in a F
660 759e5d5b Nikos Skalkotos
            # value that contains information about this user account.
661 759e5d5b Nikos Skalkotos
            #
662 759e5d5b Nikos Skalkotos
            # See sam.h of the chntpw project on how to translate the F value
663 759e5d5b Nikos Skalkotos
            # of an account in the registry. Bytes 56 & 57 are the account type
664 759e5d5b Nikos Skalkotos
            # and status flags. The first bit is the 'account disabled' bit
665 759e5d5b Nikos Skalkotos
            disabled = lambda f: int(f[56].encode('hex'), 16) & 0x01
666 759e5d5b Nikos Skalkotos
667 759e5d5b Nikos Skalkotos
            users = []
668 759e5d5b Nikos Skalkotos
            for user_node in h.node_children(names_node):
669 759e5d5b Nikos Skalkotos
                username = h.node_name(user_node)
670 759e5d5b Nikos Skalkotos
                rid = h.value_type(h.node_get_value(user_node, ""))[0]
671 759e5d5b Nikos Skalkotos
                # if RID is 500 (=0x1f4), the corresponding node name under
672 759e5d5b Nikos Skalkotos
                # Users is '000001F4'
673 759e5d5b Nikos Skalkotos
                key = ("%8.x" % rid).replace(' ', '0').upper()
674 759e5d5b Nikos Skalkotos
                rid_node = h.node_get_child(users_node, key)
675 759e5d5b Nikos Skalkotos
                f_value = h.value_value(h.node_get_value(rid_node, 'F'))[1]
676 759e5d5b Nikos Skalkotos
677 759e5d5b Nikos Skalkotos
                if disabled(f_value):
678 759e5d5b Nikos Skalkotos
                    self.out.warn("Found disabled `%s' account!" % username)
679 759e5d5b Nikos Skalkotos
                    continue
680 759e5d5b Nikos Skalkotos
681 759e5d5b Nikos Skalkotos
                users.append(username)
682 76b200cf Nikos Skalkotos
683 76b200cf Nikos Skalkotos
        finally:
684 76b200cf Nikos Skalkotos
            os.unlink(sam)
685 76b200cf Nikos Skalkotos
686 76b200cf Nikos Skalkotos
        # Filter out the guest account
687 759e5d5b Nikos Skalkotos
        return users
688 aa2062ba Nikos Skalkotos
689 8bae613f Nikos Skalkotos
    def _check_connectivity(self):
690 8bae613f Nikos Skalkotos
        """Check if winexe works on the Windows VM"""
691 8bae613f Nikos Skalkotos
692 82e8c357 Nikos Skalkotos
        retries = self.sysprep_params['connection_retries']
693 82e8c357 Nikos Skalkotos
        # If the connection_retries parameter is set to 0 disable the
694 82e8c357 Nikos Skalkotos
        # connectivity check
695 82e8c357 Nikos Skalkotos
        if retries == 0:
696 82e8c357 Nikos Skalkotos
            return True
697 82e8c357 Nikos Skalkotos
698 8bae613f Nikos Skalkotos
        passwd = self.sysprep_params['password']
699 8bae613f Nikos Skalkotos
        winexe = WinEXE('Administrator', passwd, 'localhost')
700 8bae613f Nikos Skalkotos
        winexe.uninstall().debug(9)
701 8bae613f Nikos Skalkotos
702 82e8c357 Nikos Skalkotos
        for i in range(retries):
703 8bae613f Nikos Skalkotos
            (stdout, stderr, rc) = winexe.run('cmd /C')
704 8bae613f Nikos Skalkotos
            if rc == 0:
705 8bae613f Nikos Skalkotos
                return True
706 8bae613f Nikos Skalkotos
            log = tempfile.NamedTemporaryFile(delete=False)
707 8bae613f Nikos Skalkotos
            try:
708 8bae613f Nikos Skalkotos
                log.file.write(stdout)
709 8bae613f Nikos Skalkotos
            finally:
710 8bae613f Nikos Skalkotos
                log.close()
711 47505e53 Nikos Skalkotos
            self.out.output("failed! See: `%s' for the full output" % log.name)
712 82e8c357 Nikos Skalkotos
            if i < retries - 1:
713 82e8c357 Nikos Skalkotos
                self.out.output("retrying ...", False)
714 82e8c357 Nikos Skalkotos
715 82e8c357 Nikos Skalkotos
        raise FatalError("Connection to the Windows VM failed after %d retries"
716 82e8c357 Nikos Skalkotos
                         % retries)
717 8bae613f Nikos Skalkotos
718 10088af7 Nikos Skalkotos
    def _guest_exec(self, command, fatal=True):
719 10088af7 Nikos Skalkotos
        """Execute a command on a windows VM"""
720 10088af7 Nikos Skalkotos
721 f5873ed0 Nikos Skalkotos
        passwd = self.sysprep_params['password']
722 74780ad6 Nikos Skalkotos
723 f5873ed0 Nikos Skalkotos
        winexe = WinEXE('Administrator', passwd, 'localhost')
724 f5873ed0 Nikos Skalkotos
        winexe.runas('Administrator', passwd).uninstall()
725 f5873ed0 Nikos Skalkotos
726 f5873ed0 Nikos Skalkotos
        try:
727 f5873ed0 Nikos Skalkotos
            (stdout, stderr, rc) = winexe.run(command)
728 f5873ed0 Nikos Skalkotos
        except WinexeTimeout:
729 f5873ed0 Nikos Skalkotos
            FatalError("Command: `%s' timeout out." % command)
730 74780ad6 Nikos Skalkotos
731 10088af7 Nikos Skalkotos
        if rc != 0 and fatal:
732 10088af7 Nikos Skalkotos
            reason = stderr if len(stderr) else stdout
733 c53cc8dd Nikos Skalkotos
            self.out.output("Command: `%s' failed (rc=%d). Reason: %s" %
734 c53cc8dd Nikos Skalkotos
                            (command, rc, reason))
735 c53cc8dd Nikos Skalkotos
            raise FatalError("Command: `%s' failed (rc=%d). Reason: %s" %
736 c53cc8dd Nikos Skalkotos
                             (command, rc, reason))
737 10088af7 Nikos Skalkotos
738 10088af7 Nikos Skalkotos
        return (stdout, stderr, rc)
739 74780ad6 Nikos Skalkotos
740 8bae613f Nikos Skalkotos
741 8bae613f Nikos Skalkotos
class _VM(object):
742 8bae613f Nikos Skalkotos
    """Windows Virtual Machine"""
743 82e8c357 Nikos Skalkotos
    def __init__(self, disk, serial, params):
744 8bae613f Nikos Skalkotos
        """Create _VM instance
745 8bae613f Nikos Skalkotos

746 8bae613f Nikos Skalkotos
            disk: VM's hard disk
747 8bae613f Nikos Skalkotos
            serial: File to save the output of the serial port
748 8bae613f Nikos Skalkotos
        """
749 8bae613f Nikos Skalkotos
750 8bae613f Nikos Skalkotos
        self.disk = disk
751 8bae613f Nikos Skalkotos
        self.serial = serial
752 82e8c357 Nikos Skalkotos
        self.params = params
753 8bae613f Nikos Skalkotos
754 8bae613f Nikos Skalkotos
        def random_mac():
755 47505e53 Nikos Skalkotos
            """creates a random mac address"""
756 8bae613f Nikos Skalkotos
            mac = [0x00, 0x16, 0x3e,
757 8bae613f Nikos Skalkotos
                   random.randint(0x00, 0x7f),
758 8bae613f Nikos Skalkotos
                   random.randint(0x00, 0xff),
759 8bae613f Nikos Skalkotos
                   random.randint(0x00, 0xff)]
760 8bae613f Nikos Skalkotos
761 47505e53 Nikos Skalkotos
            return ':'.join(['%02x' % x for x in mac])
762 8bae613f Nikos Skalkotos
763 8bae613f Nikos Skalkotos
        # Use ganeti's VNC port range for a random vnc port
764 8bae613f Nikos Skalkotos
        self.display = random.randint(11000, 14999) - 5900
765 8bae613f Nikos Skalkotos
766 4246a133 Nikos Skalkotos
        kvm = get_kvm_binary()
767 4246a133 Nikos Skalkotos
768 4246a133 Nikos Skalkotos
        if kvm is None:
769 4246a133 Nikos Skalkotos
            FatalError("Can't find the kvm binary")
770 4246a133 Nikos Skalkotos
771 8bae613f Nikos Skalkotos
        args = [
772 4246a133 Nikos Skalkotos
            kvm, '-smp', '1', '-m', '1024', '-drive',
773 8bae613f Nikos Skalkotos
            'file=%s,format=raw,cache=unsafe,if=virtio' % self.disk,
774 8bae613f Nikos Skalkotos
            '-netdev', 'type=user,hostfwd=tcp::445-:445,id=netdev0',
775 8bae613f Nikos Skalkotos
            '-device', 'virtio-net-pci,mac=%s,netdev=netdev0' % random_mac(),
776 8bae613f Nikos Skalkotos
            '-vnc', ':%d' % self.display, '-serial', 'file:%s' % self.serial,
777 8bae613f Nikos Skalkotos
            '-monitor', 'stdio']
778 8bae613f Nikos Skalkotos
779 8bae613f Nikos Skalkotos
        self.process = subprocess.Popen(args, stdin=subprocess.PIPE,
780 8bae613f Nikos Skalkotos
                                        stdout=subprocess.PIPE)
781 8bae613f Nikos Skalkotos
782 8bae613f Nikos Skalkotos
    def isalive(self):
783 8bae613f Nikos Skalkotos
        """Check if the VM is still alive"""
784 8bae613f Nikos Skalkotos
        return self.process.poll() is None
785 8bae613f Nikos Skalkotos
786 8bae613f Nikos Skalkotos
    def destroy(self):
787 8bae613f Nikos Skalkotos
        """Destroy the VM"""
788 8bae613f Nikos Skalkotos
789 8bae613f Nikos Skalkotos
        if not self.isalive():
790 8bae613f Nikos Skalkotos
            return
791 8bae613f Nikos Skalkotos
792 8bae613f Nikos Skalkotos
        def handler(signum, frame):
793 8bae613f Nikos Skalkotos
            self.process.terminate()
794 8bae613f Nikos Skalkotos
            time.sleep(1)
795 8bae613f Nikos Skalkotos
            if self.isalive():
796 8bae613f Nikos Skalkotos
                self.process.kill()
797 8bae613f Nikos Skalkotos
            self.process.wait()
798 8bae613f Nikos Skalkotos
            raise FatalError("VM destroy timed-out")
799 8bae613f Nikos Skalkotos
800 8bae613f Nikos Skalkotos
        signal.signal(signal.SIGALRM, handler)
801 8bae613f Nikos Skalkotos
802 82e8c357 Nikos Skalkotos
        signal.alarm(self.params['shutdown_timeout'])
803 8bae613f Nikos Skalkotos
        self.process.communicate(input="system_powerdown\n")
804 8bae613f Nikos Skalkotos
        signal.alarm(0)
805 8bae613f Nikos Skalkotos
806 8bae613f Nikos Skalkotos
    def wait(self, timeout=0):
807 8bae613f Nikos Skalkotos
        """Wait for the VM to terminate"""
808 8bae613f Nikos Skalkotos
809 8bae613f Nikos Skalkotos
        def handler(signum, frame):
810 8bae613f Nikos Skalkotos
            self.destroy()
811 8bae613f Nikos Skalkotos
            raise FatalError("VM wait timed-out.")
812 8bae613f Nikos Skalkotos
813 8bae613f Nikos Skalkotos
        signal.signal(signal.SIGALRM, handler)
814 8bae613f Nikos Skalkotos
815 8bae613f Nikos Skalkotos
        signal.alarm(timeout)
816 8bae613f Nikos Skalkotos
        stdout, stderr = self.process.communicate()
817 8bae613f Nikos Skalkotos
        signal.alarm(0)
818 8bae613f Nikos Skalkotos
819 8bae613f Nikos Skalkotos
        return (stdout, stderr, self.process.poll())
820 8bae613f Nikos Skalkotos
821 aa2062ba Nikos Skalkotos
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :