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 # This commit was added in libguestfs 1.17.18 and is critical because
120 # Microsoft Sysprep removes this key:
122 # When a Windows guest doesn't have a HKLM\SYSTEM\MountedDevices node,
123 # inspection fails. However inspection should not completely fail just
124 # because we cannot get the drive letter mapping from a guest.
125 if check_guestfs_version(self.image.g, 1, 17, 18) < 0:
127 'For windows support libguestfs 1.17.17 or above is needed')
129 device = self.image.g.part_to_dev(self.root)
131 self.last_part_num = self.image.g.part_list(device)[-1]['part_num']
132 self.last_drive = None
133 self.system_drive = None
135 for drive, part in self.image.g.inspect_get_drive_mappings(self.root):
136 if part == "%s%d" % (device, self.last_part_num):
137 self.last_drive = drive
138 if part == self.root:
139 self.system_drive = drive
141 assert self.system_drive
143 self.product_name = self.image.g.inspect_get_product_name(self.root)
144 self.syspreped = False
146 @sysprep('Disabling IPv6 privacy extensions')
147 def disable_ipv6_privacy_extensions(self):
148 """Disable IPv6 privacy extensions"""
150 self._guest_exec('netsh interface ipv6 set global '
151 'randomizeidentifiers=disabled store=persistent')
153 @sysprep('Disabling Teredo interface')
154 def disable_teredo(self):
155 """Disable Teredo interface"""
157 self._guest_exec('netsh interface teredo set state disabled')
159 @sysprep('Disabling ISATAP Adapters')
160 def disable_isatap(self):
161 """Disable ISATAP Adapters"""
163 self._guest_exec('netsh interface isa set state disabled')
165 @sysprep('Enabling ping responses')
166 def enable_pings(self):
167 """Enable ping responses"""
169 self._guest_exec('netsh firewall set icmpsetting 8')
171 @sysprep('Disabling hibernation support')
172 def disable_hibernation(self):
173 """Disable hibernation support and remove the hibernation file"""
175 self._guest_exec(r'powercfg.exe /hibernate off')
177 @sysprep('Setting the system clock to UTC')
179 """Set the hardware clock to UTC"""
181 path = r'HKLM\SYSTEM\CurrentControlSet\Control\TimeZoneInformation'
183 r'REG ADD %s /v RealTimeIsUniversal /t REG_DWORD /d 1 /f' % path)
185 @sysprep('Clearing the event logs')
186 def clear_logs(self):
187 """Clear all the event logs"""
190 r"cmd /q /c for /f %l in ('wevtutil el') do wevtutil cl %l")
192 @sysprep('Executing Sysprep on the image (may take more that 10 minutes)')
193 def microsoft_sysprep(self):
194 """Run the Microsoft System Preparation Tool. This will remove
195 system-specific data and will make the image ready to be deployed.
196 After this no other task may run.
199 self._guest_exec(r'C:\Windows\system32\sysprep\sysprep '
200 r'/quiet /generalize /oobe /shutdown')
201 self.syspreped = True
203 @sysprep('Converting the image into a KMS client', enabled=False)
204 def kms_client_setup(self):
205 """Install the appropriate KMS client setup key to the image to convert
206 it to a KMS client. Computers that are running volume licensing
207 editions of Windows 8, Windows Server 2012, Windows 7, Windows Server
208 2008 R2, Windows Vista, and Windows Server 2008 are, by default, KMS
209 clients with no additional configuration needed.
212 setup_key = KMS_CLIENT_SETUP_KEYS[self.product_name]
215 "Don't know the KMS client setup key for product: `%s'" %
220 r"cscript \Windows\system32\slmgr.vbs /ipk %s" % setup_key)
222 @sysprep('Shrinking the last filesystem')
224 """Shrink the last filesystem. Make sure the filesystem is defragged"""
226 # Query for the maximum number of reclaimable bytes
228 r'cmd /Q /V:ON /C "SET SCRIPT=%TEMP%\QUERYMAX_%RANDOM%.TXT & ' +
229 r'ECHO SELECT DISK 0 > %SCRIPT% & ' +
230 'ECHO SELECT PARTITION %d >> %%SCRIPT%% & ' % self.last_part_num +
231 r'ECHO SHRINK QUERYMAX >> %SCRIPT% & ' +
232 r'ECHO EXIT >> %SCRIPT% & ' +
233 r'DISKPART /S %SCRIPT% & ' +
234 r'IF NOT !ERRORLEVEL! EQU 0 EXIT /B 1 & ' +
237 stdout, stderr, rc = self._guest_exec(cmd)
240 for line in stdout.splitlines():
241 # diskpart will return something like this:
243 # The maximum number of reclaimable bytes is: xxxx MB
245 if line.find('reclaimable') >= 0:
246 querymax = line.split(':')[1].split()[0].strip()
247 assert querymax.isdigit(), \
248 "Number of reclaimable bytes not a number"
251 FatalError("Error in shrinking! "
252 "Couldn't find the max number of reclaimable bytes!")
254 querymax = int(querymax)
256 # Practically the smallest shrunken size generally is at around
257 # "used space" + (20-200 MB). Please also take into account that
258 # Windows might need about 50-100 MB free space left to boot safely.
259 # I'll give 100MB extra space just to be sure
263 self.out.warn("Not enought available space to shrink the image!")
266 self.out.output("\tReclaiming %dMB ..." % querymax)
269 r'cmd /Q /V:ON /C "SET SCRIPT=%TEMP%\QUERYMAX_%RANDOM%.TXT & ' +
270 r'ECHO SELECT DISK 0 > %SCRIPT% & ' +
271 'ECHO SELECT PARTITION %d >> %%SCRIPT%% & ' % self.last_part_num +
272 'ECHO SHRINK DESIRED=%d >> %%SCRIPT%% & ' % querymax +
273 r'ECHO EXIT >> %SCRIPT% & ' +
274 r'DISKPART /S %SCRIPT% & ' +
275 r'IF NOT !ERRORLEVEL! EQU 0 EXIT /B 1 & ' +
278 stdout, stderr, rc = self._guest_exec(cmd)
280 for line in stdout.splitlines():
281 if line.find('shrunk') >= 0:
282 self.out.output(line)
284 def do_sysprep(self):
285 """Prepare system for image creation."""
287 if getattr(self, 'syspreped', False):
288 raise FatalError("Image is already syspreped!")
290 txt = "System preparation parameter: `%s' is needed but missing!"
291 for name, param in self.needed_sysprep_params.items():
292 if name not in self.sysprep_params:
293 raise FatalError(txt % name)
295 self.mount(readonly=False)
297 disabled_uac = self._update_uac_remote_setting(1)
298 token = self._enable_os_monitor()
300 # disable the firewalls
301 firewall_states = self._update_firewalls(0, 0, 0)
303 # Delete the pagefile. It will be recreated when the system boots
304 systemroot = self.image.g.inspect_get_windows_systemroot(self.root)
306 pagefile = "%s/pagefile.sys" % systemroot
307 self.image.g.rm_rf(self.image.g.case_sensitive_path(pagefile))
314 self.image.disable_guestfs()
319 self.out.output("Starting windows VM ...", False)
320 monitorfd, monitor = tempfile.mkstemp()
322 vm = _VM(self.image.device, monitor, self.sysprep_params)
323 self.out.success("started (console on vnc display: %d)." %
326 self.out.output("Waiting for OS to boot ...", False)
327 self._wait_vm_boot(vm, monitor, token)
328 self.out.success('done')
330 self.out.output("Checking connectivity to the VM ...", False)
331 self._check_connectivity()
332 self.out.success('done')
334 self.out.output("Disabling automatic logon ...", False)
335 self._disable_autologon()
336 self.out.success('done')
338 self.out.output('Preparing system for image creation:')
340 tasks = self.list_syspreps()
341 enabled = [task for task in tasks if task.enabled]
344 # Make sure shrink runs in the end, before ms sysprep
345 enabled = [task for task in enabled if
346 self.sysprep_info(task).name != 'shrink']
348 if len(enabled) != size:
349 enabled.append(self.shrink)
351 # Make sure the ms sysprep is the last task to run if it is enabled
352 enabled = [task for task in enabled if
353 self.sysprep_info(task).name != 'microsoft-sysprep']
355 ms_sysprep_enabled = False
356 if len(enabled) != size:
357 enabled.append(self.microsoft_sysprep)
358 ms_sysprep_enabled = True
363 self.out.output(('(%d/%d)' % (cnt, size)).ljust(7), False)
365 setattr(task.im_func, 'executed', True)
367 self.out.output("Sending shut down command ...", False)
368 if not ms_sysprep_enabled:
370 self.out.success("done")
372 self.out.output("Waiting for windows to shut down ...", False)
373 vm.wait(self.sysprep_params['shutdown_timeout'])
374 self.out.success("done")
376 if monitor is not None:
381 self.out.output("Destroying windows VM ...", False)
383 self.out.success("done")
385 self.image.enable_guestfs()
387 self.mount(readonly=False)
390 self._update_uac_remote_setting(0)
392 self._update_firewalls(*firewall_states)
397 """Shuts down the windows VM"""
398 self._guest_exec(r'shutdown /s /t 5')
400 def _wait_vm_boot(self, vm, fname, msg):
401 """Wait until a message appears on a file or the vm process dies"""
403 for _ in range(self.sysprep_params['boot_timeout']):
405 with open(fname) as f:
407 if line.startswith(msg):
410 raise FatalError("Windows VM died unexpectedly!")
412 raise FatalError("Windows VM booting timed out!")
414 def _disable_autologon(self):
415 """Disable automatic logon on the windows image"""
418 r'"HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon"'
420 self._guest_exec('REG DELETE %s /v DefaultUserName /f' % winlogon)
421 self._guest_exec('REG DELETE %s /v DefaultPassword /f' % winlogon)
422 self._guest_exec('REG DELETE %s /v AutoAdminLogon /f' % winlogon)
424 def _registry_file_path(self, regfile):
425 """Retrieves the case sensitive path to a registry file"""
427 systemroot = self.image.g.inspect_get_windows_systemroot(self.root)
428 path = "%s/system32/config/%s" % (systemroot, regfile)
430 path = self.image.g.case_sensitive_path(path)
431 except RuntimeError as error:
432 raise FatalError("Unable to retrieve registry file: %s. Reason: %s"
433 % (regfile, str(error)))
436 def _enable_os_monitor(self):
437 """Add a script in the registry that will send a random string to the
438 first serial port when the windows image finishes booting.
441 token = "".join(random.choice(string.ascii_letters) for x in range(16))
443 path = self._registry_file_path('SOFTWARE')
444 softwarefd, software = tempfile.mkstemp()
447 self.image.g.download(path, software)
449 h = hivex.Hivex(software, write=True)
451 # Enable automatic logon.
452 # This is needed because we need to execute a script that we add in
453 # the RunOnce registry entry and those programs only get executed
454 # when a user logs on. There is a RunServicesOnce registry entry
455 # whose keys get executed in the background when the logon dialog
456 # box first appears, but they seem to only work with services and
457 # not arbitrary command line expressions :-(
459 # Instructions on how to turn on automatic logon in Windows can be
460 # found here: http://support.microsoft.com/kb/324737
462 # Warning: Registry change will not work if the “Logon Banner” is
463 # defined on the server either by a Group Policy object (GPO) or by
467 for child in ('Microsoft', 'Windows NT', 'CurrentVersion',
469 winlogon = h.node_get_child(winlogon, child)
473 {'key': 'DefaultUserName', 't': 1,
474 'value': "Administrator".encode('utf-16le')})
477 {'key': 'DefaultPassword', 't': 1,
478 'value': self.sysprep_params['password'].encode('utf-16le')})
481 {'key': 'AutoAdminLogon', 't': 1,
482 'value': "1".encode('utf-16le')})
485 for child in ('Microsoft', 'Windows', 'CurrentVersion'):
486 key = h.node_get_child(key, child)
488 runonce = h.node_get_child(key, "RunOnce")
490 runonce = h.node_add_child(key, "RunOnce")
493 r'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe '
494 r'-ExecutionPolicy RemoteSigned '
495 r'"&{$port=new-Object System.IO.Ports.SerialPort COM1,9600,'
496 r'None,8,one;$port.open();$port.WriteLine(\"' + token + r'\");'
497 r'$port.Close()}"').encode('utf-16le')
499 h.node_set_value(runonce,
500 {'key': "BootMonitor", 't': 1, 'value': value})
503 r'REG ADD HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion'
504 r'\policies\system /v LocalAccountTokenFilterPolicy'
505 r' /t REG_DWORD /d 1 /f').encode('utf-16le')
507 h.node_set_value(runonce,
508 {'key': "UpdateRegistry", 't': 1, 'value': value})
512 self.image.g.upload(software, path)
518 def _update_firewalls(self, domain, public, standard):
519 """Enables or disables the firewall for the Domain, the Public and the
520 Standard profile. Returns a triplete with the old values.
522 1 will enable a firewall and 0 will disable it
525 if domain not in (0, 1):
526 raise ValueError("Valid values for domain parameter are 0 and 1")
528 if public not in (0, 1):
529 raise ValueError("Valid values for public parameter are 0 and 1")
531 if standard not in (0, 1):
532 raise ValueError("Valid values for standard parameter are 0 and 1")
534 path = self._registry_file_path("SYSTEM")
535 systemfd, system = tempfile.mkstemp()
538 self.image.g.download(path, system)
540 h = hivex.Hivex(system, write=True)
542 select = h.node_get_child(h.root(), 'Select')
543 current_value = h.node_get_value(select, 'Current')
545 # expecting a little endian dword
546 assert h.value_type(current_value)[1] == 4
547 current = "%03d" % h.value_dword(current_value)
549 firewall_policy = h.root()
550 for child in ('ControlSet%s' % current, 'services', 'SharedAccess',
551 'Parameters', 'FirewallPolicy'):
552 firewall_policy = h.node_get_child(firewall_policy, child)
555 new_values = [domain, public, standard]
556 for profile in ('Domain', 'Public', 'Standard'):
557 node = h.node_get_child(firewall_policy, '%sProfile' % profile)
559 old_value = h.node_get_value(node, 'EnableFirewall')
561 # expecting a little endian dword
562 assert h.value_type(old_value)[1] == 4
563 old_values.append(h.value_dword(old_value))
566 node, {'key': 'EnableFirewall', 't': 4L,
567 'value': struct.pack("<I", new_values.pop(0))})
570 self.image.g.upload(system, path)
577 def _update_uac_remote_setting(self, value):
578 """Updates the registry key value:
579 [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies
580 \System]"LocalAccountTokenFilterPolicy"
582 value = 1 will disable the UAC remote restrictions
583 value = 0 will enable the UAC remote restrictions
585 For more info see here: http://support.microsoft.com/kb/951016
588 True if the key is changed
589 False if the key is unchanged
592 if value not in (0, 1):
593 raise ValueError("Valid values for value parameter are 0 and 1")
595 path = self._registry_file_path('SOFTWARE')
596 softwarefd, software = tempfile.mkstemp()
599 self.image.g.download(path, software)
601 h = hivex.Hivex(software, write=True)
604 for child in ('Microsoft', 'Windows', 'CurrentVersion', 'Policies',
606 key = h.node_get_child(key, child)
609 for val in h.node_values(key):
610 if h.value_key(val) == "LocalAccountTokenFilterPolicy":
613 if policy is not None:
614 dword = h.value_dword(policy)
620 new_value = {'key': "LocalAccountTokenFilterPolicy", 't': 4L,
621 'value': struct.pack("<I", value)}
623 h.node_set_value(key, new_value)
626 self.image.g.upload(software, path)
633 def _do_collect_metadata(self):
634 """Collect metadata about the OS"""
635 super(Windows, self)._do_collect_metadata()
636 self.meta["USERS"] = " ".join(self._get_users())
638 def _get_users(self):
639 """Returns a list of users found in the images"""
640 samfd, sam = tempfile.mkstemp()
643 self.image.g.download(self._registry_file_path('SAM'), sam)
647 # Navigate to /SAM/Domains/Account/Users
648 users_node = h.root()
649 for child in ('SAM', 'Domains', 'Account', 'Users'):
650 users_node = h.node_get_child(users_node, child)
652 # Navigate to /SAM/Domains/Account/Users/Names
653 names_node = h.node_get_child(users_node, 'Names')
655 # HKEY_LOCAL_MACHINE\SAM\SAM\Domains\Account\Users\%RID%
656 # HKEY_LOCAL_MACHINE\SAM\SAM\Domains\Account\Users\Names\%Username%
658 # The RID (relative identifier) of each user is stored as the type!
659 # (not the value) of the default key of the node under Names whose
660 # name is the user's username. Under the RID node, there in a F
661 # value that contains information about this user account.
663 # See sam.h of the chntpw project on how to translate the F value
664 # of an account in the registry. Bytes 56 & 57 are the account type
665 # and status flags. The first bit is the 'account disabled' bit
666 disabled = lambda f: int(f[56].encode('hex'), 16) & 0x01
669 for user_node in h.node_children(names_node):
670 username = h.node_name(user_node)
671 rid = h.value_type(h.node_get_value(user_node, ""))[0]
672 # if RID is 500 (=0x1f4), the corresponding node name under
673 # Users is '000001F4'
674 key = ("%8.x" % rid).replace(' ', '0').upper()
675 rid_node = h.node_get_child(users_node, key)
676 f_value = h.value_value(h.node_get_value(rid_node, 'F'))[1]
678 if disabled(f_value):
679 self.out.warn("Found disabled `%s' account!" % username)
682 users.append(username)
687 # Filter out the guest account
690 def _check_connectivity(self):
691 """Check if winexe works on the Windows VM"""
693 retries = self.sysprep_params['connection_retries']
694 # If the connection_retries parameter is set to 0 disable the
699 passwd = self.sysprep_params['password']
700 winexe = WinEXE('Administrator', passwd, 'localhost')
701 winexe.uninstall().debug(9)
703 for i in range(retries):
704 (stdout, stderr, rc) = winexe.run('cmd /C')
707 log = tempfile.NamedTemporaryFile(delete=False)
709 log.file.write(stdout)
712 self.out.output("failed! See: `%s' for the full output" % log.name)
714 self.out.output("retrying ...", False)
716 raise FatalError("Connection to the Windows VM failed after %d retries"
719 def _guest_exec(self, command, fatal=True):
720 """Execute a command on a windows VM"""
722 passwd = self.sysprep_params['password']
724 winexe = WinEXE('Administrator', passwd, 'localhost')
725 winexe.runas('Administrator', passwd).uninstall()
728 (stdout, stderr, rc) = winexe.run(command)
729 except WinexeTimeout:
730 FatalError("Command: `%s' timeout out." % command)
732 if rc != 0 and fatal:
733 reason = stderr if len(stderr) else stdout
734 self.out.output("Command: `%s' failed (rc=%d). Reason: %s" %
735 (command, rc, reason))
736 raise FatalError("Command: `%s' failed (rc=%d). Reason: %s" %
737 (command, rc, reason))
739 return (stdout, stderr, rc)
743 """Windows Virtual Machine"""
744 def __init__(self, disk, serial, params):
745 """Create _VM instance
748 serial: File to save the output of the serial port
756 """creates a random mac address"""
757 mac = [0x00, 0x16, 0x3e,
758 random.randint(0x00, 0x7f),
759 random.randint(0x00, 0xff),
760 random.randint(0x00, 0xff)]
762 return ':'.join(['%02x' % x for x in mac])
764 # Use ganeti's VNC port range for a random vnc port
765 self.display = random.randint(11000, 14999) - 5900
767 kvm = get_kvm_binary()
770 FatalError("Can't find the kvm binary")
773 kvm, '-smp', '1', '-m', '1024', '-drive',
774 'file=%s,format=raw,cache=unsafe,if=virtio' % self.disk,
775 '-netdev', 'type=user,hostfwd=tcp::445-:445,id=netdev0',
776 '-device', 'virtio-net-pci,mac=%s,netdev=netdev0' % random_mac(),
777 '-vnc', ':%d' % self.display, '-serial', 'file:%s' % self.serial,
780 self.process = subprocess.Popen(args, stdin=subprocess.PIPE,
781 stdout=subprocess.PIPE)
784 """Check if the VM is still alive"""
785 return self.process.poll() is None
790 if not self.isalive():
793 def handler(signum, frame):
794 self.process.terminate()
799 raise FatalError("VM destroy timed-out")
801 signal.signal(signal.SIGALRM, handler)
803 signal.alarm(self.params['shutdown_timeout'])
804 self.process.communicate(input="system_powerdown\n")
807 def wait(self, timeout=0):
808 """Wait for the VM to terminate"""
810 def handler(signum, frame):
812 raise FatalError("VM wait timed-out.")
814 signal.signal(signal.SIGALRM, handler)
816 signal.alarm(timeout)
817 stdout, stderr = self.process.communicate()
820 return (stdout, stderr, self.process.poll())
822 # vim: set sta sts=4 shiftwidth=4 sw=4 et ai :