Statistics
| Branch: | Tag: | Revision:

root / image_creator / os_type / windows.py @ 8bae613f

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

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

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

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

589 640096fb Nikos Skalkotos
        Returns:
590 640096fb Nikos Skalkotos
            True if the key is changed
591 640096fb Nikos Skalkotos
            False if the key is unchanged
592 640096fb Nikos Skalkotos
        """
593 640096fb Nikos Skalkotos
594 640096fb Nikos Skalkotos
        if value not in (0, 1):
595 640096fb Nikos Skalkotos
            raise ValueError("Valid values for value parameter are 0 and 1")
596 640096fb Nikos Skalkotos
597 640096fb Nikos Skalkotos
        path = self._registry_file_path('SOFTWARE')
598 640096fb Nikos Skalkotos
        softwarefd, software = tempfile.mkstemp()
599 640096fb Nikos Skalkotos
        try:
600 640096fb Nikos Skalkotos
            os.close(softwarefd)
601 640096fb Nikos Skalkotos
            self.g.download(path, software)
602 640096fb Nikos Skalkotos
603 640096fb Nikos Skalkotos
            h = hivex.Hivex(software, write=True)
604 640096fb Nikos Skalkotos
605 640096fb Nikos Skalkotos
            key = h.root()
606 640096fb Nikos Skalkotos
            for child in ('Microsoft', 'Windows', 'CurrentVersion', 'Policies',
607 640096fb Nikos Skalkotos
                          'System'):
608 640096fb Nikos Skalkotos
                key = h.node_get_child(key, child)
609 640096fb Nikos Skalkotos
610 640096fb Nikos Skalkotos
            policy = None
611 640096fb Nikos Skalkotos
            for val in h.node_values(key):
612 640096fb Nikos Skalkotos
                if h.value_key(val) == "LocalAccountTokenFilterPolicy":
613 640096fb Nikos Skalkotos
                    policy = val
614 640096fb Nikos Skalkotos
615 640096fb Nikos Skalkotos
            if policy is not None:
616 640096fb Nikos Skalkotos
                dword = h.value_dword(policy)
617 640096fb Nikos Skalkotos
                if dword == value:
618 640096fb Nikos Skalkotos
                    return False
619 640096fb Nikos Skalkotos
            elif value == 0:
620 640096fb Nikos Skalkotos
                return False
621 640096fb Nikos Skalkotos
622 85e97ae0 Nikos Skalkotos
            new_value = {'key': "LocalAccountTokenFilterPolicy", 't': 4L,
623 85e97ae0 Nikos Skalkotos
                         'value': struct.pack("<I", value)}
624 640096fb Nikos Skalkotos
625 640096fb Nikos Skalkotos
            h.node_set_value(key, new_value)
626 640096fb Nikos Skalkotos
            h.commit(None)
627 640096fb Nikos Skalkotos
628 640096fb Nikos Skalkotos
            self.g.upload(software, path)
629 640096fb Nikos Skalkotos
630 640096fb Nikos Skalkotos
        finally:
631 640096fb Nikos Skalkotos
            os.unlink(software)
632 640096fb Nikos Skalkotos
633 640096fb Nikos Skalkotos
        return True
634 640096fb 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 640096fb Nikos Skalkotos
        path = self._registry_file_path('SAM')
643 76b200cf Nikos Skalkotos
        samfd, sam = tempfile.mkstemp()
644 76b200cf Nikos Skalkotos
        try:
645 640096fb 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 8bae613f Nikos Skalkotos
    def _check_connectivity(self):
664 8bae613f Nikos Skalkotos
        """Check if winexe works on the Windows VM"""
665 8bae613f Nikos Skalkotos
666 8bae613f Nikos Skalkotos
        passwd = self.sysprep_params['password']
667 8bae613f Nikos Skalkotos
        winexe = WinEXE('Administrator', passwd, 'localhost')
668 8bae613f Nikos Skalkotos
        winexe.uninstall().debug(9)
669 8bae613f Nikos Skalkotos
670 8bae613f Nikos Skalkotos
        for i in range(CONNECTION_RETRIES):
671 8bae613f Nikos Skalkotos
            (stdout, stderr, rc) = winexe.run('cmd /C')
672 8bae613f Nikos Skalkotos
            if rc == 0:
673 8bae613f Nikos Skalkotos
                return True
674 8bae613f Nikos Skalkotos
            log = tempfile.NamedTemporaryFile(delete=False)
675 8bae613f Nikos Skalkotos
            try:
676 8bae613f Nikos Skalkotos
                log.file.write(stdout)
677 8bae613f Nikos Skalkotos
            finally:
678 8bae613f Nikos Skalkotos
                log.close()
679 8bae613f Nikos Skalkotos
            self.out.output("failed! See: `%' for the full output" % log.name)
680 8bae613f Nikos Skalkotos
            if i < CONNECTION_RETRIES - 1:
681 8bae613f Nikos Skalkotos
                self.out.output("Retrying ...", False)
682 8bae613f Nikos Skalkotos
        raise FatalError("Connection to the VM failed after %d retries" %
683 8bae613f Nikos Skalkotos
                         CONNECTION_RETRIES)
684 8bae613f Nikos Skalkotos
685 10088af7 Nikos Skalkotos
    def _guest_exec(self, command, fatal=True):
686 10088af7 Nikos Skalkotos
        """Execute a command on a windows VM"""
687 10088af7 Nikos Skalkotos
688 f5873ed0 Nikos Skalkotos
        passwd = self.sysprep_params['password']
689 74780ad6 Nikos Skalkotos
690 f5873ed0 Nikos Skalkotos
        winexe = WinEXE('Administrator', passwd, 'localhost')
691 f5873ed0 Nikos Skalkotos
        winexe.runas('Administrator', passwd).uninstall()
692 f5873ed0 Nikos Skalkotos
693 f5873ed0 Nikos Skalkotos
        try:
694 f5873ed0 Nikos Skalkotos
            (stdout, stderr, rc) = winexe.run(command)
695 f5873ed0 Nikos Skalkotos
        except WinexeTimeout:
696 f5873ed0 Nikos Skalkotos
            FatalError("Command: `%s' timeout out." % command)
697 74780ad6 Nikos Skalkotos
698 10088af7 Nikos Skalkotos
        if rc != 0 and fatal:
699 10088af7 Nikos Skalkotos
            reason = stderr if len(stderr) else stdout
700 c53cc8dd Nikos Skalkotos
            self.out.output("Command: `%s' failed (rc=%d). Reason: %s" %
701 c53cc8dd Nikos Skalkotos
                            (command, rc, reason))
702 c53cc8dd Nikos Skalkotos
            raise FatalError("Command: `%s' failed (rc=%d). Reason: %s" %
703 c53cc8dd Nikos Skalkotos
                             (command, rc, reason))
704 10088af7 Nikos Skalkotos
705 10088af7 Nikos Skalkotos
        return (stdout, stderr, rc)
706 74780ad6 Nikos Skalkotos
707 8bae613f Nikos Skalkotos
708 8bae613f Nikos Skalkotos
class _VM(object):
709 8bae613f Nikos Skalkotos
    """Windows Virtual Machine"""
710 8bae613f Nikos Skalkotos
    def __init__(self, disk, serial):
711 8bae613f Nikos Skalkotos
        """Create _VM instance
712 8bae613f Nikos Skalkotos

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