Statistics
| Branch: | Tag: | Revision:

root / image_creator / os_type / windows.py @ 12c97404

History | View | Annotate | Download (10.1 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 aa2062ba Nikos Skalkotos
42 76b200cf Nikos Skalkotos
import hivex
43 76b200cf Nikos Skalkotos
import tempfile
44 76b200cf Nikos Skalkotos
import os
45 c50c5ae7 Nikos Skalkotos
import time
46 c50c5ae7 Nikos Skalkotos
import random
47 12c97404 Nikos Skalkotos
import subprocess
48 c50c5ae7 Nikos Skalkotos
49 c50c5ae7 Nikos Skalkotos
kvm = get_command('kvm')
50 76b200cf Nikos Skalkotos
51 8c574358 Nikos Skalkotos
52 aa2062ba Nikos Skalkotos
class Windows(OSBase):
53 88f83027 Nikos Skalkotos
    """OS class for Windows"""
54 76b200cf Nikos Skalkotos
55 12c97404 Nikos Skalkotos
    def needed_sysprep_params(self):
56 12c97404 Nikos Skalkotos
        """Returns a list of needed sysprep parameters. Each element in the
57 12c97404 Nikos Skalkotos
        list is a SysprepParam object.
58 12c97404 Nikos Skalkotos
        """
59 12c97404 Nikos Skalkotos
60 12c97404 Nikos Skalkotos
        password = self.SysprepParam(
61 12c97404 Nikos Skalkotos
            'password', 'Image Administrator Password', 20, lambda x: True)
62 12c97404 Nikos Skalkotos
63 12c97404 Nikos Skalkotos
        return [password]
64 12c97404 Nikos Skalkotos
65 12c97404 Nikos Skalkotos
    @sysprep(enabled=True)
66 12c97404 Nikos Skalkotos
    def disable_ipv6_privacy_extensions(self, print_header=True):
67 12c97404 Nikos Skalkotos
        """Disable IPv6 privacy extensions"""
68 12c97404 Nikos Skalkotos
69 12c97404 Nikos Skalkotos
        if print_header:
70 12c97404 Nikos Skalkotos
            self.out.output("Disabling IPv6 privacy extensions")
71 12c97404 Nikos Skalkotos
72 12c97404 Nikos Skalkotos
        out, err, rc = self._guest_exec(
73 12c97404 Nikos Skalkotos
            'netsh interface ipv6 set global randomizeidentifiers=disabled '
74 12c97404 Nikos Skalkotos
            'store=persistent')
75 12c97404 Nikos Skalkotos
76 12c97404 Nikos Skalkotos
        if rc != 0:
77 12c97404 Nikos Skalkotos
            raise FatalError("Unable to disable IPv6 privacy extensions: %s" %
78 12c97404 Nikos Skalkotos
                             err)
79 12c97404 Nikos Skalkotos
80 12c97404 Nikos Skalkotos
    @sysprep(enabled=True)
81 12c97404 Nikos Skalkotos
    def microsoft_sysprep(self, print_header=True):
82 12c97404 Nikos Skalkotos
        """Run the Micorsoft System Preparation Tool on the Image. After this
83 12c97404 Nikos Skalkotos
        runs, no other task may run.
84 12c97404 Nikos Skalkotos
        """
85 12c97404 Nikos Skalkotos
86 12c97404 Nikos Skalkotos
        if print_header:
87 12c97404 Nikos Skalkotos
            self.out.output("Executing sysprep on the image (may take more "
88 12c97404 Nikos Skalkotos
                            "than 10 minutes)")
89 12c97404 Nikos Skalkotos
90 12c97404 Nikos Skalkotos
        out, err, rc = self._guest_exec(r'C:\windows\system32\sysprep\sysprep '
91 12c97404 Nikos Skalkotos
                                        r'/quiet /generalize /oobe /shutdown')
92 12c97404 Nikos Skalkotos
        self.syspreped = True
93 12c97404 Nikos Skalkotos
        if rc != 0:
94 12c97404 Nikos Skalkotos
            raise FatalError("Unable to perform sysprep: %s" % err)
95 c50c5ae7 Nikos Skalkotos
96 55133880 Nikos Skalkotos
    def do_sysprep(self):
97 55133880 Nikos Skalkotos
        """Prepare system for image creation."""
98 55133880 Nikos Skalkotos
99 55133880 Nikos Skalkotos
        if getattr(self, 'syspreped', False):
100 55133880 Nikos Skalkotos
            raise FatalError("Image is already syspreped!")
101 55133880 Nikos Skalkotos
102 12c97404 Nikos Skalkotos
        txt = "System preparation parameter: `%s' is needed but missing!"
103 12c97404 Nikos Skalkotos
        for param in self.needed_sysprep_params():
104 12c97404 Nikos Skalkotos
            if param[0] not in self.sysprep_params:
105 12c97404 Nikos Skalkotos
                raise FatalError(txt % param[0])
106 12c97404 Nikos Skalkotos
107 55133880 Nikos Skalkotos
        self.mount(readonly=False)
108 55133880 Nikos Skalkotos
        try:
109 55133880 Nikos Skalkotos
            disabled_uac = self._update_uac_remote_setting(1)
110 55133880 Nikos Skalkotos
        finally:
111 55133880 Nikos Skalkotos
            self.umount()
112 55133880 Nikos Skalkotos
113 55133880 Nikos Skalkotos
        self.out.output("Shutting down helper VM ...", False)
114 55133880 Nikos Skalkotos
        self.g.sync()
115 55133880 Nikos Skalkotos
        # guestfs_shutdown which is the prefered way to shutdown the backend
116 55133880 Nikos Skalkotos
        # process was introduced in version 1.19.16
117 55133880 Nikos Skalkotos
        if check_guestfs_version(self.g, 1, 19, 16) >= 0:
118 55133880 Nikos Skalkotos
            ret = self.g.shutdown()
119 55133880 Nikos Skalkotos
        else:
120 55133880 Nikos Skalkotos
            ret = self.g.kill_subprocess()
121 55133880 Nikos Skalkotos
122 55133880 Nikos Skalkotos
        self.out.success('done')
123 55133880 Nikos Skalkotos
        try:
124 c50c5ae7 Nikos Skalkotos
            self.out.output("Starting windows VM ...", False)
125 c50c5ae7 Nikos Skalkotos
126 c50c5ae7 Nikos Skalkotos
            def random_mac():
127 c50c5ae7 Nikos Skalkotos
                mac = [0x00, 0x16, 0x3e,
128 c50c5ae7 Nikos Skalkotos
                       random.randint(0x00, 0x7f),
129 c50c5ae7 Nikos Skalkotos
                       random.randint(0x00, 0xff),
130 c50c5ae7 Nikos Skalkotos
                       random.randint(0x00, 0xff)]
131 c50c5ae7 Nikos Skalkotos
                return ':'.join(map(lambda x: "%02x" % x, mac))
132 c50c5ae7 Nikos Skalkotos
133 c50c5ae7 Nikos Skalkotos
            vm = kvm('-smp', '1', '-m', '1024', '-drive',
134 c50c5ae7 Nikos Skalkotos
                     'file=%s,format=raw,cache=none,if=virtio' %
135 c50c5ae7 Nikos Skalkotos
                     self.image.device,
136 c50c5ae7 Nikos Skalkotos
                     '-netdev', 'type=user,hostfwd=tcp::445-:445,id=netdev0',
137 c50c5ae7 Nikos Skalkotos
                     '-device', 'virtio-net-pci,mac=%s,netdev=netdev0' %
138 c50c5ae7 Nikos Skalkotos
                     random_mac(), '-vnc', ':0', _bg=True)
139 12c97404 Nikos Skalkotos
            time.sleep(60)
140 c50c5ae7 Nikos Skalkotos
            self.out.success('done')
141 12c97404 Nikos Skalkotos
142 12c97404 Nikos Skalkotos
            tasks = self.list_syspreps()
143 12c97404 Nikos Skalkotos
            enabled = filter(lambda x: x.enabled, tasks)
144 12c97404 Nikos Skalkotos
145 12c97404 Nikos Skalkotos
            size = len(enabled)
146 12c97404 Nikos Skalkotos
147 12c97404 Nikos Skalkotos
            # Make sure the ms sysprep is the last task to run if it is enabled
148 12c97404 Nikos Skalkotos
            enabled = filter(
149 12c97404 Nikos Skalkotos
                lambda x: x.im_func.func_name != 'microsoft_sysprep', enabled)
150 12c97404 Nikos Skalkotos
151 12c97404 Nikos Skalkotos
            ms_sysprep_enabled = False
152 12c97404 Nikos Skalkotos
            if len(enabled) != size:
153 12c97404 Nikos Skalkotos
                enabled.append(self.ms_sysprep)
154 12c97404 Nikos Skalkotos
                ms_sysprep_enabled = True
155 12c97404 Nikos Skalkotos
156 12c97404 Nikos Skalkotos
            cnt = 0
157 12c97404 Nikos Skalkotos
            for task in enabled:
158 12c97404 Nikos Skalkotos
                cnt += 1
159 12c97404 Nikos Skalkotos
                self.out.output(('(%d/%d)' % (cnt, size)).ljust(7), False)
160 12c97404 Nikos Skalkotos
                task()
161 12c97404 Nikos Skalkotos
                setattr(task.im_func, 'executed', True)
162 12c97404 Nikos Skalkotos
163 12c97404 Nikos Skalkotos
            if not ms_sysprep_enabled:
164 12c97404 Nikos Skalkotos
                self._shutdown()
165 12c97404 Nikos Skalkotos
166 c50c5ae7 Nikos Skalkotos
            vm.wait()
167 55133880 Nikos Skalkotos
        finally:
168 12c97404 Nikos Skalkotos
            if vm.process.alive:
169 12c97404 Nikos Skalkotos
                vm.terminate()
170 12c97404 Nikos Skalkotos
171 55133880 Nikos Skalkotos
            self.out.output("Relaunching helper VM (may take a while) ...",
172 55133880 Nikos Skalkotos
                            False)
173 55133880 Nikos Skalkotos
            self.g.launch()
174 55133880 Nikos Skalkotos
            self.out.success('done')
175 55133880 Nikos Skalkotos
176 55133880 Nikos Skalkotos
        if disabled_uac:
177 55133880 Nikos Skalkotos
            self._update_uac_remote_setting(0)
178 55133880 Nikos Skalkotos
179 12c97404 Nikos Skalkotos
    def _shutdown(self):
180 12c97404 Nikos Skalkotos
        """Shuts down the windows VM"""
181 12c97404 Nikos Skalkotos
182 12c97404 Nikos Skalkotos
        self.out.output("Shutting down windows VM ...", False)
183 12c97404 Nikos Skalkotos
        out, err, rc = self._guest_exec(r'shutdown /s /t 5')
184 12c97404 Nikos Skalkotos
185 12c97404 Nikos Skalkotos
        if rc != 0:
186 12c97404 Nikos Skalkotos
            raise FatalError("Unable to perform shutdown: %s" % err)
187 12c97404 Nikos Skalkotos
188 12c97404 Nikos Skalkotos
        self.out.success('done')
189 55133880 Nikos Skalkotos
190 55133880 Nikos Skalkotos
    def _registry_file_path(self, regfile):
191 55133880 Nikos Skalkotos
        """Retrieves the case sensitive path to a registry file"""
192 55133880 Nikos Skalkotos
193 55133880 Nikos Skalkotos
        systemroot = self.g.inspect_get_windows_systemroot(self.root)
194 55133880 Nikos Skalkotos
        path = "%s/system32/config/%s" % (systemroot, regfile)
195 55133880 Nikos Skalkotos
        try:
196 55133880 Nikos Skalkotos
            path = self.g.case_sensitive_path(path)
197 55133880 Nikos Skalkotos
        except RuntimeError as e:
198 55133880 Nikos Skalkotos
            raise FatalError("Unable to retrieve registry file: %s. Reason: %s"
199 55133880 Nikos Skalkotos
                             % (regfile, str(e)))
200 55133880 Nikos Skalkotos
        return path
201 55133880 Nikos Skalkotos
202 55133880 Nikos Skalkotos
    def _update_uac_remote_setting(self, value):
203 55133880 Nikos Skalkotos
        """Updates the registry key value:
204 55133880 Nikos Skalkotos
        [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies
205 55133880 Nikos Skalkotos
        \System]"LocalAccountTokenFilterPolicy"
206 55133880 Nikos Skalkotos

207 55133880 Nikos Skalkotos
        value = 1 will disable the UAC remote restrictions
208 55133880 Nikos Skalkotos
        value = 0 will enable the UAC remote restrictions
209 55133880 Nikos Skalkotos

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

212 55133880 Nikos Skalkotos
        Returns:
213 55133880 Nikos Skalkotos
            True if the key is changed
214 55133880 Nikos Skalkotos
            False if the key is unchanged
215 55133880 Nikos Skalkotos
        """
216 55133880 Nikos Skalkotos
217 55133880 Nikos Skalkotos
        if value not in (0, 1):
218 55133880 Nikos Skalkotos
            raise ValueError("Valid values for value parameter are 0 and 1")
219 55133880 Nikos Skalkotos
220 55133880 Nikos Skalkotos
        path = self._registry_file_path('SOFTWARE')
221 55133880 Nikos Skalkotos
        softwarefd, software = tempfile.mkstemp()
222 55133880 Nikos Skalkotos
        try:
223 55133880 Nikos Skalkotos
            os.close(softwarefd)
224 55133880 Nikos Skalkotos
            self.g.download(path, software)
225 55133880 Nikos Skalkotos
226 55133880 Nikos Skalkotos
            h = hivex.Hivex(software, write=True)
227 55133880 Nikos Skalkotos
228 55133880 Nikos Skalkotos
            key = h.root()
229 55133880 Nikos Skalkotos
            for child in ('Microsoft', 'Windows', 'CurrentVersion', 'Policies',
230 55133880 Nikos Skalkotos
                          'System'):
231 55133880 Nikos Skalkotos
                key = h.node_get_child(key, child)
232 55133880 Nikos Skalkotos
233 55133880 Nikos Skalkotos
            policy = None
234 55133880 Nikos Skalkotos
            for val in h.node_values(key):
235 55133880 Nikos Skalkotos
                if h.value_key(val) == "LocalAccountTokenFilterPolicy":
236 55133880 Nikos Skalkotos
                    policy = val
237 55133880 Nikos Skalkotos
238 55133880 Nikos Skalkotos
            if policy is not None:
239 55133880 Nikos Skalkotos
                dword = h.value_dword(policy)
240 55133880 Nikos Skalkotos
                if dword == value:
241 55133880 Nikos Skalkotos
                    return False
242 55133880 Nikos Skalkotos
            elif value == 0:
243 55133880 Nikos Skalkotos
                return False
244 55133880 Nikos Skalkotos
245 55133880 Nikos Skalkotos
            new_value = {
246 55133880 Nikos Skalkotos
                'key': "LocalAccountTokenFilterPolicy", 't': 4L,
247 55133880 Nikos Skalkotos
                'value': '%s\x00\x00\x00' % '\x00' if value == 0 else '\x01'}
248 55133880 Nikos Skalkotos
249 55133880 Nikos Skalkotos
            h.node_set_value(key, new_value)
250 55133880 Nikos Skalkotos
            h.commit(None)
251 55133880 Nikos Skalkotos
252 55133880 Nikos Skalkotos
            self.g.upload(software, path)
253 55133880 Nikos Skalkotos
254 55133880 Nikos Skalkotos
        finally:
255 55133880 Nikos Skalkotos
            os.unlink(software)
256 55133880 Nikos Skalkotos
257 55133880 Nikos Skalkotos
        return True
258 55133880 Nikos Skalkotos
259 b8c0848c Nikos Skalkotos
    def _do_collect_metadata(self):
260 b8c0848c Nikos Skalkotos
        """Collect metadata about the OS"""
261 b8c0848c Nikos Skalkotos
        super(Windows, self)._do_collect_metadata()
262 76b200cf Nikos Skalkotos
        self.meta["USERS"] = " ".join(self._get_users())
263 76b200cf Nikos Skalkotos
264 76b200cf Nikos Skalkotos
    def _get_users(self):
265 121f3bc0 Nikos Skalkotos
        """Returns a list of users found in the images"""
266 55133880 Nikos Skalkotos
        path = self._registry_file_path('SAM')
267 76b200cf Nikos Skalkotos
        samfd, sam = tempfile.mkstemp()
268 76b200cf Nikos Skalkotos
        try:
269 55133880 Nikos Skalkotos
            os.close(samfd)
270 76b200cf Nikos Skalkotos
            self.g.download(path, sam)
271 76b200cf Nikos Skalkotos
272 76b200cf Nikos Skalkotos
            h = hivex.Hivex(sam)
273 76b200cf Nikos Skalkotos
274 76b200cf Nikos Skalkotos
            key = h.root()
275 76b200cf Nikos Skalkotos
            # Navigate to /SAM/Domains/Account/Users/Names
276 76b200cf Nikos Skalkotos
            for child in ('SAM', 'Domains', 'Account', 'Users', 'Names'):
277 76b200cf Nikos Skalkotos
                key = h.node_get_child(key, child)
278 76b200cf Nikos Skalkotos
279 76b200cf Nikos Skalkotos
            users = [h.node_name(x) for x in h.node_children(key)]
280 76b200cf Nikos Skalkotos
281 76b200cf Nikos Skalkotos
        finally:
282 76b200cf Nikos Skalkotos
            os.unlink(sam)
283 76b200cf Nikos Skalkotos
284 76b200cf Nikos Skalkotos
        # Filter out the guest account
285 76b200cf Nikos Skalkotos
        return filter(lambda x: x != "Guest", users)
286 aa2062ba Nikos Skalkotos
287 12c97404 Nikos Skalkotos
    def _guest_exec(self, command):
288 12c97404 Nikos Skalkotos
        user = "Administrator%" + self.sysprep_params['password']
289 12c97404 Nikos Skalkotos
        addr = 'localhost'
290 12c97404 Nikos Skalkotos
        runas = '--runas=%s' % user
291 12c97404 Nikos Skalkotos
        winexe = subprocess.Popen(
292 12c97404 Nikos Skalkotos
            ['winexe', '-U', user, "//%s" % addr, runas, command],
293 12c97404 Nikos Skalkotos
            stdout=subprocess.PIPE, stderr=subprocess.PIPE)
294 12c97404 Nikos Skalkotos
295 12c97404 Nikos Skalkotos
        result = winexe.communicate()
296 12c97404 Nikos Skalkotos
        rc = winexe.poll()
297 12c97404 Nikos Skalkotos
298 12c97404 Nikos Skalkotos
        return (result[0], result[1], rc)
299 12c97404 Nikos Skalkotos
300 aa2062ba Nikos Skalkotos
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :