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
41 from image_creator.winexe import WinEXE, WinexeTimeout
54 SHUTDOWN_TIMEOUT = 120
55 CONNECTION_RETRIES = 5
57 # For more info see: http://technet.microsoft.com/en-us/library/jj612867.aspx
58 KMS_CLIENT_SETUP_KEYS = {
59 "Windows 8 Professional": "NG4HW-VH26C-733KW-K6F98-J8CK4",
60 "Windows 8 Professional N": "XCVCF-2NXM9-723PB-MHCB7-2RYQQ",
61 "Windows 8 Enterprise": "32JNW-9KQ84-P47T8-D8GGY-CWCK7",
62 "Windows 8 Enterprise N": "JMNMF-RHW7P-DMY6X-RF3DR-X2BQT",
63 "Windows Server 2012 Core": "BN3D2-R7TKB-3YPBD-8DRP2-27GG4",
64 "Windows Server 2012 Core N": "8N2M2-HWPGY-7PGT9-HGDD8-GVGGY",
65 "Windows Server 2012 Core Single Language":
66 "2WN2H-YGCQR-KFX6K-CD6TF-84YXQ",
67 "Windows Server 2012 Core Country Specific":
68 "4K36P-JN4VD-GDC6V-KDT89-DYFKP",
69 "Windows Server 2012 Server Standard": "XC9B7-NBPP2-83J2H-RHMBY-92BT4",
70 "Windows Server 2012 Standard Core": "XC9B7-NBPP2-83J2H-RHMBY-92BT4",
71 "Windows Server 2012 MultiPoint Standard": "HM7DN-YVMH3-46JC3-XYTG7-CYQJJ",
72 "Windows Server 2012 MultiPoint Premium": "XNH6W-2V9GX-RGJ4K-Y8X6F-QGJ2G",
73 "Windows Server 2012 Datacenter": "48HP8-DN98B-MYWDG-T2DCC-8W83P",
74 "Windows Server 2012 Datacenter Core": "48HP8-DN98B-MYWDG-T2DCC-8W83P",
75 "Windows 7 Professional": "FJ82H-XT6CR-J8D7P-XQJJ2-GPDD4",
76 "Windows 7 Professional N": "MRPKT-YTG23-K7D7T-X2JMM-QY7MG",
77 "Windows 7 Professional E": "W82YF-2Q76Y-63HXB-FGJG9-GF7QX",
78 "Windows 7 Enterprise": "33PXH-7Y6KF-2VJC9-XBBR8-HVTHH",
79 "Windows 7 Enterprise N": "YDRBP-3D83W-TY26F-D46B2-XCKRJ",
80 "Windows 7 Enterprise E": "C29WB-22CC8-VJ326-GHFJW-H9DH4",
81 "Windows Server 2008 R2 Web": "6TPJF-RBVHG-WBW2R-86QPH-6RTM4",
82 "Windows Server 2008 R2 HPC edition": "TT8MH-CG224-D3D7Q-498W2-9QCTX",
83 "Windows Server 2008 R2 Standard": "YC6KT-GKW9T-YTKYR-T4X34-R7VHC",
84 "Windows Server 2008 R2 Enterprise": "489J6-VHDMP-X63PK-3K798-CPX3Y",
85 "Windows Server 2008 R2 Datacenter": "74YFP-3QFB3-KQT8W-PMXWJ-7M648",
86 "Windows Server 2008 R2 for Itanium-based Systems":
87 "GT63C-RJFQ3-4GMB6-BRFB9-CB83V",
88 "Windows Vista Business": "YFKBB-PQJJV-G996G-VWGXY-2V3X8",
89 "Windows Vista Business N": "HMBQG-8H2RH-C77VX-27R82-VMQBT",
90 "Windows Vista Enterprise": "VKK3X-68KWM-X2YGT-QR4M6-4BWMV",
91 "Windows Vista Enterprise N": "VTC42-BM838-43QHV-84HX6-XJXKV",
92 "Windows Web Server 2008": "WYR28-R7TFJ-3X2YQ-YCY4H-M249D",
93 "Windows Server 2008 Standard": "TM24T-X9RMF-VWXK6-X8JC9-BFGM2",
94 "Windows Server 2008 Standard without Hyper-V":
95 "W7VD6-7JFBR-RX26B-YKQ3Y-6FFFJ",
96 "Windows Server 2008 Enterprise":
97 "YQGMW-MPWTJ-34KDK-48M3W-X4Q6V",
98 "Windows Server 2008 Enterprise without Hyper-V":
99 "39BXF-X8Q23-P2WWT-38T2F-G3FPG",
100 "Windows Server 2008 HPC": "RCTX3-KWVHP-BR6TB-RB6DM-6X7HP",
101 "Windows Server 2008 Datacenter": "7M67G-PC374-GR742-YH8V4-TCBY3",
102 "Windows Server 2008 Datacenter without Hyper-V":
103 "22XQ2-VRXRG-P8D42-K34TD-G3QQC",
104 "Windows Server 2008 for Itanium-Based Systems":
105 "4DWFP-JF3DJ-B7DTH-78FJB-PDRHK"}
108 class Windows(OSBase):
109 """OS class for Windows"""
111 @add_sysprep_param('password', str, None, 'Image Administrator Password')
112 def __init__(self, image, **kargs):
113 super(Windows, self).__init__(image, **kargs)
115 device = self.g.part_to_dev(self.root)
117 self.last_part_num = self.g.part_list(device)[-1]['part_num']
118 self.last_drive = None
119 self.system_drive = None
121 for drive, partition in self.g.inspect_get_drive_mappings(self.root):
122 if partition == "%s%d" % (device, self.last_part_num):
123 self.last_drive = drive
124 if partition == self.root:
125 self.system_drive = drive
127 assert self.system_drive
129 self.product_name = self.g.inspect_get_product_name(self.root)
130 self.syspreped = False
132 @sysprep('Disabling IPv6 privacy extensions')
133 def disable_ipv6_privacy_extensions(self):
134 """Disable IPv6 privacy extensions"""
136 self._guest_exec('netsh interface ipv6 set global '
137 'randomizeidentifiers=disabled store=persistent')
139 @sysprep('Disabling Teredo interface')
140 def disable_teredo(self):
141 """Disable Teredo interface"""
143 self._guest_exec('netsh interface teredo set state disabled')
145 @sysprep('Disabling ISATAP Adapters')
146 def disable_isatap(self):
147 """Disable ISATAP Adapters"""
149 self._guest_exec('netsh interface isa set state disabled')
151 @sysprep('Enabling ping responses')
152 def enable_pings(self):
153 """Enable ping responses"""
155 self._guest_exec('netsh firewall set icmpsetting 8')
157 @sysprep('Disabling hibernation support')
158 def disable_hibernation(self):
159 """Disable hibernation support and remove the hibernation file"""
161 self._guest_exec(r'powercfg.exe /hibernate off')
163 @sysprep('Setting the system clock to UTC')
165 """Set the hardware clock to UTC"""
167 path = r'HKLM\SYSTEM\CurrentControlSet\Control\TimeZoneInformation'
169 r'REG ADD %s /v RealTimeIsUniversal /t REG_DWORD /d 1 /f' % path)
171 @sysprep('Clearing the event logs')
172 def clear_logs(self):
173 """Clear all the event logs"""
176 r"cmd /q /c for /f %l in ('wevtutil el') do wevtutil cl %l")
178 @sysprep('Executing Sysprep on the image (may take more that 10 minutes)')
179 def microsoft_sysprep(self):
180 """Run the Microsoft System Preparation Tool. This will remove
181 system-specific data and will make the image ready to be deployed.
182 After this no other task may run.
185 self._guest_exec(r'C:\Windows\system32\sysprep\sysprep '
186 r'/quiet /generalize /oobe /shutdown')
187 self.syspreped = True
189 @sysprep('Converting the image into a KMS client', enabled=False)
190 def kms_client_setup(self):
191 """Install the appropriate KMS client setup key to the image to convert
192 it to a KMS client. Computers that are running volume licensing
193 editions of Windows 8, Windows Server 2012, Windows 7, Windows Server
194 2008 R2, Windows Vista, and Windows Server 2008 are, by default, KMS
195 clients with no additional configuration needed.
198 setup_key = KMS_CLIENT_SETUP_KEYS[self.product_name]
201 "Don't know the KMS client setup key for product: `%s'" %
206 r"cscript \Windows\system32\slmgr.vbs /ipk %s" % setup_key)
208 @sysprep('Shrinking the last filesystem')
210 """Shrink the last filesystem. Make sure the filesystem is defragged"""
212 # Query for the maximum number of reclaimable bytes
214 r'cmd /Q /V:ON /C "SET SCRIPT=%TEMP%\QUERYMAX_%RANDOM%.TXT & ' +
215 r'ECHO SELECT DISK 0 > %SCRIPT% & ' +
216 'ECHO SELECT PARTITION %d >> %%SCRIPT%% & ' % self.last_part_num +
217 r'ECHO SHRINK QUERYMAX >> %SCRIPT% & ' +
218 r'ECHO EXIT >> %SCRIPT% & ' +
219 r'DISKPART /S %SCRIPT% & ' +
220 r'IF NOT !ERRORLEVEL! EQU 0 EXIT /B 1 & ' +
223 stdout, stderr, rc = self._guest_exec(cmd)
226 for line in stdout.splitlines():
227 # diskpart will return something like this:
229 # The maximum number of reclaimable bytes is: xxxx MB
231 if line.find('reclaimable') >= 0:
232 querymax = line.split(':')[1].split()[0].strip()
233 assert querymax.isdigit(), \
234 "Number of reclaimable bytes not a number"
237 FatalError("Error in shrinking! "
238 "Couldn't find the max number of reclaimable bytes!")
240 querymax = int(querymax)
242 # Practically the smallest shrunken size generally is at around
243 # "used space" + (20-200 MB). Please also take into account that
244 # Windows might need about 50-100 MB free space left to boot safely.
245 # I'll give 100MB extra space just to be sure
249 self.out.warn("Not enought available space to shrink the image!")
252 self.out.output("\tReclaiming %dMB ..." % querymax)
255 r'cmd /Q /V:ON /C "SET SCRIPT=%TEMP%\QUERYMAX_%RANDOM%.TXT & ' +
256 r'ECHO SELECT DISK 0 > %SCRIPT% & ' +
257 'ECHO SELECT PARTITION %d >> %%SCRIPT%% & ' % self.last_part_num +
258 'ECHO SHRINK DESIRED=%d >> %%SCRIPT%% & ' % querymax +
259 r'ECHO EXIT >> %SCRIPT% & ' +
260 r'DISKPART /S %SCRIPT% & ' +
261 r'IF NOT !ERRORLEVEL! EQU 0 EXIT /B 1 & ' +
264 stdout, stderr, rc = self._guest_exec(cmd)
266 for line in stdout.splitlines():
267 if line.find('shrunk') >= 0:
268 self.out.output(line)
270 def do_sysprep(self):
271 """Prepare system for image creation."""
273 if getattr(self, 'syspreped', False):
274 raise FatalError("Image is already syspreped!")
276 txt = "System preparation parameter: `%s' is needed but missing!"
277 for name, param in self.needed_sysprep_params.items():
278 if name not in self.sysprep_params:
279 raise FatalError(txt % param)
281 self.mount(readonly=False)
283 disabled_uac = self._update_uac_remote_setting(1)
284 token = self._enable_os_monitor()
286 # disable the firewalls
287 firewall_states = self._update_firewalls(0, 0, 0)
289 # Delete the pagefile. It will be recreated when the system boots
290 systemroot = self.g.inspect_get_windows_systemroot(self.root)
291 pagefile = "%s/pagefile.sys" % systemroot
292 self.g.rm_rf(self.g.case_sensitive_path(pagefile))
297 self.out.output("Shutting down helper VM ...", False)
299 # guestfs_shutdown which is the prefered way to shutdown the backend
300 # process was introduced in version 1.19.16
301 if check_guestfs_version(self.g, 1, 19, 16) >= 0:
304 self.g.kill_subprocess()
306 self.out.success('done')
311 self.out.output("Starting windows VM ...", False)
312 monitorfd, monitor = tempfile.mkstemp()
314 vm = _VM(self.image.device, monitor)
315 self.out.success("started (console on vnc display: %d)." %
318 self.out.output("Waiting for OS to boot ...", False)
319 self._wait_vm_boot(vm, monitor, token)
320 self.out.success('done')
322 self.out.output("Checking connectivity to the VM ...", False)
323 self._check_connectivity()
324 self.out.success('done')
326 self.out.output("Disabling automatic logon ...", False)
327 self._disable_autologon()
328 self.out.success('done')
330 self.out.output('Preparing system for image creation:')
332 tasks = self.list_syspreps()
333 enabled = [task for task in tasks if task.enabled]
336 # Make sure shrink runs in the end, before ms sysprep
337 enabled = [task for task in enabled if
338 self.sysprep_info(task).name != 'shrink']
340 if len(enabled) != size:
341 enabled.append(self.shrink)
343 # Make sure the ms sysprep is the last task to run if it is enabled
344 enabled = [task for task in enabled if
345 self.sysprep_info(task).name != 'microsoft-sysprep']
347 ms_sysprep_enabled = False
348 if len(enabled) != size:
349 enabled.append(self.microsoft_sysprep)
350 ms_sysprep_enabled = True
355 self.out.output(('(%d/%d)' % (cnt, size)).ljust(7), False)
357 setattr(task.im_func, 'executed', True)
359 self.out.output("Sending shut down command ...", False)
360 if not ms_sysprep_enabled:
362 self.out.success("done")
364 self.out.output("Waiting for windows to shut down ...", False)
365 vm.wait(SHUTDOWN_TIMEOUT)
366 self.out.success("done")
368 if monitor is not None:
373 self.out.output("Destroying windows VM ...", False)
375 self.out.success("done")
377 self.out.output("Relaunching helper VM (may take a while) ...",
380 self.out.success('done')
382 self.mount(readonly=False)
385 self._update_uac_remote_setting(0)
387 self._update_firewalls(*firewall_states)
392 """Shuts down the windows VM"""
393 self._guest_exec(r'shutdown /s /t 5')
395 def _wait_vm_boot(self, vm, fname, msg):
396 """Wait until a message appears on a file or the vm process dies"""
398 for _ in range(BOOT_TIMEOUT):
400 with open(fname) as f:
402 if line.startswith(msg):
405 raise FatalError("Windows VM died unexpectedly!")
407 raise FatalError("Windows VM booting timed out!")
409 def _disable_autologon(self):
410 """Disable automatic logon on the windows image"""
413 r'"HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon"'
415 self._guest_exec('REG DELETE %s /v DefaultUserName /f' % winlogon)
416 self._guest_exec('REG DELETE %s /v DefaultPassword /f' % winlogon)
417 self._guest_exec('REG DELETE %s /v AutoAdminLogon /f' % winlogon)
419 def _registry_file_path(self, regfile):
420 """Retrieves the case sensitive path to a registry file"""
422 systemroot = self.g.inspect_get_windows_systemroot(self.root)
423 path = "%s/system32/config/%s" % (systemroot, regfile)
425 path = self.g.case_sensitive_path(path)
426 except RuntimeError as error:
427 raise FatalError("Unable to retrieve registry file: %s. Reason: %s"
428 % (regfile, str(error)))
431 def _enable_os_monitor(self):
432 """Add a script in the registry that will send a random string to the
433 first serial port when the windows image finishes booting.
436 token = "".join(random.choice(string.ascii_letters) for x in range(16))
438 path = self._registry_file_path('SOFTWARE')
439 softwarefd, software = tempfile.mkstemp()
442 self.g.download(path, software)
444 h = hivex.Hivex(software, write=True)
446 # Enable automatic logon.
447 # This is needed because we need to execute a script that we add in
448 # the RunOnce registry entry and those programs only get executed
449 # when a user logs on. There is a RunServicesOnce registry entry
450 # whose keys get executed in the background when the logon dialog
451 # box first appears, but they seem to only work with services and
452 # not arbitrary command line expressions :-(
454 # Instructions on how to turn on automatic logon in Windows can be
455 # found here: http://support.microsoft.com/kb/324737
457 # Warning: Registry change will not work if the “Logon Banner” is
458 # defined on the server either by a Group Policy object (GPO) or by
462 for child in ('Microsoft', 'Windows NT', 'CurrentVersion',
464 winlogon = h.node_get_child(winlogon, child)
468 {'key': 'DefaultUserName', 't': 1,
469 'value': "Administrator".encode('utf-16le')})
472 {'key': 'DefaultPassword', 't': 1,
473 'value': self.sysprep_params['password'].encode('utf-16le')})
476 {'key': 'AutoAdminLogon', 't': 1,
477 'value': "1".encode('utf-16le')})
480 for child in ('Microsoft', 'Windows', 'CurrentVersion'):
481 key = h.node_get_child(key, child)
483 runonce = h.node_get_child(key, "RunOnce")
485 runonce = h.node_add_child(key, "RunOnce")
488 r'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe '
489 r'-ExecutionPolicy RemoteSigned '
490 r'"&{$port=new-Object System.IO.Ports.SerialPort COM1,9600,'
491 r'None,8,one;$port.open();$port.WriteLine(\"' + token + r'\");'
492 r'$port.Close()}"').encode('utf-16le')
494 h.node_set_value(runonce,
495 {'key': "BootMonitor", 't': 1, 'value': value})
498 r'REG ADD HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion'
499 r'\policies\system /v LocalAccountTokenFilterPolicy'
500 r' /t REG_DWORD /d 1 /f').encode('utf-16le')
502 h.node_set_value(runonce,
503 {'key': "UpdateRegistry", 't': 1, 'value': value})
507 self.g.upload(software, path)
513 def _update_firewalls(self, domain, public, standard):
514 """Enables or disables the firewall for the Domain, the Public and the
515 Standard profile. Returns a triplete with the old values.
517 1 will enable a firewall and 0 will disable it
520 if domain not in (0, 1):
521 raise ValueError("Valid values for domain parameter are 0 and 1")
523 if public not in (0, 1):
524 raise ValueError("Valid values for public parameter are 0 and 1")
526 if standard not in (0, 1):
527 raise ValueError("Valid values for standard parameter are 0 and 1")
529 path = self._registry_file_path("SYSTEM")
530 systemfd, system = tempfile.mkstemp()
533 self.g.download(path, system)
535 h = hivex.Hivex(system, write=True)
537 select = h.node_get_child(h.root(), 'Select')
538 current_value = h.node_get_value(select, 'Current')
540 # expecting a little endian dword
541 assert h.value_type(current_value)[1] == 4
542 current = "%03d" % h.value_dword(current_value)
544 firewall_policy = h.root()
545 for child in ('ControlSet%s' % current, 'services', 'SharedAccess',
546 'Parameters', 'FirewallPolicy'):
547 firewall_policy = h.node_get_child(firewall_policy, child)
550 new_values = [domain, public, standard]
551 for profile in ('Domain', 'Public', 'Standard'):
552 node = h.node_get_child(firewall_policy, '%sProfile' % profile)
554 old_value = h.node_get_value(node, 'EnableFirewall')
556 # expecting a little endian dword
557 assert h.value_type(old_value)[1] == 4
558 old_values.append(h.value_dword(old_value))
561 node, {'key': 'EnableFirewall', 't': 4L,
562 'value': struct.pack("<I", new_values.pop(0))})
565 self.g.upload(system, path)
572 def _update_uac_remote_setting(self, value):
573 """Updates the registry key value:
574 [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies
575 \System]"LocalAccountTokenFilterPolicy"
577 value = 1 will disable the UAC remote restrictions
578 value = 0 will enable the UAC remote restrictions
580 For more info see here: http://support.microsoft.com/kb/951016
583 True if the key is changed
584 False if the key is unchanged
587 if value not in (0, 1):
588 raise ValueError("Valid values for value parameter are 0 and 1")
590 path = self._registry_file_path('SOFTWARE')
591 softwarefd, software = tempfile.mkstemp()
594 self.g.download(path, software)
596 h = hivex.Hivex(software, write=True)
599 for child in ('Microsoft', 'Windows', 'CurrentVersion', 'Policies',
601 key = h.node_get_child(key, child)
604 for val in h.node_values(key):
605 if h.value_key(val) == "LocalAccountTokenFilterPolicy":
608 if policy is not None:
609 dword = h.value_dword(policy)
615 new_value = {'key': "LocalAccountTokenFilterPolicy", 't': 4L,
616 'value': struct.pack("<I", value)}
618 h.node_set_value(key, new_value)
621 self.g.upload(software, path)
628 def _do_collect_metadata(self):
629 """Collect metadata about the OS"""
630 super(Windows, self)._do_collect_metadata()
631 self.meta["USERS"] = " ".join(self._get_users())
633 def _get_users(self):
634 """Returns a list of users found in the images"""
635 samfd, sam = tempfile.mkstemp()
638 self.g.download(self._registry_file_path('SAM'), sam)
642 # Navigate to /SAM/Domains/Account/Users
643 users_node = h.root()
644 for child in ('SAM', 'Domains', 'Account', 'Users'):
645 users_node = h.node_get_child(users_node, child)
647 # Navigate to /SAM/Domains/Account/Users/Names
648 names_node = h.node_get_child(users_node, 'Names')
650 # HKEY_LOCAL_MACHINE\SAM\SAM\Domains\Account\Users\%RID%
651 # HKEY_LOCAL_MACHINE\SAM\SAM\Domains\Account\Users\Names\%Username%
653 # The RID (relative identifier) of each user is stored as the type!
654 # (not the value) of the default key of the node under Names whose
655 # name is the user's username. Under the RID node, there in a F
656 # value that contains information about this user account.
658 # See sam.h of the chntpw project on how to translate the F value
659 # of an account in the registry. Bytes 56 & 57 are the account type
660 # and status flags. The first bit is the 'account disabled' bit
661 disabled = lambda f: int(f[56].encode('hex'), 16) & 0x01
664 for user_node in h.node_children(names_node):
665 username = h.node_name(user_node)
666 rid = h.value_type(h.node_get_value(user_node, ""))[0]
667 # if RID is 500 (=0x1f4), the corresponding node name under
668 # Users is '000001F4'
669 key = ("%8.x" % rid).replace(' ', '0').upper()
670 rid_node = h.node_get_child(users_node, key)
671 f_value = h.value_value(h.node_get_value(rid_node, 'F'))[1]
673 if disabled(f_value):
674 self.out.warn("Found disabled `%s' account!" % username)
677 users.append(username)
682 # Filter out the guest account
685 def _check_connectivity(self):
686 """Check if winexe works on the Windows VM"""
688 passwd = self.sysprep_params['password']
689 winexe = WinEXE('Administrator', passwd, 'localhost')
690 winexe.uninstall().debug(9)
692 for i in range(CONNECTION_RETRIES):
693 (stdout, stderr, rc) = winexe.run('cmd /C')
696 log = tempfile.NamedTemporaryFile(delete=False)
698 log.file.write(stdout)
701 self.out.output("failed! See: `%s' for the full output" % log.name)
702 if i < CONNECTION_RETRIES - 1:
703 self.out.output("Retrying ...", False)
704 raise FatalError("Connection to the VM failed after %d retries" %
707 def _guest_exec(self, command, fatal=True):
708 """Execute a command on a windows VM"""
710 passwd = self.sysprep_params['password']
712 winexe = WinEXE('Administrator', passwd, 'localhost')
713 winexe.runas('Administrator', passwd).uninstall()
716 (stdout, stderr, rc) = winexe.run(command)
717 except WinexeTimeout:
718 FatalError("Command: `%s' timeout out." % command)
720 if rc != 0 and fatal:
721 reason = stderr if len(stderr) else stdout
722 self.out.output("Command: `%s' failed (rc=%d). Reason: %s" %
723 (command, rc, reason))
724 raise FatalError("Command: `%s' failed (rc=%d). Reason: %s" %
725 (command, rc, reason))
727 return (stdout, stderr, rc)
731 """Windows Virtual Machine"""
732 def __init__(self, disk, serial):
733 """Create _VM instance
736 serial: File to save the output of the serial port
743 """creates a random mac address"""
744 mac = [0x00, 0x16, 0x3e,
745 random.randint(0x00, 0x7f),
746 random.randint(0x00, 0xff),
747 random.randint(0x00, 0xff)]
749 return ':'.join(['%02x' % x for x in mac])
751 # Use ganeti's VNC port range for a random vnc port
752 self.display = random.randint(11000, 14999) - 5900
755 'kvm', '-smp', '1', '-m', '1024', '-drive',
756 'file=%s,format=raw,cache=unsafe,if=virtio' % self.disk,
757 '-netdev', 'type=user,hostfwd=tcp::445-:445,id=netdev0',
758 '-device', 'virtio-net-pci,mac=%s,netdev=netdev0' % random_mac(),
759 '-vnc', ':%d' % self.display, '-serial', 'file:%s' % self.serial,
762 self.process = subprocess.Popen(args, stdin=subprocess.PIPE,
763 stdout=subprocess.PIPE)
766 """Check if the VM is still alive"""
767 return self.process.poll() is None
772 if not self.isalive():
775 def handler(signum, frame):
776 self.process.terminate()
781 raise FatalError("VM destroy timed-out")
783 signal.signal(signal.SIGALRM, handler)
785 signal.alarm(SHUTDOWN_TIMEOUT)
786 self.process.communicate(input="system_powerdown\n")
789 def wait(self, timeout=0):
790 """Wait for the VM to terminate"""
792 def handler(signum, frame):
794 raise FatalError("VM wait timed-out.")
796 signal.signal(signal.SIGALRM, handler)
798 signal.alarm(timeout)
799 stdout, stderr = self.process.communicate()
802 return (stdout, stderr, self.process.poll())
804 # vim: set sta sts=4 shiftwidth=4 sw=4 et ai :