From: Nikos Skalkotos Date: Wed, 3 Jul 2013 13:28:19 +0000 (+0300) Subject: Add support for sysprep-parameters X-Git-Tag: 0.5~1^2~13^2~28 X-Git-Url: https://code.grnet.gr/git/snf-image-creator/commitdiff_plain/0eac025658a5fe7a995ad3f8a12930aa19758f49 Add support for sysprep-parameters The user may define parameters needed by the os_type classes to perform the system preparation tasks. A new needed_sysprep_params method is added to os_type.OSBase that returns a list with parameters that should be defined by the user. --- diff --git a/image_creator/dialog_menu.py b/image_creator/dialog_menu.py index a6228db..9bc5c40 100644 --- a/image_creator/dialog_menu.py +++ b/image_creator/dialog_menu.py @@ -619,6 +619,41 @@ def exclude_tasks(session): return True +def sysprep_params(session): + + d = session['dialog'] + image = session['image'] + + available = image.os.sysprep_params + needed = image.os.needed_sysprep_params() + + if len(needed) == 0: + return True + + fields = [] + for param in needed: + default = available[param.name] if param.name in available else "" + fields.append(("%s: " % param.description, default, param.length)) + + txt = "Please provide the following system preparation parameters:" + code, output = d.form(txt, height=13, width=WIDTH, form_height=len(fields), + fields=fields) + + if code in (d.DIALOG_CANCEL, d.DIALOG_ESC): + return False + + sysprep_params = {} + for i in range(len(fields)): + if needed[i].validator(output[i]): + image.os.sysprep_params[needed[i].name] = output[i] + else: + d.msgbox("The value you provided for parameter: %s is not valid" % + name, width=SMALL_WIDTH) + return False + + return True + + def sysprep(session): """Perform various system preperation tasks on the image""" d = session['dialog'] @@ -683,6 +718,9 @@ def sysprep(session): title="System Preperation", width=SMALL_WIDTH) continue + if not sysprep_params(session): + continue + infobox = InfoBoxOutput(d, "Image Configuration") try: image.out.add(infobox) diff --git a/image_creator/dialog_wizard.py b/image_creator/dialog_wizard.py index 8abcfed..e5a39f8 100644 --- a/image_creator/dialog_wizard.py +++ b/image_creator/dialog_wizard.py @@ -208,6 +208,36 @@ class WizardInputPage(WizardPage): return self.NEXT +class WizardFormPage(WizardPage): + """Represents a Form in a wizard""" + + def __init__(self, name, display_name, text, fields, **kargs): + super(WizardFormPage, self).__init__(name, display_name, text, **kargs) + self.fields = fields + + def run(self, session, title): + d = session['dialog'] + w = session['wizard'] + + field_lenght = len(self.fields()) + form_height = field_lenght if field_lenght < PAGE_HEIGHT - 4 \ + else PAGET_HEIGHT - 4 + + (code, output) = d.form( + self.text, width=PAGE_WIDTH, height=PAGE_HEIGHT, + form_height=form_height, ok_label="Next", cancel="Back", + fields=self.fields(), title=title) + + if code in (d.DIALOG_CANCEL, d.DIALOG_ESC): + return self.PREV + + w[self.name] = self.validate(output) + self.default = output + self.info = "%s: %s" % (self.display_name, self.display(w[self.name])) + + return self.NEXT + + class WizardMenuPage(WizardPageWthChoices): """Represents a menu dialog with available choices in a wizard""" @@ -250,9 +280,11 @@ class WizardMenuPage(WizardPageWthChoices): def start_wizard(session): """Run the image creation wizard""" - distro = session['image'].distro - ostype = session['image'].ostype + image = session['image'] + distro = image.distro + ostype = image.ostype + # Create Cloud Wizard Page def cloud_choices(): choices = [] for (name, cloud) in Kamaki.get_clouds().items(): @@ -279,7 +311,7 @@ def start_wizard(session): if edit_cloud(session, cloud): return cloud - raise WizardInvalidData + raise WizardReloadPage return cloud @@ -289,16 +321,52 @@ def start_wizard(session): choices=cloud_choices, extra_label="Add", extra=cloud_add, title="Clouds", validate=cloud_validate, fallback=cloud_none_available) + # Create Image Name Wizard Page name = WizardInputPage( "ImageName", "Image Name", "Please provide a name for the image:", title="Image Name", default=ostype if distro == "unknown" else distro) + # Create Image Description Wizard Page descr = WizardInputPage( "ImageDescription", "Image Description", "Please provide a description for the image:", title="Image Description", default=session['metadata']['DESCRIPTION'] if 'DESCRIPTION' in session['metadata'] else '') + # Create Sysprep Params Wizard Page + needed = image.os.needed_sysprep_params() + + def sysprep_params_fields(): + fields = [] + available = image.os.sysprep_params + for param in needed: + text = param.description + default = available[param.name] if param.name in available else "" + fields.append(("%s: " % text, default, param.length)) + return fields + + def sysprep_params_validate(answer): + params = {} + for i in range(len(answer)): + if needed[i].validator(answer): + params[needed[i].name] = answer[i] + else: + session['dialog'].msgbox("Invalid value for parameter `%s'" % + needed[i].name) + raise WizardReloadPage + return params + + def sysprep_params_display(params): + return ",".join(["%s=%s" % (key, val) for key, val in params.items()]) + + sysprep_params = WizardFormPage( + "SysprepParams", "Sysprep Parameters", + "Prease fill in the following system preparation parameters:", + title="System Preparation Parameters", fields=sysprep_params_fields, + display=sysprep_params_display, validate=sysprep_params_validate + ) if len(needed) != 0 else None + + # Create Image Registration Wizard Page def registration_choices(): return [("Private", "Image is accessible only by this user"), ("Public", "Everyone can create VMs from this image")] @@ -313,6 +381,8 @@ def start_wizard(session): w.add_page(cloud) w.add_page(name) w.add_page(descr) + if sysprep_params is not None: + w.add_page(sysprep_params) w.add_page(registration) if w.run(): @@ -336,6 +406,7 @@ def create_image(session): out.clear() #Sysprep + image.os.sysprep_params.update(wizard['SysprepParams']) image.os.do_sysprep() metadata = image.os.meta diff --git a/image_creator/disk.py b/image_creator/disk.py index 5806d6f..e4b6bc8 100644 --- a/image_creator/disk.py +++ b/image_creator/disk.py @@ -187,10 +187,10 @@ class Disk(object): self.out.success('done') return "/dev/mapper/%s" % snapshot - def get_image(self, media): + def get_image(self, media, **kargs): """Returns a newly created Image instance.""" - image = Image(media, self.out) + image = Image(media, self.out, **kargs) self._images.append(image) image.enable() return image diff --git a/image_creator/image.py b/image_creator/image.py index ccc4f80..2ec52a6 100644 --- a/image_creator/image.py +++ b/image_creator/image.py @@ -45,12 +45,16 @@ from sendfile import sendfile class Image(object): """The instances of this class can create images out of block devices.""" - def __init__(self, device, output, meta={}): + def __init__(self, device, output, **kargs): """Create a new Image instance""" self.device = device self.out = output - self.meta = meta + + self.meta = kargs['meta'] if 'meta' in kargs else {} + self.sysprep_params = \ + kargs['sysprep_params'] if 'sysprep_params' in kargs else {} + self.progress_bar = None self.guestfs_device = None self.size = 0 @@ -117,7 +121,7 @@ class Image(object): self.enable() cls = os_cls(self.distro, self.ostype) - self._os = cls(self) + self._os = cls(self, sysprep_params=self.sysprep_params) self._os.collect_metadata() diff --git a/image_creator/main.py b/image_creator/main.py index bff95d9..edee4fe 100644 --- a/image_creator/main.py +++ b/image_creator/main.py @@ -121,6 +121,10 @@ def parse_options(input_args): "input media", default=[], action="append", metavar="SYSPREP") + parser.add_option("--sysprep-param", dest="sysprep_params", default=[], + help="Add KEY=VALUE system preparation parameter", + action="append") + parser.add_option("--no-sysprep", dest="sysprep", default=True, help="don't perform any system preparation operation", action="store_false") @@ -170,6 +174,16 @@ def parse_options(input_args): meta[key] = value options.metadata = meta + sysprep_params = {} + for p in options.sysprep_params: + try: + key, value = p.split('=', 1) + except ValueError: + raise FatalError("Sysprep parameter optiont: `%s' is not in " + "KEY=VALUE format." % p) + sysprep_params[key] = value + options.sysprep_params = sysprep_params + return options @@ -253,7 +267,7 @@ def image_creator(): try: snapshot = disk.snapshot() - image = disk.get_image(snapshot) + image = disk.get_image(snapshot, sysprep_params=options.sysprep_params) for sysprep in options.disabled_syspreps: image.os.disable_sysprep(image.os.get_sysprep_by_name(sysprep)) diff --git a/image_creator/os_type/__init__.py b/image_creator/os_type/__init__.py index 063b410..8501094 100644 --- a/image_creator/os_type/__init__.py +++ b/image_creator/os_type/__init__.py @@ -41,6 +41,7 @@ from image_creator.util import FatalError import textwrap import re +from collections import namedtuple def os_cls(distro, osfamily): @@ -79,13 +80,19 @@ def sysprep(enabled=True): class OSBase(object): """Basic operating system class""" - def __init__(self, image): + SysprepParam = namedtuple('SysprepParam', + 'name description length validator') + + def __init__(self, image, **kargs): self.image = image self.root = image.root self.g = image.g self.out = image.out + self.sysprep_params = \ + kargs['sysprep_params'] if 'sysprep_params' in kargs else {} + self.meta = {} def collect_metadata(self): @@ -102,6 +109,12 @@ class OSBase(object): self.out.output() + def needed_sysprep_params(self): + """Returns a list of needed sysprep parameters. Each element in the + list is a SysprepParam object. + """ + return [] + def list_syspreps(self): """Returns a list of sysprep objects""" objs = [getattr(self, name) for name in dir(self) diff --git a/image_creator/os_type/linux.py b/image_creator/os_type/linux.py index a0302d1..cd86c37 100644 --- a/image_creator/os_type/linux.py +++ b/image_creator/os_type/linux.py @@ -43,8 +43,8 @@ import time class Linux(Unix): """OS class for Linux""" - def __init__(self, image): - super(Linux, self).__init__(image) + def __init__(self, image, **kargs): + super(Linux, self).__init__(image, **kargs) self._uuid = dict() self._persistent = re.compile('/dev/[hsv]d[a-z][1-9]*')