Statistics
| Branch: | Tag: | Revision:

root / image_creator / os_type / windows.py @ 0f089865

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

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

584 55133880 Nikos Skalkotos
        value = 1 will disable the UAC remote restrictions
585 55133880 Nikos Skalkotos
        value = 0 will enable the UAC remote restrictions
586 55133880 Nikos Skalkotos

587 55133880 Nikos Skalkotos
        For more info see here: http://support.microsoft.com/kb/951016
588 55133880 Nikos Skalkotos

589 55133880 Nikos Skalkotos
        Returns:
590 55133880 Nikos Skalkotos
            True if the key is changed
591 55133880 Nikos Skalkotos
            False if the key is unchanged
592 55133880 Nikos Skalkotos
        """
593 55133880 Nikos Skalkotos
594 55133880 Nikos Skalkotos
        if value not in (0, 1):
595 55133880 Nikos Skalkotos
            raise ValueError("Valid values for value parameter are 0 and 1")
596 55133880 Nikos Skalkotos
597 55133880 Nikos Skalkotos
        path = self._registry_file_path('SOFTWARE')
598 55133880 Nikos Skalkotos
        softwarefd, software = tempfile.mkstemp()
599 55133880 Nikos Skalkotos
        try:
600 55133880 Nikos Skalkotos
            os.close(softwarefd)
601 55133880 Nikos Skalkotos
            self.g.download(path, software)
602 55133880 Nikos Skalkotos
603 55133880 Nikos Skalkotos
            h = hivex.Hivex(software, write=True)
604 55133880 Nikos Skalkotos
605 55133880 Nikos Skalkotos
            key = h.root()
606 55133880 Nikos Skalkotos
            for child in ('Microsoft', 'Windows', 'CurrentVersion', 'Policies',
607 55133880 Nikos Skalkotos
                          'System'):
608 55133880 Nikos Skalkotos
                key = h.node_get_child(key, child)
609 55133880 Nikos Skalkotos
610 55133880 Nikos Skalkotos
            policy = None
611 55133880 Nikos Skalkotos
            for val in h.node_values(key):
612 55133880 Nikos Skalkotos
                if h.value_key(val) == "LocalAccountTokenFilterPolicy":
613 55133880 Nikos Skalkotos
                    policy = val
614 55133880 Nikos Skalkotos
615 55133880 Nikos Skalkotos
            if policy is not None:
616 55133880 Nikos Skalkotos
                dword = h.value_dword(policy)
617 55133880 Nikos Skalkotos
                if dword == value:
618 55133880 Nikos Skalkotos
                    return False
619 55133880 Nikos Skalkotos
            elif value == 0:
620 55133880 Nikos Skalkotos
                return False
621 55133880 Nikos Skalkotos
622 28d354ce Nikos Skalkotos
            new_value = {'key': "LocalAccountTokenFilterPolicy", 't': 4L,
623 28d354ce Nikos Skalkotos
                         'value': struct.pack("<I", value)}
624 55133880 Nikos Skalkotos
625 55133880 Nikos Skalkotos
            h.node_set_value(key, new_value)
626 55133880 Nikos Skalkotos
            h.commit(None)
627 55133880 Nikos Skalkotos
628 55133880 Nikos Skalkotos
            self.g.upload(software, path)
629 55133880 Nikos Skalkotos
630 55133880 Nikos Skalkotos
        finally:
631 55133880 Nikos Skalkotos
            os.unlink(software)
632 55133880 Nikos Skalkotos
633 55133880 Nikos Skalkotos
        return True
634 55133880 Nikos Skalkotos
635 b8c0848c Nikos Skalkotos
    def _do_collect_metadata(self):
636 b8c0848c Nikos Skalkotos
        """Collect metadata about the OS"""
637 b8c0848c Nikos Skalkotos
        super(Windows, self)._do_collect_metadata()
638 76b200cf Nikos Skalkotos
        self.meta["USERS"] = " ".join(self._get_users())
639 76b200cf Nikos Skalkotos
640 76b200cf Nikos Skalkotos
    def _get_users(self):
641 121f3bc0 Nikos Skalkotos
        """Returns a list of users found in the images"""
642 55133880 Nikos Skalkotos
        path = self._registry_file_path('SAM')
643 76b200cf Nikos Skalkotos
        samfd, sam = tempfile.mkstemp()
644 76b200cf Nikos Skalkotos
        try:
645 55133880 Nikos Skalkotos
            os.close(samfd)
646 76b200cf Nikos Skalkotos
            self.g.download(path, sam)
647 76b200cf Nikos Skalkotos
648 76b200cf Nikos Skalkotos
            h = hivex.Hivex(sam)
649 76b200cf Nikos Skalkotos
650 76b200cf Nikos Skalkotos
            key = h.root()
651 76b200cf Nikos Skalkotos
            # Navigate to /SAM/Domains/Account/Users/Names
652 76b200cf Nikos Skalkotos
            for child in ('SAM', 'Domains', 'Account', 'Users', 'Names'):
653 76b200cf Nikos Skalkotos
                key = h.node_get_child(key, child)
654 76b200cf Nikos Skalkotos
655 76b200cf Nikos Skalkotos
            users = [h.node_name(x) for x in h.node_children(key)]
656 76b200cf Nikos Skalkotos
657 76b200cf Nikos Skalkotos
        finally:
658 76b200cf Nikos Skalkotos
            os.unlink(sam)
659 76b200cf Nikos Skalkotos
660 76b200cf Nikos Skalkotos
        # Filter out the guest account
661 76b200cf Nikos Skalkotos
        return filter(lambda x: x != "Guest", users)
662 aa2062ba Nikos Skalkotos
663 0f089865 Nikos Skalkotos
    def _check_connectivity(self):
664 0f089865 Nikos Skalkotos
        """Check if winexe works on the Windows VM"""
665 0f089865 Nikos Skalkotos
666 0f089865 Nikos Skalkotos
        passwd = self.sysprep_params['password']
667 0f089865 Nikos Skalkotos
        winexe = WinEXE('Administrator', passwd, 'localhost')
668 0f089865 Nikos Skalkotos
        winexe.uninstall().debug(9)
669 0f089865 Nikos Skalkotos
670 0f089865 Nikos Skalkotos
        for i in range(CONNECTION_RETRIES):
671 0f089865 Nikos Skalkotos
            (stdout, stderr, rc) = winexe.run('cmd /C')
672 0f089865 Nikos Skalkotos
            if rc == 0:
673 0f089865 Nikos Skalkotos
                return True
674 0f089865 Nikos Skalkotos
            log = tempfile.NamedTemporaryFile(delete=False)
675 0f089865 Nikos Skalkotos
            try:
676 0f089865 Nikos Skalkotos
                log.file.write(stdout)
677 0f089865 Nikos Skalkotos
            finally:
678 0f089865 Nikos Skalkotos
                log.close()
679 0f089865 Nikos Skalkotos
            self.out.output("failed! See: `%' for the full output" % log.name)
680 0f089865 Nikos Skalkotos
            if i < CONNECTION_RETRIES - 1:
681 0f089865 Nikos Skalkotos
                self.out.output("Retrying ...", False)
682 0f089865 Nikos Skalkotos
        raise FatalError("Connection to the VM failed after %d retries" %
683 0f089865 Nikos Skalkotos
                         CONNECTION_RETRIES)
684 0f089865 Nikos Skalkotos
685 0db17fcf Nikos Skalkotos
    def _guest_exec(self, command, fatal=True):
686 0db17fcf Nikos Skalkotos
        """Execute a command on a windows VM"""
687 0db17fcf Nikos Skalkotos
688 63af9c37 Nikos Skalkotos
        passwd = self.sysprep_params['password']
689 12c97404 Nikos Skalkotos
690 63af9c37 Nikos Skalkotos
        winexe = WinEXE('Administrator', passwd, 'localhost')
691 63af9c37 Nikos Skalkotos
        winexe.runas('Administrator', passwd).uninstall()
692 63af9c37 Nikos Skalkotos
693 63af9c37 Nikos Skalkotos
        try:
694 63af9c37 Nikos Skalkotos
            (stdout, stderr, rc) = winexe.run(command)
695 63af9c37 Nikos Skalkotos
        except WinexeTimeout:
696 63af9c37 Nikos Skalkotos
            FatalError("Command: `%s' timeout out." % command)
697 12c97404 Nikos Skalkotos
698 0db17fcf Nikos Skalkotos
        if rc != 0 and fatal:
699 0db17fcf Nikos Skalkotos
            reason = stderr if len(stderr) else stdout
700 b1d55773 Nikos Skalkotos
            self.out.output("Command: `%s' failed (rc=%d). Reason: %s" %
701 b1d55773 Nikos Skalkotos
                            (command, rc, reason))
702 b1d55773 Nikos Skalkotos
            raise FatalError("Command: `%s' failed (rc=%d). Reason: %s" %
703 b1d55773 Nikos Skalkotos
                             (command, rc, reason))
704 0db17fcf Nikos Skalkotos
705 0db17fcf Nikos Skalkotos
        return (stdout, stderr, rc)
706 12c97404 Nikos Skalkotos
707 0f089865 Nikos Skalkotos
708 0f089865 Nikos Skalkotos
class _VM(object):
709 0f089865 Nikos Skalkotos
    """Windows Virtual Machine"""
710 0f089865 Nikos Skalkotos
    def __init__(self, disk, serial):
711 0f089865 Nikos Skalkotos
        """Create _VM instance
712 0f089865 Nikos Skalkotos

713 0f089865 Nikos Skalkotos
            disk: VM's hard disk
714 0f089865 Nikos Skalkotos
            serial: File to save the output of the serial port
715 0f089865 Nikos Skalkotos
        """
716 0f089865 Nikos Skalkotos
717 0f089865 Nikos Skalkotos
        self.disk = disk
718 0f089865 Nikos Skalkotos
        self.serial = serial
719 0f089865 Nikos Skalkotos
720 0f089865 Nikos Skalkotos
        def random_mac():
721 0f089865 Nikos Skalkotos
            mac = [0x00, 0x16, 0x3e,
722 0f089865 Nikos Skalkotos
                   random.randint(0x00, 0x7f),
723 0f089865 Nikos Skalkotos
                   random.randint(0x00, 0xff),
724 0f089865 Nikos Skalkotos
                   random.randint(0x00, 0xff)]
725 0f089865 Nikos Skalkotos
726 0f089865 Nikos Skalkotos
            return ':'.join(map(lambda x: "%02x" % x, mac))
727 0f089865 Nikos Skalkotos
728 0f089865 Nikos Skalkotos
        # Use ganeti's VNC port range for a random vnc port
729 0f089865 Nikos Skalkotos
        self.display = random.randint(11000, 14999) - 5900
730 0f089865 Nikos Skalkotos
731 0f089865 Nikos Skalkotos
        args = [
732 0f089865 Nikos Skalkotos
            'kvm', '-smp', '1', '-m', '1024', '-drive',
733 0f089865 Nikos Skalkotos
            'file=%s,format=raw,cache=unsafe,if=virtio' % self.disk,
734 0f089865 Nikos Skalkotos
            '-netdev', 'type=user,hostfwd=tcp::445-:445,id=netdev0',
735 0f089865 Nikos Skalkotos
            '-device', 'virtio-net-pci,mac=%s,netdev=netdev0' % random_mac(),
736 0f089865 Nikos Skalkotos
            '-vnc', ':%d' % self.display, '-serial', 'file:%s' % self.serial,
737 0f089865 Nikos Skalkotos
            '-monitor', 'stdio']
738 0f089865 Nikos Skalkotos
739 0f089865 Nikos Skalkotos
        self.process = subprocess.Popen(args, stdin=subprocess.PIPE,
740 0f089865 Nikos Skalkotos
                                        stdout=subprocess.PIPE)
741 0f089865 Nikos Skalkotos
742 0f089865 Nikos Skalkotos
    def isalive(self):
743 0f089865 Nikos Skalkotos
        """Check if the VM is still alive"""
744 0f089865 Nikos Skalkotos
        return self.process.poll() is None
745 0f089865 Nikos Skalkotos
746 0f089865 Nikos Skalkotos
    def destroy(self):
747 0f089865 Nikos Skalkotos
        """Destroy the VM"""
748 0f089865 Nikos Skalkotos
749 0f089865 Nikos Skalkotos
        if not self.isalive():
750 0f089865 Nikos Skalkotos
            return
751 0f089865 Nikos Skalkotos
752 0f089865 Nikos Skalkotos
        def handler(signum, frame):
753 0f089865 Nikos Skalkotos
            self.process.terminate()
754 0f089865 Nikos Skalkotos
            time.sleep(1)
755 0f089865 Nikos Skalkotos
            if self.isalive():
756 0f089865 Nikos Skalkotos
                self.process.kill()
757 0f089865 Nikos Skalkotos
            self.process.wait()
758 0f089865 Nikos Skalkotos
            self.out.output("timed-out")
759 0f089865 Nikos Skalkotos
            raise FatalError("VM destroy timed-out")
760 0f089865 Nikos Skalkotos
761 0f089865 Nikos Skalkotos
        signal.signal(signal.SIGALRM, handler)
762 0f089865 Nikos Skalkotos
763 0f089865 Nikos Skalkotos
        signal.alarm(SHUTDOWN_TIMEOUT)
764 0f089865 Nikos Skalkotos
        self.process.communicate(input="system_powerdown\n")
765 0f089865 Nikos Skalkotos
        signal.alarm(0)
766 0f089865 Nikos Skalkotos
767 0f089865 Nikos Skalkotos
    def wait(self, timeout=0):
768 0f089865 Nikos Skalkotos
        """Wait for the VM to terminate"""
769 0f089865 Nikos Skalkotos
770 0f089865 Nikos Skalkotos
        def handler(signum, frame):
771 0f089865 Nikos Skalkotos
            self.destroy()
772 0f089865 Nikos Skalkotos
            raise FatalError("VM wait timed-out.")
773 0f089865 Nikos Skalkotos
774 0f089865 Nikos Skalkotos
        signal.signal(signal.SIGALRM, handler)
775 0f089865 Nikos Skalkotos
776 0f089865 Nikos Skalkotos
        signal.alarm(timeout)
777 0f089865 Nikos Skalkotos
        stdout, stderr = self.process.communicate()
778 0f089865 Nikos Skalkotos
        signal.alarm(0)
779 0f089865 Nikos Skalkotos
780 0f089865 Nikos Skalkotos
        return (stdout, stderr, self.process.poll())
781 0f089865 Nikos Skalkotos
782 aa2062ba Nikos Skalkotos
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :