1 # -*- coding: utf-8 -*-
3 # Copyright 2012 GRNET S.A. All rights reserved.
5 # Redistribution and use in source and binary forms, with or
6 # without modification, are permitted provided that the following
9 # 1. Redistributions of source code must retain the above
10 # copyright notice, this list of conditions and the following
13 # 2. Redistributions in binary form must reproduce the above
14 # copyright notice, this list of conditions and the following
15 # disclaimer in the documentation and/or other materials
16 # provided with the distribution.
18 # THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
19 # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
22 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
25 # USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 # POSSIBILITY OF SUCH DAMAGE.
31 # The views and conclusions contained in the software and
32 # documentation are those of the authors and should not be
33 # interpreted as representing official policies, either expressed
34 # or implied, of GRNET S.A.
36 """This module hosts OS-specific code common for the various Microsoft
39 from image_creator.os_type import OSBase, sysprep, add_sysprep_param
40 from image_creator.util import FatalError, check_guestfs_version, \
42 from image_creator.winexe import WinEXE, WinexeTimeout
54 # For more info see: http://technet.microsoft.com/en-us/library/jj612867.aspx
55 KMS_CLIENT_SETUP_KEYS = {
56 "Windows 8 Professional": "NG4HW-VH26C-733KW-K6F98-J8CK4",
57 "Windows 8 Professional N": "XCVCF-2NXM9-723PB-MHCB7-2RYQQ",
58 "Windows 8 Enterprise": "32JNW-9KQ84-P47T8-D8GGY-CWCK7",
59 "Windows 8 Enterprise N": "JMNMF-RHW7P-DMY6X-RF3DR-X2BQT",
60 "Windows Server 2012 Core": "BN3D2-R7TKB-3YPBD-8DRP2-27GG4",
61 "Windows Server 2012 Core N": "8N2M2-HWPGY-7PGT9-HGDD8-GVGGY",
62 "Windows Server 2012 Core Single Language":
63 "2WN2H-YGCQR-KFX6K-CD6TF-84YXQ",
64 "Windows Server 2012 Core Country Specific":
65 "4K36P-JN4VD-GDC6V-KDT89-DYFKP",
66 "Windows Server 2012 Server Standard": "XC9B7-NBPP2-83J2H-RHMBY-92BT4",
67 "Windows Server 2012 Standard Core": "XC9B7-NBPP2-83J2H-RHMBY-92BT4",
68 "Windows Server 2012 MultiPoint Standard": "HM7DN-YVMH3-46JC3-XYTG7-CYQJJ",
69 "Windows Server 2012 MultiPoint Premium": "XNH6W-2V9GX-RGJ4K-Y8X6F-QGJ2G",
70 "Windows Server 2012 Datacenter": "48HP8-DN98B-MYWDG-T2DCC-8W83P",
71 "Windows Server 2012 Datacenter Core": "48HP8-DN98B-MYWDG-T2DCC-8W83P",
72 "Windows 7 Professional": "FJ82H-XT6CR-J8D7P-XQJJ2-GPDD4",
73 "Windows 7 Professional N": "MRPKT-YTG23-K7D7T-X2JMM-QY7MG",
74 "Windows 7 Professional E": "W82YF-2Q76Y-63HXB-FGJG9-GF7QX",
75 "Windows 7 Enterprise": "33PXH-7Y6KF-2VJC9-XBBR8-HVTHH",
76 "Windows 7 Enterprise N": "YDRBP-3D83W-TY26F-D46B2-XCKRJ",
77 "Windows 7 Enterprise E": "C29WB-22CC8-VJ326-GHFJW-H9DH4",
78 "Windows Server 2008 R2 Web": "6TPJF-RBVHG-WBW2R-86QPH-6RTM4",
79 "Windows Server 2008 R2 HPC edition": "TT8MH-CG224-D3D7Q-498W2-9QCTX",
80 "Windows Server 2008 R2 Standard": "YC6KT-GKW9T-YTKYR-T4X34-R7VHC",
81 "Windows Server 2008 R2 Enterprise": "489J6-VHDMP-X63PK-3K798-CPX3Y",
82 "Windows Server 2008 R2 Datacenter": "74YFP-3QFB3-KQT8W-PMXWJ-7M648",
83 "Windows Server 2008 R2 for Itanium-based Systems":
84 "GT63C-RJFQ3-4GMB6-BRFB9-CB83V",
85 "Windows Vista Business": "YFKBB-PQJJV-G996G-VWGXY-2V3X8",
86 "Windows Vista Business N": "HMBQG-8H2RH-C77VX-27R82-VMQBT",
87 "Windows Vista Enterprise": "VKK3X-68KWM-X2YGT-QR4M6-4BWMV",
88 "Windows Vista Enterprise N": "VTC42-BM838-43QHV-84HX6-XJXKV",
89 "Windows Web Server 2008": "WYR28-R7TFJ-3X2YQ-YCY4H-M249D",
90 "Windows Server 2008 Standard": "TM24T-X9RMF-VWXK6-X8JC9-BFGM2",
91 "Windows Server 2008 Standard without Hyper-V":
92 "W7VD6-7JFBR-RX26B-YKQ3Y-6FFFJ",
93 "Windows Server 2008 Enterprise":
94 "YQGMW-MPWTJ-34KDK-48M3W-X4Q6V",
95 "Windows Server 2008 Enterprise without Hyper-V":
96 "39BXF-X8Q23-P2WWT-38T2F-G3FPG",
97 "Windows Server 2008 HPC": "RCTX3-KWVHP-BR6TB-RB6DM-6X7HP",
98 "Windows Server 2008 Datacenter": "7M67G-PC374-GR742-YH8V4-TCBY3",
99 "Windows Server 2008 Datacenter without Hyper-V":
100 "22XQ2-VRXRG-P8D42-K34TD-G3QQC",
101 "Windows Server 2008 for Itanium-Based Systems":
102 "4DWFP-JF3DJ-B7DTH-78FJB-PDRHK"}
104 _POSINT = lambda x: type(x) == int and x >= 0
107 class Windows(OSBase):
108 """OS class for Windows"""
110 'shutdown_timeout', int, 120, "Shutdown Timeout (seconds)", _POSINT)
112 'boot_timeout', int, 300, "Boot Timeout (seconds)", _POSINT)
114 'connection_retries', int, 5, "Connection Retries", _POSINT)
115 @add_sysprep_param('password', str, None, 'Image Administrator Password')
116 def __init__(self, image, **kargs):
117 super(Windows, self).__init__(image, **kargs)
119 device = self.g.part_to_dev(self.root)
121 self.last_part_num = self.g.part_list(device)[-1]['part_num']
122 self.last_drive = None
123 self.system_drive = None
125 for drive, partition in self.g.inspect_get_drive_mappings(self.root):
126 if partition == "%s%d" % (device, self.last_part_num):
127 self.last_drive = drive
128 if partition == self.root:
129 self.system_drive = drive
131 assert self.system_drive
133 self.product_name = self.g.inspect_get_product_name(self.root)
134 self.syspreped = False
136 @sysprep('Disabling IPv6 privacy extensions')
137 def disable_ipv6_privacy_extensions(self):
138 """Disable IPv6 privacy extensions"""
140 self._guest_exec('netsh interface ipv6 set global '
141 'randomizeidentifiers=disabled store=persistent')
143 @sysprep('Disabling Teredo interface')
144 def disable_teredo(self):
145 """Disable Teredo interface"""
147 self._guest_exec('netsh interface teredo set state disabled')
149 @sysprep('Disabling ISATAP Adapters')
150 def disable_isatap(self):
151 """Disable ISATAP Adapters"""
153 self._guest_exec('netsh interface isa set state disabled')
155 @sysprep('Enabling ping responses')
156 def enable_pings(self):
157 """Enable ping responses"""
159 self._guest_exec('netsh firewall set icmpsetting 8')
161 @sysprep('Disabling hibernation support')
162 def disable_hibernation(self):
163 """Disable hibernation support and remove the hibernation file"""
165 self._guest_exec(r'powercfg.exe /hibernate off')
167 @sysprep('Setting the system clock to UTC')
169 """Set the hardware clock to UTC"""
171 path = r'HKLM\SYSTEM\CurrentControlSet\Control\TimeZoneInformation'
173 r'REG ADD %s /v RealTimeIsUniversal /t REG_DWORD /d 1 /f' % path)
175 @sysprep('Clearing the event logs')
176 def clear_logs(self):
177 """Clear all the event logs"""
180 r"cmd /q /c for /f %l in ('wevtutil el') do wevtutil cl %l")
182 @sysprep('Executing Sysprep on the image (may take more that 10 minutes)')
183 def microsoft_sysprep(self):
184 """Run the Microsoft System Preparation Tool. This will remove
185 system-specific data and will make the image ready to be deployed.
186 After this no other task may run.
189 self._guest_exec(r'C:\Windows\system32\sysprep\sysprep '
190 r'/quiet /generalize /oobe /shutdown')
191 self.syspreped = True
193 @sysprep('Converting the image into a KMS client', enabled=False)
194 def kms_client_setup(self):
195 """Install the appropriate KMS client setup key to the image to convert
196 it to a KMS client. Computers that are running volume licensing
197 editions of Windows 8, Windows Server 2012, Windows 7, Windows Server
198 2008 R2, Windows Vista, and Windows Server 2008 are, by default, KMS
199 clients with no additional configuration needed.
202 setup_key = KMS_CLIENT_SETUP_KEYS[self.product_name]
205 "Don't know the KMS client setup key for product: `%s'" %
210 r"cscript \Windows\system32\slmgr.vbs /ipk %s" % setup_key)
212 @sysprep('Shrinking the last filesystem')
214 """Shrink the last filesystem. Make sure the filesystem is defragged"""
216 # Query for the maximum number of reclaimable bytes
218 r'cmd /Q /V:ON /C "SET SCRIPT=%TEMP%\QUERYMAX_%RANDOM%.TXT & ' +
219 r'ECHO SELECT DISK 0 > %SCRIPT% & ' +
220 'ECHO SELECT PARTITION %d >> %%SCRIPT%% & ' % self.last_part_num +
221 r'ECHO SHRINK QUERYMAX >> %SCRIPT% & ' +
222 r'ECHO EXIT >> %SCRIPT% & ' +
223 r'DISKPART /S %SCRIPT% & ' +
224 r'IF NOT !ERRORLEVEL! EQU 0 EXIT /B 1 & ' +
227 stdout, stderr, rc = self._guest_exec(cmd)
230 for line in stdout.splitlines():
231 # diskpart will return something like this:
233 # The maximum number of reclaimable bytes is: xxxx MB
235 if line.find('reclaimable') >= 0:
236 querymax = line.split(':')[1].split()[0].strip()
237 assert querymax.isdigit(), \
238 "Number of reclaimable bytes not a number"
241 FatalError("Error in shrinking! "
242 "Couldn't find the max number of reclaimable bytes!")
244 querymax = int(querymax)
246 # Practically the smallest shrunken size generally is at around
247 # "used space" + (20-200 MB). Please also take into account that
248 # Windows might need about 50-100 MB free space left to boot safely.
249 # I'll give 100MB extra space just to be sure
253 self.out.warn("Not enought available space to shrink the image!")
256 self.out.output("\tReclaiming %dMB ..." % querymax)
259 r'cmd /Q /V:ON /C "SET SCRIPT=%TEMP%\QUERYMAX_%RANDOM%.TXT & ' +
260 r'ECHO SELECT DISK 0 > %SCRIPT% & ' +
261 'ECHO SELECT PARTITION %d >> %%SCRIPT%% & ' % self.last_part_num +
262 'ECHO SHRINK DESIRED=%d >> %%SCRIPT%% & ' % querymax +
263 r'ECHO EXIT >> %SCRIPT% & ' +
264 r'DISKPART /S %SCRIPT% & ' +
265 r'IF NOT !ERRORLEVEL! EQU 0 EXIT /B 1 & ' +
268 stdout, stderr, rc = self._guest_exec(cmd)
270 for line in stdout.splitlines():
271 if line.find('shrunk') >= 0:
272 self.out.output(line)
274 def do_sysprep(self):
275 """Prepare system for image creation."""
277 if getattr(self, 'syspreped', False):
278 raise FatalError("Image is already syspreped!")
280 txt = "System preparation parameter: `%s' is needed but missing!"
281 for name, param in self.needed_sysprep_params.items():
282 if name not in self.sysprep_params:
283 raise FatalError(txt % param)
285 self.mount(readonly=False)
287 disabled_uac = self._update_uac_remote_setting(1)
288 token = self._enable_os_monitor()
290 # disable the firewalls
291 firewall_states = self._update_firewalls(0, 0, 0)
293 # Delete the pagefile. It will be recreated when the system boots
294 systemroot = self.g.inspect_get_windows_systemroot(self.root)
295 pagefile = "%s/pagefile.sys" % systemroot
296 self.g.rm_rf(self.g.case_sensitive_path(pagefile))
301 self.out.output("Shutting down helper VM ...", False)
303 # guestfs_shutdown which is the prefered way to shutdown the backend
304 # process was introduced in version 1.19.16
305 if check_guestfs_version(self.g, 1, 19, 16) >= 0:
308 self.g.kill_subprocess()
310 self.out.success('done')
315 self.out.output("Starting windows VM ...", False)
316 monitorfd, monitor = tempfile.mkstemp()
318 vm = _VM(self.image.device, monitor, self.sysprep_params)
319 self.out.success("started (console on vnc display: %d)." %
322 self.out.output("Waiting for OS to boot ...", False)
323 self._wait_vm_boot(vm, monitor, token)
324 self.out.success('done')
326 self.out.output("Checking connectivity to the VM ...", False)
327 self._check_connectivity()
328 self.out.success('done')
330 self.out.output("Disabling automatic logon ...", False)
331 self._disable_autologon()
332 self.out.success('done')
334 self.out.output('Preparing system for image creation:')
336 tasks = self.list_syspreps()
337 enabled = [task for task in tasks if task.enabled]
340 # Make sure shrink runs in the end, before ms sysprep
341 enabled = [task for task in enabled if
342 self.sysprep_info(task).name != 'shrink']
344 if len(enabled) != size:
345 enabled.append(self.shrink)
347 # Make sure the ms sysprep is the last task to run if it is enabled
348 enabled = [task for task in enabled if
349 self.sysprep_info(task).name != 'microsoft-sysprep']
351 ms_sysprep_enabled = False
352 if len(enabled) != size:
353 enabled.append(self.microsoft_sysprep)
354 ms_sysprep_enabled = True
359 self.out.output(('(%d/%d)' % (cnt, size)).ljust(7), False)
361 setattr(task.im_func, 'executed', True)
363 self.out.output("Sending shut down command ...", False)
364 if not ms_sysprep_enabled:
366 self.out.success("done")
368 self.out.output("Waiting for windows to shut down ...", False)
369 vm.wait(self.sysprep_params['shutdown_timeout'])
370 self.out.success("done")
372 if monitor is not None:
377 self.out.output("Destroying windows VM ...", False)
379 self.out.success("done")
381 self.out.output("Relaunching helper VM (may take a while) ...",
384 self.out.success('done')
386 self.mount(readonly=False)
389 self._update_uac_remote_setting(0)
391 self._update_firewalls(*firewall_states)
396 """Shuts down the windows VM"""
397 self._guest_exec(r'shutdown /s /t 5')
399 def _wait_vm_boot(self, vm, fname, msg):
400 """Wait until a message appears on a file or the vm process dies"""
402 for _ in range(self.sysprep_params['boot_timeout']):
404 with open(fname) as f:
406 if line.startswith(msg):
409 raise FatalError("Windows VM died unexpectedly!")
411 raise FatalError("Windows VM booting timed out!")
413 def _disable_autologon(self):
414 """Disable automatic logon on the windows image"""
417 r'"HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon"'
419 self._guest_exec('REG DELETE %s /v DefaultUserName /f' % winlogon)
420 self._guest_exec('REG DELETE %s /v DefaultPassword /f' % winlogon)
421 self._guest_exec('REG DELETE %s /v AutoAdminLogon /f' % winlogon)
423 def _registry_file_path(self, regfile):
424 """Retrieves the case sensitive path to a registry file"""
426 systemroot = self.g.inspect_get_windows_systemroot(self.root)
427 path = "%s/system32/config/%s" % (systemroot, regfile)
429 path = self.g.case_sensitive_path(path)
430 except RuntimeError as error:
431 raise FatalError("Unable to retrieve registry file: %s. Reason: %s"
432 % (regfile, str(error)))
435 def _enable_os_monitor(self):
436 """Add a script in the registry that will send a random string to the
437 first serial port when the windows image finishes booting.
440 token = "".join(random.choice(string.ascii_letters) for x in range(16))
442 path = self._registry_file_path('SOFTWARE')
443 softwarefd, software = tempfile.mkstemp()
446 self.g.download(path, software)
448 h = hivex.Hivex(software, write=True)
450 # Enable automatic logon.
451 # This is needed because we need to execute a script that we add in
452 # the RunOnce registry entry and those programs only get executed
453 # when a user logs on. There is a RunServicesOnce registry entry
454 # whose keys get executed in the background when the logon dialog
455 # box first appears, but they seem to only work with services and
456 # not arbitrary command line expressions :-(
458 # Instructions on how to turn on automatic logon in Windows can be
459 # found here: http://support.microsoft.com/kb/324737
461 # Warning: Registry change will not work if the “Logon Banner” is
462 # defined on the server either by a Group Policy object (GPO) or by
466 for child in ('Microsoft', 'Windows NT', 'CurrentVersion',
468 winlogon = h.node_get_child(winlogon, child)
472 {'key': 'DefaultUserName', 't': 1,
473 'value': "Administrator".encode('utf-16le')})
476 {'key': 'DefaultPassword', 't': 1,
477 'value': self.sysprep_params['password'].encode('utf-16le')})
480 {'key': 'AutoAdminLogon', 't': 1,
481 'value': "1".encode('utf-16le')})
484 for child in ('Microsoft', 'Windows', 'CurrentVersion'):
485 key = h.node_get_child(key, child)
487 runonce = h.node_get_child(key, "RunOnce")
489 runonce = h.node_add_child(key, "RunOnce")
492 r'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe '
493 r'-ExecutionPolicy RemoteSigned '
494 r'"&{$port=new-Object System.IO.Ports.SerialPort COM1,9600,'
495 r'None,8,one;$port.open();$port.WriteLine(\"' + token + r'\");'
496 r'$port.Close()}"').encode('utf-16le')
498 h.node_set_value(runonce,
499 {'key': "BootMonitor", 't': 1, 'value': value})
502 r'REG ADD HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion'
503 r'\policies\system /v LocalAccountTokenFilterPolicy'
504 r' /t REG_DWORD /d 1 /f').encode('utf-16le')
506 h.node_set_value(runonce,
507 {'key': "UpdateRegistry", 't': 1, 'value': value})
511 self.g.upload(software, path)
517 def _update_firewalls(self, domain, public, standard):
518 """Enables or disables the firewall for the Domain, the Public and the
519 Standard profile. Returns a triplete with the old values.
521 1 will enable a firewall and 0 will disable it
524 if domain not in (0, 1):
525 raise ValueError("Valid values for domain parameter are 0 and 1")
527 if public not in (0, 1):
528 raise ValueError("Valid values for public parameter are 0 and 1")
530 if standard not in (0, 1):
531 raise ValueError("Valid values for standard parameter are 0 and 1")
533 path = self._registry_file_path("SYSTEM")
534 systemfd, system = tempfile.mkstemp()
537 self.g.download(path, system)
539 h = hivex.Hivex(system, write=True)
541 select = h.node_get_child(h.root(), 'Select')
542 current_value = h.node_get_value(select, 'Current')
544 # expecting a little endian dword
545 assert h.value_type(current_value)[1] == 4
546 current = "%03d" % h.value_dword(current_value)
548 firewall_policy = h.root()
549 for child in ('ControlSet%s' % current, 'services', 'SharedAccess',
550 'Parameters', 'FirewallPolicy'):
551 firewall_policy = h.node_get_child(firewall_policy, child)
554 new_values = [domain, public, standard]
555 for profile in ('Domain', 'Public', 'Standard'):
556 node = h.node_get_child(firewall_policy, '%sProfile' % profile)
558 old_value = h.node_get_value(node, 'EnableFirewall')
560 # expecting a little endian dword
561 assert h.value_type(old_value)[1] == 4
562 old_values.append(h.value_dword(old_value))
565 node, {'key': 'EnableFirewall', 't': 4L,
566 'value': struct.pack("<I", new_values.pop(0))})
569 self.g.upload(system, path)
576 def _update_uac_remote_setting(self, value):
577 """Updates the registry key value:
578 [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies
579 \System]"LocalAccountTokenFilterPolicy"
581 value = 1 will disable the UAC remote restrictions
582 value = 0 will enable the UAC remote restrictions
584 For more info see here: http://support.microsoft.com/kb/951016
587 True if the key is changed
588 False if the key is unchanged
591 if value not in (0, 1):
592 raise ValueError("Valid values for value parameter are 0 and 1")
594 path = self._registry_file_path('SOFTWARE')
595 softwarefd, software = tempfile.mkstemp()
598 self.g.download(path, software)
600 h = hivex.Hivex(software, write=True)
603 for child in ('Microsoft', 'Windows', 'CurrentVersion', 'Policies',
605 key = h.node_get_child(key, child)
608 for val in h.node_values(key):
609 if h.value_key(val) == "LocalAccountTokenFilterPolicy":
612 if policy is not None:
613 dword = h.value_dword(policy)
619 new_value = {'key': "LocalAccountTokenFilterPolicy", 't': 4L,
620 'value': struct.pack("<I", value)}
622 h.node_set_value(key, new_value)
625 self.g.upload(software, path)
632 def _do_collect_metadata(self):
633 """Collect metadata about the OS"""
634 super(Windows, self)._do_collect_metadata()
635 self.meta["USERS"] = " ".join(self._get_users())
637 def _get_users(self):
638 """Returns a list of users found in the images"""
639 samfd, sam = tempfile.mkstemp()
642 self.g.download(self._registry_file_path('SAM'), sam)
646 # Navigate to /SAM/Domains/Account/Users
647 users_node = h.root()
648 for child in ('SAM', 'Domains', 'Account', 'Users'):
649 users_node = h.node_get_child(users_node, child)
651 # Navigate to /SAM/Domains/Account/Users/Names
652 names_node = h.node_get_child(users_node, 'Names')
654 # HKEY_LOCAL_MACHINE\SAM\SAM\Domains\Account\Users\%RID%
655 # HKEY_LOCAL_MACHINE\SAM\SAM\Domains\Account\Users\Names\%Username%
657 # The RID (relative identifier) of each user is stored as the type!
658 # (not the value) of the default key of the node under Names whose
659 # name is the user's username. Under the RID node, there in a F
660 # value that contains information about this user account.
662 # See sam.h of the chntpw project on how to translate the F value
663 # of an account in the registry. Bytes 56 & 57 are the account type
664 # and status flags. The first bit is the 'account disabled' bit
665 disabled = lambda f: int(f[56].encode('hex'), 16) & 0x01
668 for user_node in h.node_children(names_node):
669 username = h.node_name(user_node)
670 rid = h.value_type(h.node_get_value(user_node, ""))[0]
671 # if RID is 500 (=0x1f4), the corresponding node name under
672 # Users is '000001F4'
673 key = ("%8.x" % rid).replace(' ', '0').upper()
674 rid_node = h.node_get_child(users_node, key)
675 f_value = h.value_value(h.node_get_value(rid_node, 'F'))[1]
677 if disabled(f_value):
678 self.out.warn("Found disabled `%s' account!" % username)
681 users.append(username)
686 # Filter out the guest account
689 def _check_connectivity(self):
690 """Check if winexe works on the Windows VM"""
692 retries = self.sysprep_params['connection_retries']
693 # If the connection_retries parameter is set to 0 disable the
698 passwd = self.sysprep_params['password']
699 winexe = WinEXE('Administrator', passwd, 'localhost')
700 winexe.uninstall().debug(9)
702 for i in range(retries):
703 (stdout, stderr, rc) = winexe.run('cmd /C')
706 log = tempfile.NamedTemporaryFile(delete=False)
708 log.file.write(stdout)
711 self.out.output("failed! See: `%s' for the full output" % log.name)
713 self.out.output("retrying ...", False)
715 raise FatalError("Connection to the Windows VM failed after %d retries"
718 def _guest_exec(self, command, fatal=True):
719 """Execute a command on a windows VM"""
721 passwd = self.sysprep_params['password']
723 winexe = WinEXE('Administrator', passwd, 'localhost')
724 winexe.runas('Administrator', passwd).uninstall()
727 (stdout, stderr, rc) = winexe.run(command)
728 except WinexeTimeout:
729 FatalError("Command: `%s' timeout out." % command)
731 if rc != 0 and fatal:
732 reason = stderr if len(stderr) else stdout
733 self.out.output("Command: `%s' failed (rc=%d). Reason: %s" %
734 (command, rc, reason))
735 raise FatalError("Command: `%s' failed (rc=%d). Reason: %s" %
736 (command, rc, reason))
738 return (stdout, stderr, rc)
742 """Windows Virtual Machine"""
743 def __init__(self, disk, serial, params):
744 """Create _VM instance
747 serial: File to save the output of the serial port
755 """creates a random mac address"""
756 mac = [0x00, 0x16, 0x3e,
757 random.randint(0x00, 0x7f),
758 random.randint(0x00, 0xff),
759 random.randint(0x00, 0xff)]
761 return ':'.join(['%02x' % x for x in mac])
763 # Use ganeti's VNC port range for a random vnc port
764 self.display = random.randint(11000, 14999) - 5900
766 kvm = get_kvm_binary()
769 FatalError("Can't find the kvm binary")
772 kvm, '-smp', '1', '-m', '1024', '-drive',
773 'file=%s,format=raw,cache=unsafe,if=virtio' % self.disk,
774 '-netdev', 'type=user,hostfwd=tcp::445-:445,id=netdev0',
775 '-device', 'virtio-net-pci,mac=%s,netdev=netdev0' % random_mac(),
776 '-vnc', ':%d' % self.display, '-serial', 'file:%s' % self.serial,
779 self.process = subprocess.Popen(args, stdin=subprocess.PIPE,
780 stdout=subprocess.PIPE)
783 """Check if the VM is still alive"""
784 return self.process.poll() is None
789 if not self.isalive():
792 def handler(signum, frame):
793 self.process.terminate()
798 raise FatalError("VM destroy timed-out")
800 signal.signal(signal.SIGALRM, handler)
802 signal.alarm(self.params['shutdown_timeout'])
803 self.process.communicate(input="system_powerdown\n")
806 def wait(self, timeout=0):
807 """Wait for the VM to terminate"""
809 def handler(signum, frame):
811 raise FatalError("VM wait timed-out.")
813 signal.signal(signal.SIGALRM, handler)
815 signal.alarm(timeout)
816 stdout, stderr = self.process.communicate()
819 return (stdout, stderr, self.process.poll())
821 # vim: set sta sts=4 shiftwidth=4 sw=4 et ai :