Merge branch 'develop'
authorNikos Skalkotos <skalkoto@grnet.gr>
Wed, 8 Jan 2014 10:33:08 +0000 (12:33 +0200)
committerNikos Skalkotos <skalkoto@grnet.gr>
Wed, 8 Jan 2014 10:33:08 +0000 (12:33 +0200)
21 files changed:
README
ci/autopkg_debian.sh
ci/make_docs.sh
docs/conf.py
docs/man/snf-image-creator.rst
docs/man/snf-mkimage.rst
docs/usage.rst
image_creator/dialog_main.py
image_creator/dialog_menu.py
image_creator/image.py
image_creator/main.py
image_creator/os_type/__init__.py
image_creator/os_type/freebsd.py
image_creator/os_type/linux.py
image_creator/os_type/unsupported.py [new file with mode: 0644]
image_creator/os_type/windows.py
image_creator/output/dialog.py
image_creator/version.py
image_creator/winexe.py
setup.py
version

diff --git a/README b/README
index a05706d..6d6dacb 100644 (file)
--- a/README
+++ b/README
@@ -5,5 +5,5 @@ snf-image-creator is a command line tool for creating OS images to be used with
 synnefo.
 
 It comes in 2 variants:
- * snf-image-creator: A non-interactive command line program
- * snf-mkimage: A user-friendly dialog-based program
+ * snf-image-creator: A user-friendly dialog-based program
+ * snf-mkimage: A non-interactive command line program
index 70eecdd..62e678e 100755 (executable)
@@ -1,18 +1,18 @@
 #!/bin/sh
 set -e
 
-PACKAGES_DIR=$1
+PACKAGES_DIR="$1"
 
 shift
 
-TEMP_DIR=$(mktemp -d /tmp/devflow_autopkg_XXXXXXX)
+TEMP_DIR="$(mktemp -d /tmp/devflow_autopkg_XXXXXXX)"
 
 # Create the packages
-devflow-autopkg snapshot -b $TEMP_DIR $@
+devflow-autopkg snapshot -b "$TEMP_DIR" "$@"
 
 # MOVE the packages
-mkdir -p $PACKAGES_DIR
-mv -n $TEMP_DIR/* $PACKAGES_DIR
+mkdir -p "$PACKAGES_DIR"
+mv -n "$TEMP_DIR"/* "$PACKAGES_DIR"
 
 echo "Moved packages to: $(pwd)/$PACKAGES_DIR"
 
index 61a53d9..f9ed0e1 100755 (executable)
@@ -1,13 +1,13 @@
 #!/bin/sh
 set -e
 
-DOCS_DIR=$1
+DOCS_DIR="$1"
 
 cd docs
 make html
 cd -
 
-mkdir -p $DOCS_DIR
-mv -n docs/_build/html/* $DOCS_DIR
+mkdir -p "$DOCS_DIR"
+mv -n docs/_build/html/* "$DOCS_DIR"
 
 echo "Moved docs to to: $(pwd)/$DOCS_DIR"
index 5521dad..3f6dee2 100644 (file)
@@ -238,10 +238,10 @@ latex_documents = [
 # (source start file, name, description, authors, manual section).
 man_pages = [
     ('man/snf-image-creator', 'snf-image-creator',
-     'Command line image creator for Synnefo',
+     'Dialog-based image creator for Synnefo',
      'Synnefo development team <synnefo-devel@googlegroups.com>', 1),
     ('man/snf-mkimage', 'snf-mkimage',
-     'Dialog-based image creator for Synnefo',
+     'Command line image creator for Synnefo',
      'Synnefo development team <synnefo-devel@googlegroups.com>', 1)
 ]
 
index 9d38859..cd1704c 100644 (file)
@@ -6,69 +6,22 @@ snf-image-creator manual page
 Synopsis
 --------
 
-**snf-image-creator** [OPTION] <INPUT MEDIA>
+**snf-image-creator** [OPTION] [<INPUT MEDIA>]
 
 Description
 -----------
 Create image out of an <INPUT MEDIA>. The <INPUT MEDIA> may be a block device,
 a regular file that represents a hard disk or \`/' to bundle the host system
-itself.
+itself. If the <INPUT MEDIA> argument is missing, the user will be asked during
+the program initializaton to specify one.
 
 Options
 -------
--a URL, --authentication-url=URL
-       use this authentication URL when uploading/registering images
-
--c CLOUD, --cloud=CLOUD
-        use this saved cloud account to authenticate against a cloud when
-        uploading/registering images
-
---disable-sysprep=SYSPREP
-       prevent SYSPREP operation from running on the input media
-
---enable-sysprep=SYSPREP
-       run SYSPREP operation on the input media
-
--f, --force
-       overwrite output files if they exist
-
+--version
+       show program's version number and exit
 -h, --help
        show this help message and exit
-
--m KEY=VALUE, --metadata=KEY=VALUE
-       add custom KEY=VALUE metadata to the image
-
---no-shrink
-       don't shrink any partition
-
---no-sysprep
-       don't perform any system preparation operation
-
--o FILE, --outfile=FILE
-       dump image to FILE
-
---public
-       register image with the storage service as public
-
---print-sysprep
-       print the enabled and disabled system preparation operations for this
-       input media
-
--r IMAGENAME, --register=IMAGENAME
-       register the image with the compute service with name IMAGENAME
-
--s, --silent
-       output only errors
-
--t TOKEN, --token=TOKEN
-       use this token when uploading/registering images
-
+-l FILE, --logfile=FILE
+       log all messages to FILE
 --tmpdir=DIR
        create large temporary image files under DIR
-
--u FILENAME, --upload=FILENAME
-       save the image to the storage service with remote name FILENAME
-
---version
-       show program's version number and exit
-
index 87bbf10..3d7e6cf 100644 (file)
@@ -6,22 +6,78 @@ snf-mkimage manual page
 Synopsis
 --------
 
-**snf-mkimage** [OPTION] [<INPUT MEDIA>]
+**snf-mkimage** [OPTION] <INPUT MEDIA>
 
 Description
 -----------
 Create image out of an <INPUT MEDIA>. The <INPUT MEDIA> may be a block device,
 a regular file that represents a hard disk or \`/' to bundle the host system
-itself. If the <INPUT MEDIA> argument is missing, the user will be asked during
-the program initializaton to specify one.
+itself.
 
 Options
 -------
---version
-       show program's version number and exit
+-a URL, --authentication-url=URL
+       use this authentication URL when uploading/registering images
+
+--allow-unsupported
+       Proceed with the image creation even if the media is not supported
+
+-c CLOUD, --cloud=CLOUD
+        use this saved cloud account to authenticate against a cloud when
+        uploading/registering images
+
+--disable-sysprep=SYSPREP
+       prevent SYSPREP operation from running on the input media
+
+--enable-sysprep=SYSPREP
+       run SYSPREP operation on the input media
+
+-f, --force
+       overwrite output files if they exist
+
 -h, --help
        show this help message and exit
--l FILE, --logfile=FILE
-       log all messages to FILE
+
+-m KEY=VALUE, --metadata=KEY=VALUE
+       add custom KEY=VALUE metadata to the image
+
+--no-shrink
+       don't shrink any partition
+
+--no-sysprep
+       don't perform any system preparation operation
+
+-o FILE, --outfile=FILE
+       dump image to FILE
+
+--public
+       register image with the storage service as public
+
+--print-syspreps
+       print the enabled and disabled system preparation operations for this
+       input media
+
+--print-sysprep-params
+       print the needed sysprep parameters for this input media
+
+-r IMAGENAME, --register=IMAGENAME
+       register the image with the compute service with name IMAGENAME
+
+-s, --silent
+       output only errors
+
+--sysprep-param=SYSPREP_PARAMS
+       add KEY=VALUE system preparation parameter
+
+-t TOKEN, --token=TOKEN
+       use this token when uploading/registering images
+
 --tmpdir=DIR
        create large temporary image files under DIR
+
+-u FILENAME, --upload=FILENAME
+       save the image to the storage service with remote name FILENAME
+
+--version
+       show program's version number and exit
+
index fc99172..6e436a7 100644 (file)
@@ -3,8 +3,8 @@ Usage
 
 snf-image-creator comes in 2 variants:
 
- * snf-image-creator: A non-interactive command line program
- * snf-mkimage: A user-friendly dialog-based program
+ * snf-mkimage: A non-interactive command line program
+ * snf-image-creator: A user-friendly dialog-based program
 
 Both expect the input media as first argument. The input media may be a local
 file, a block device or *"/"* if you want to create an image out of the running
@@ -13,12 +13,12 @@ system (see `host bundling operation`_).
 Non-interactive version
 =======================
 
-snf-image-creator receives the following options:
+snf-mkimage receives the following options:
 
 .. code-block:: console
 
-  $ snf-image-creator --help
-  Usage: snf-image-creator [options] <input_media>
+  $ snf-mkimage --help
+  Usage: snf-mkimage [options] <input_media>
 
   Options:
     --version             show program's version number and exit
@@ -70,7 +70,7 @@ registered as *private*. Only the user that registers the image can create
 VM's out of it. If you want the image to be visible by other user too, use the
 *--public* option.
 
-By default, before extracting the image, snf-image-creator will perform a
+By default, before extracting the image, snf-mkimage will perform a
 number of system preparation operations on the snapshot of the media and will
 shrink the last partition found. Both actions can be disabled by specifying
 *--no-sysprep* and *--no-shrink* respectively.
@@ -81,15 +81,15 @@ input media. The user can enable or disable specific *syspreps*, using
 *-{enable,disable}-sysprep* options. The user may specify those options
 multiple times.
 
-Running *snf-image-creator* with *--print-sysprep* on a raw file that hosts a
+Running *snf-mkimage* with *--print-sysprep* on a raw file that hosts a
 debian system, we print the following output:
 
 .. _sysprep:
 
 .. code-block:: console
 
-   $ snf-image-creator --print-sysprep ubuntu.raw
-   snf-image-creator 0.3
+   $ snf-mkimage --print-syspreps ubuntu.raw
+   snf-image-creator 0.6
    =====================
    Examining source media `ubuntu_hd.raw' ... looks like an image file
    Snapshotting media source ... done
@@ -150,17 +150,17 @@ removed, you should use *--enable-sysprep* option like this:
 
 .. code-block:: console
 
-   $ snf-image-creator --enable-sysprep cleanup-mail --enable-sysprep remove-user-accounts ...
+   $ snf-mkimage --enable-sysprep cleanup-mail --enable-sysprep remove-user-accounts ...
 
 Dialog-based version
 ====================
 
-*snf-mkimage* receives the following options:
+*snf-image-creator* receives the following options:
 
 .. code-block:: console
 
- $ snf-mkimage --help
- Usage: snf-mkimage [options] [<input_media>]
+ $ snf-image-creator --help
+ Usage: snf-image-creator [options] [<input_media>]
 
  Options:
    --version             show program's version number and exit
@@ -179,12 +179,12 @@ button to create an image out of the running system (see
 `Host bundling operation`_).
 
 After the input media is examined and the program is initialized, the user will
-be given the choice to run *snf-mkimage* in *wizard* or *expert* mode.
+be given the choice to run *snf-image-creator* in *wizard* or *expert* mode.
 
 Wizard mode
 -----------
 
-When *snf-mkimage* runs in *wizard* mode, the user is just asked to provide the
+When *snf-image-creator* runs in *wizard* mode, the user is just asked to provide the
 following basic information:
 
  * Cloud: The cloud account to use to upload and register the resulting image
@@ -278,13 +278,13 @@ You will be able to boot your installed OS and make any changes you want
 
    $ sudo kvm -m 1G -boot c -drive file=ubuntu.raw,format=raw,cache=none,if=virtio
 
-After you're done, you may use *snf-mkimage* as root to create and upload the
+After you're done, you may use *snf-image-creator* as root to create and upload the
 image:
 
 .. code-block:: console
 
    $ sudo -s
-   $ snf-mkimage ubuntu.raw
+   $ snf-image-creator ubuntu.raw
 
 In the first screen you will be asked to choose if you want to run the program
 in *Wizard* or *Expert* mode. Choose *Wizard*.
index 7895287..2a56a1c 100644 (file)
@@ -101,6 +101,25 @@ def create_image(d, media, out, tmp):
                    "image": image,
                    "metadata": metadata}
 
+        if image.is_unsupported():
+
+            session['excluded_tasks'] = [-1]
+            session['task_metadata'] = ["EXCLUDE_ALL_TASKS"]
+
+            msg = "The system on the input media is not supported." \
+                "\n\nReason: %s\n\n" \
+                "We highly recommend not to create an image out of this, " \
+                "since the image won't be cleaned up and you will not be " \
+                "able to configure it during the deployment. Press <YES> if " \
+                "you still want to continue with the image creation process." \
+                % image._unsupported
+
+            if not d.yesno(msg, width=WIDTH, defaultno=1, height=12):
+                main_menu(session)
+
+            d.infobox("Thank you for using snf-image-creator. Bye", width=53)
+            return 0
+
         msg = "snf-image-creator detected a %s system on the input media. " \
               "Would you like to run a wizard to assist you through the " \
               "image creation process?\n\nChoose <Wizard> to run the wizard," \
index 25fcb6c..4f24b1e 100644 (file)
@@ -217,13 +217,26 @@ def register_image(session):
                  "register it", width=SMALL_WIDTH)
         return False
 
+    name = ""
+    description = session['metadata']['DESCRIPTION'] if 'DESCRIPTION' in \
+        session['metadata'] else ""
+
     while 1:
-        (code, answer) = d.inputbox("Please provide a registration name:",
-                                    width=WIDTH)
+        fields = [
+            ("Registration name:", name, 60),
+            ("Description (optional):", description, 80)]
+
+        (code, output) = d.form(
+            "Please provide the following registration info:", height=11,
+            width=WIDTH, form_height=2, fields=fields)
+
         if code in (d.DIALOG_CANCEL, d.DIALOG_ESC):
             return False
 
-        name = answer.strip()
+        name, description = output
+        name = name.strip()
+        description = description.strip()
+
         if len(name) == 0:
             d.msgbox("Registration name cannot be empty", width=SMALL_WIDTH)
             continue
@@ -238,6 +251,7 @@ def register_image(session):
 
         break
 
+    session['metadata']['DESCRIPTION'] = description
     metadata = {}
     metadata.update(session['metadata'])
     if 'task_metadata' in session:
@@ -583,6 +597,12 @@ def delete_properties(session):
 def exclude_tasks(session):
     """Exclude specific tasks from running during image deployment"""
     d = session['dialog']
+    image = session['image']
+
+    if image.is_unsupported():
+        d.msgbox("Image deployment configuration is disabled for unsupported "
+                 "images.", width=SMALL_WIDTH)
+        return False
 
     index = 0
     displayed_index = 1
index 7eb8ad8..aebd2c9 100644 (file)
@@ -88,16 +88,26 @@ class Image(object):
 
         self.out.output('Inspecting Operating System ...', False)
         roots = self.g.inspect_os()
-        if len(roots) == 0:
-            raise FatalError("No operating system found")
-        if len(roots) > 1:
-            raise FatalError("Multiple operating systems found."
-                             "We only support images with one OS.")
+
+        if len(roots) == 0 or len(roots) > 1:
+            self.root = None
+            self.ostype = "unsupported"
+            self.distro = "unsupported"
+            self.guestfs_device = '/dev/sda'
+            self.size = self.g.blockdev_getsize64(self.guestfs_device)
+
+            if len(roots) > 1:
+                reason = "Multiple operating systems found on the media."
+            else:
+                reason = "Unable to detect any operating system on the media."
+
+            self.set_unsupported(reason)
+            return
+
         self.root = roots[0]
-        self.guestfs_device = self.g.part_to_dev(self.root)
+        self.meta['PARTITION_TABLE'] = self.g.part_get_parttype('/dev/sda')
+        self.guestfs_device = '/dev/sda'  # self.g.part_to_dev(self.root)
         self.size = self.g.blockdev_getsize64(self.guestfs_device)
-        self.meta['PARTITION_TABLE'] = \
-            self.g.part_get_parttype(self.guestfs_device)
 
         self.ostype = self.g.inspect_get_type(self.root)
         self.distro = self.g.inspect_get_distro(self.root)
@@ -105,6 +115,20 @@ class Image(object):
             'found a(n) %s system' %
             self.ostype if self.distro == "unknown" else self.distro)
 
+        # Inspect the OS
+        self.os.inspect()
+
+    def set_unsupported(self, reason):
+        """Flag this image us ansupported"""
+
+        self._unsupported = reason
+        self.meta['UNSUPPORTED'] = reason
+        self.out.warn('Media is not supported. Reason: %s' % reason)
+
+    def is_unsupported(self):
+        """Returns if this image is unsupported"""
+        return hasattr(self, '_unsupported')
+
     def enable_guestfs(self):
         """Enable the guestfs handler"""
 
@@ -264,6 +288,10 @@ class Image(object):
 
         self.out.output("Shrinking image (this may take a while) ...", False)
 
+        if self.is_unsupported():
+            self.out.warn("Shrinking is disabled for unsupported images")
+            return self.size
+
         sector_size = self.g.blockdev_getss(self.guestfs_device)
 
         last_part = None
index c328146..ccf5125 100644 (file)
@@ -50,6 +50,7 @@ import optparse
 import StringIO
 import signal
 import json
+import textwrap
 
 
 def check_writable_dir(option, opt_str, value, parser):
@@ -140,6 +141,10 @@ def parse_options(input_args):
                       help="register image with the cloud as public",
                       action="store_true")
 
+    parser.add_option("--allow-unsupported", dest="allow_unsupported",
+                      help="Proceed with the image creation even if the media "
+                      "is not supported", default=False, action="store_true")
+
     parser.add_option("--tmpdir", dest="tmp", type="string", default=None,
                       help="create large temporary image files under DIR",
                       metavar="DIR")
@@ -273,6 +278,16 @@ def image_creator():
 
         image = disk.get_image(snapshot, sysprep_params=options.sysprep_params)
 
+        if image.is_unsupported() and not options.allow_unsupported:
+            raise FatalError(
+                "The media seems to be unsupported.\n\n" +
+                textwrap.fill("To create an image from an unsupported media, "
+                              "you'll need to use the`--allow-unsupported' "
+                              "command line option. Using this is highly "
+                              "discouraged, since the resulting image will "
+                              "not be cleared out of sensitive data and will "
+                              "not get customized during the deployment."))
+
         for sysprep in options.disabled_syspreps:
             image.os.disable_sysprep(image.os.get_sysprep_by_name(sysprep))
 
@@ -298,6 +313,9 @@ def image_creator():
         size = options.shrink and image.shrink() or image.size
         metadata.update(image.meta)
 
+        if image.is_unsupported():
+            metadata['EXCLUDE_ALL_TASKS'] = "yes"
+
         # Add command line metadata to the collected ones...
         metadata.update(options.metadata)
 
@@ -384,6 +402,10 @@ def main():
         sys.exit(ret)
     except FatalError as e:
         colored = sys.stderr.isatty()
+        warning = \
+            "The name of the executable has changed. If you want to use the " \
+            "user-friendly dialog-based program try `snf-image-creator'"
+        SimpleOutput(colored).warn(warning)
         SimpleOutput(colored).error(e)
         sys.exit(1)
 
index 5298569..ce84130 100644 (file)
@@ -144,6 +144,22 @@ class OSBase(object):
         except RuntimeError:
             self._scrub_support = False
 
+    def inspect(self):
+        """Inspect the media to check if it is supported"""
+
+        if self.image.is_unsupported():
+            return
+
+        self.out.output('Running OS inspection:')
+        try:
+            if not self.mount(readonly=True):
+                raise FatalError("Unable to mount the media read-only")
+            self._do_inspect()
+        finally:
+            self.umount()
+
+        self.out.output()
+
     def collect_metadata(self):
         """Collect metadata about the OS"""
         try:
@@ -239,7 +255,7 @@ class OSBase(object):
             return
 
         for name, param in self.needed_sysprep_params.items():
-            self.out.output("\t%s (%s): %s" %
+            self.out.output("\t%s [%s]: %s" %
                             (param.description, name,
                              self.sysprep_params[name] if name in
                              self.sysprep_params else "(none)"))
@@ -247,12 +263,17 @@ class OSBase(object):
     def do_sysprep(self):
         """Prepare system for image creation."""
 
+        self.out.output('Preparing system for image creation:')
+
+        if self.image.is_unsupported():
+            self.out.warn(
+                "System preparation is disabled for unsupported media")
+            return
+
         try:
             if not self.mount(readonly=False):
                 raise FatalError("Unable to mount the media read-write")
 
-            self.out.output('Preparing system for image creation:')
-
             enabled = [task for task in self.list_syspreps() if task.enabled]
 
             size = len(enabled)
@@ -351,10 +372,20 @@ class OSBase(object):
             if has_ftype(f, ftype):
                 action(full_path)
 
+    def _do_inspect(self):
+        """helper method for inspect"""
+        pass
+
     def _do_collect_metadata(self):
         """helper method for collect_metadata"""
-        self.meta['ROOT_PARTITION'] = \
-            "%d" % self.image.g.part_to_partnum(self.root)
+
+        try:
+            self.meta['ROOT_PARTITION'] = \
+                "%d" % self.image.g.part_to_partnum(self.root)
+        except RuntimeError:
+            self.out.warn("Unable to identify the partition number from root "
+                          "partition: %s" % self.root)
+
         self.meta['OSFAMILY'] = self.image.g.inspect_get_type(self.root)
         self.meta['OS'] = self.image.g.inspect_get_distro(self.root)
         if self.meta['OS'] == "unknown":
index 5f81481..145be3e 100644 (file)
@@ -82,6 +82,18 @@ class Freebsd(Unix):
             self.out.warn("No passworded users found!")
             del self.meta['USERS']
 
+    def _do_inspect(self):
+        """Run various diagnostics to check if media is supported"""
+
+        self.out.output('Checking partition table type...', False)
+        ptype = self.image.g.part_get_parttype(self.image.guestfs_device)
+        if ptype != 'gpt':
+            self.out.warn("partition table type is: `%s'" % ptype)
+            self.image.set_unsupported(
+                'On FreeBSD only GUID partition tables are supported')
+        else:
+            self.out.success(ptype)
+
     def _get_passworded_users(self):
         """Returns a list of non-locked user accounts"""
         users = []
index 31b660f..53e8a5a 100644 (file)
@@ -116,6 +116,9 @@ class Linux(Unix):
 
         self.image.g.write('/etc/shadow', "\n".join(shadow) + '\n')
 
+        # Remove backup file for /etc/shadow
+        self.image.g.rm_rf('/etc/shadow-')
+
     @sysprep('Fixing acpid powerdown action')
     def fix_acpid(self):
         """Replace acpid powerdown action scripts to immediately shutdown the
@@ -298,6 +301,20 @@ class Linux(Unix):
 
         return orig, dev, mpoint
 
+    def _do_inspect(self):
+        """Run various diagnostics to check if media is supported"""
+
+        self.out.output(
+            'Checking if the media contains logical volumes (LVM)...', False)
+
+        has_lvm = True if len(self.image.g.lvs()) else False
+
+        if has_lvm:
+            self.out.output()
+            self.image.set_unsupported('The media contains logical volumes')
+        else:
+            self.out.success('no')
+
     def _do_collect_metadata(self):
         """Collect metadata about the OS"""
         super(Linux, self)._do_collect_metadata()
diff --git a/image_creator/os_type/unsupported.py b/image_creator/os_type/unsupported.py
new file mode 100644 (file)
index 0000000..9b27b7b
--- /dev/null
@@ -0,0 +1,55 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright 2013 GRNET S.A. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or
+# without modification, are permitted provided that the following
+# conditions are met:
+#
+#   1. Redistributions of source code must retain the above
+#      copyright notice, this list of conditions and the following
+#      disclaimer.
+#
+#   2. Redistributions in binary form must reproduce the above
+#      copyright notice, this list of conditions and the following
+#      disclaimer in the documentation and/or other materials
+#      provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
+# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+# The views and conclusions contained in the software and
+# documentation are those of the authors and should not be
+# interpreted as representing official policies, either expressed
+# or implied, of GRNET S.A.
+
+"""This module hosts code to handle unknown OSs."""
+
+from image_creator.os_type import OSBase
+
+
+class Unsupported(OSBase):
+    """OS class for unsupported OSs"""
+    def __init__(self, image, **kargs):
+        super(Unsupported, self).__init__(image, **kargs)
+
+    def collect_metadata(self):
+        """Collect metadata about the OS"""
+        self.out.warn("Unable to collect metadata for unsupported media")
+
+    def _do_mount(self, readonly):
+        """Mount partitions in corrent order"""
+        self.out.warn('not supported on this media.')
+        return False
+
+# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :
index 96efdab..64757a6 100644 (file)
@@ -49,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",
@@ -111,6 +119,10 @@ 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)
@@ -131,6 +143,11 @@ class Windows(OSBase):
             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']
@@ -194,7 +211,7 @@ class Windows(OSBase):
         self._guest_exec(
             r"cmd /q /c for /f %l in ('wevtutil el') do wevtutil cl %l")
 
-    @sysprep('Executing Sysprep on the image (may take more that 10 minutes)')
+    @sysprep('Executing Sysprep on the image (may take more that 10 min)')
     def microsoft_sysprep(self):
         """Run the Microsoft System Preparation Tool. This will remove
         system-specific data and will make the image ready to be deployed.
@@ -248,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! "
@@ -265,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)
@@ -329,7 +351,7 @@ class Windows(OSBase):
             monitorfd, monitor = tempfile.mkstemp()
             os.close(monitorfd)
             vm = _VM(self.image.device, monitor, self.sysprep_params)
-            self.out.success("started (console on vnc display: %d)." %
+            self.out.success("started (console on VNC display: %d)" %
                              vm.display)
 
             self.out.output("Waiting for OS to boot ...", False)
@@ -782,8 +804,8 @@ class _VM(object):
         args.extend(needed_args)
 
         args.extend([
-            '-smp', '1', '-m', '1024', '-drive',
-            'file=%s,format=raw,cache=unsafe,if=virtio' % self.disk,
+            '-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,
index f2b10e8..a82b986 100644 (file)
@@ -131,7 +131,9 @@ class InfoBoxOutput(Output):
         self.msg += "%s%s" % (msg, nl)
         # If output is long, only output the last lines that fit in the box
         lines = self.msg.splitlines()
-        h = self.height
+        # The height of the active region is 2 lines shorter that the height of
+        # the dialog
+        h = self.height - 2
         display = self.msg if len(lines) <= h else "\n".join(lines[-h:])
         self.d.infobox(display, title=self.title, height=self.height,
                        width=self.width)
index 351ff30..8d88873 100644 (file)
@@ -1,8 +1,8 @@
 
-__version__ = "0.5.3"
+__version__ = "0.5.3next"
 __version_vcs_info__ = {
-    'branch': 'hotfix-0.5.3',
-    'revid': 'dc0ecb2',
+    'branch': 'develop',
+    'revid': '2a62304',
     'revno': 402}
 __version_user_email__ = "skalkoto@grnet.gr"
 __version_user_name__ = "Nikos Skalkotos"
index ebac409..b6c00bc 100644 (file)
@@ -38,6 +38,7 @@
 import subprocess
 import time
 import signal
+import distutils
 
 from image_creator.util import FatalError
 
@@ -50,6 +51,10 @@ class WinexeTimeout(FatalError):
 class WinEXE:
     """Wrapper class for the winexe command"""
 
+    @staticmethod
+    def is_installed(program='winexe'):
+        return distutils.spawn.find_executable(program) is not None
+
     def __init__(self, username, password, hostname, program='winexe'):
         self._host = hostname
         self._user = username
index 3c67366..3a6dd27 100755 (executable)
--- a/setup.py
+++ b/setup.py
@@ -52,8 +52,8 @@ setup(
     install_requires=['sh', 'ansicolors', 'progress>=1.0.2'],
     entry_points={
         'console_scripts': [
-                'snf-image-creator = image_creator.main:main',
-                'snf-mkimage = image_creator.dialog_main:main']
+                'snf-mkimage = image_creator.main:main',
+                'snf-image-creator = image_creator.dialog_main:main']
     }
 )
 # vim: set sta sts=4 shiftwidth=4 sw=4 et ai :
diff --git a/version b/version
index be14282..3f4f53f 100644 (file)
--- a/version
+++ b/version
@@ -1 +1 @@
-0.5.3
+0.5.3next