Add extra `smp' & `mem' sysprep params in Windows
[snf-image-creator] / image_creator / os_type / windows.py
index a5ef78a..2890c62 100644 (file)
@@ -37,8 +37,7 @@
 Windows OSs."""
 
 from image_creator.os_type import OSBase, sysprep, add_sysprep_param
-from image_creator.util import FatalError, check_guestfs_version, \
-    get_kvm_binary
+from image_creator.util import FatalError, get_kvm_binary
 from image_creator.winexe import WinEXE, WinexeTimeout
 
 import hivex
@@ -50,9 +49,17 @@ import random
 import string
 import subprocess
 import struct
+import re
 
 # For more info see: http://technet.microsoft.com/en-us/library/jj612867.aspx
 KMS_CLIENT_SETUP_KEYS = {
+    "Windows 8.1 Professional": "GCRJD-8NW9H-F2CDX-CCM8D-9D6T9",
+    "Windows 8.1 Professional N": "HMCNV-VVBFX-7HMBH-CTY9B-B4FXY",
+    "Windows 8.1 Enterprise": "MHF9N-XY6XB-WVXMC-BTDCT-MKKG7",
+    "Windows 8.1 Enterprise N": "TT4HM-HN7YT-62K67-RGRQJ-JFFXW",
+    "Windows Server 2012 R2 Server Standard": "D2N9P-3P6X9-2R39C-7RTCD-MDVJX",
+    "Windows Server 2012 R2 Datacenter": "W3GGN-FT8W3-Y4M27-J84CP-Q3VJ9",
+    "Windows Server 2012 R2 Essentials": "KNC87-3J2TX-XB4WP-VCPJV-M4FWM",
     "Windows 8 Professional": "NG4HW-VH26C-733KW-K6F98-J8CK4",
     "Windows 8 Professional N": "XCVCF-2NXM9-723PB-MHCB7-2RYQQ",
     "Windows 8 Enterprise": "32JNW-9KQ84-P47T8-D8GGY-CWCK7",
@@ -112,10 +119,35 @@ class Windows(OSBase):
         'boot_timeout', int, 300, "Boot Timeout (seconds)", _POSINT)
     @add_sysprep_param(
         'connection_retries', int, 5, "Connection Retries", _POSINT)
+    @add_sysprep_param(
+        'smp', int, 1, "Number of CPUs for the helper VM", _POSINT)
+    @add_sysprep_param(
+        'mem', int, 1024, "Virtual RAM size for the helper VM (MiB)", _POSINT)
     @add_sysprep_param('password', str, None, 'Image Administrator Password')
     def __init__(self, image, **kargs):
         super(Windows, self).__init__(image, **kargs)
 
+        # The commit with the following message was added in
+        # libguestfs 1.17.18 and was backported in version 1.16.11:
+        #
+        # When a Windows guest doesn't have a HKLM\SYSTEM\MountedDevices node,
+        # inspection fails.  However inspection should not completely fail just
+        # because we cannot get the drive letter mapping from a guest.
+        #
+        # Since Microsoft Sysprep removes the aforementioned key, image
+        # creation for windows can only be supported if the installed guestfs
+        # version is 1.17.18 or higher
+        if self.image.check_guestfs_version(1, 17, 18) < 0 and \
+                (self.image.check_guestfs_version(1, 17, 0) >= 0 or
+                 self.image.check_guestfs_version(1, 16, 11) < 0):
+            raise FatalError(
+                'For windows support libguestfs 1.16.11 or above is required')
+
+        # Check if winexe is installed
+        if not WinEXE.is_installed():
+            raise FatalError(
+                "For windows support `Winexe' needs to be installed")
+
         device = self.image.g.part_to_dev(self.root)
 
         self.last_part_num = self.image.g.part_list(device)[-1]['part_num']
@@ -195,7 +227,7 @@ class Windows(OSBase):
         """Install the appropriate KMS client setup key to the image to convert
         it to a KMS client. Computers that are running volume licensing
         editions of Windows 8, Windows Server 2012, Windows 7, Windows Server
-        2008 R2, Windows Vista, and Windows Server 2008 are, by default, KMS
+        2008 R2, Windows Vista, and Windows Server 2008 are by default KMS
         clients with no additional configuration needed.
         """
         try:
@@ -233,9 +265,14 @@ class Windows(OSBase):
             #   The maximum number of reclaimable bytes is: xxxx MB
             #
             if line.find('reclaimable') >= 0:
-                querymax = line.split(':')[1].split()[0].strip()
-                assert querymax.isdigit(), \
-                    "Number of reclaimable bytes not a number"
+                answer = line.split(':')[1].strip()
+                m = re.search('(\d+) MB', answer)
+                if m:
+                    querymax = m.group(1)
+                else:
+                    FatalError(
+                        "Unexpected output for `shrink querymax' command: %s" %
+                        line)
 
         if querymax is None:
             FatalError("Error in shrinking! "
@@ -250,7 +287,7 @@ class Windows(OSBase):
         querymax -= 100
 
         if querymax < 0:
-            self.out.warn("Not enought available space to shrink the image!")
+            self.out.warn("Not enough available space to shrink the image!")
             return
 
         self.out.output("\tReclaiming %dMB ..." % querymax)
@@ -265,8 +302,12 @@ class Windows(OSBase):
             r'IF NOT !ERRORLEVEL! EQU 0 EXIT /B 1 & ' +
             r'DEL /Q %SCRIPT%"')
 
-        stdout, stderr, rc = self._guest_exec(cmd)
+        stdout, stderr, rc = self._guest_exec(cmd, False)
 
+        if rc != 0:
+            FatalError("Shrinking failed. Please make sure the media is "
+                       "defraged with a command like this: "
+                       "`Defrag.exe /U /X /W'")
         for line in stdout.splitlines():
             if line.find('shrunk') >= 0:
                 self.out.output(line)
@@ -754,18 +795,21 @@ class _VM(object):
         # Use ganeti's VNC port range for a random vnc port
         self.display = random.randint(11000, 14999) - 5900
 
-        kvm = get_kvm_binary()
+        kvm, needed_args = get_kvm_binary()
 
         if kvm is None:
             FatalError("Can't find the kvm binary")
 
-        args = [
-            kvm, '-smp', '1', '-m', '1024', '-drive',
-            'file=%s,format=raw,cache=unsafe,if=virtio' % self.disk,
+        args = [kvm]
+        args.extend(needed_args)
+
+        args.extend([
+            '-smp', str(self.params['smp']), '-m', str(self.params['mem']),
+            '-drive', 'file=%s,format=raw,cache=unsafe,if=virtio' % self.disk,
             '-netdev', 'type=user,hostfwd=tcp::445-:445,id=netdev0',
             '-device', 'virtio-net-pci,mac=%s,netdev=netdev0' % random_mac(),
             '-vnc', ':%d' % self.display, '-serial', 'file:%s' % self.serial,
-            '-monitor', 'stdio']
+            '-monitor', 'stdio'])
 
         self.process = subprocess.Popen(args, stdin=subprocess.PIPE,
                                         stdout=subprocess.PIPE)