diff --git a/image_creator/dialog_main.py b/image_creator/dialog_main.py index b5e2164210903ed045d728d98cedb44bf972efb4..2eb2dc87a8d41a5a4c833d853c043e6e78b36039 100644 --- a/image_creator/dialog_main.py +++ b/image_creator/dialog_main.py @@ -161,7 +161,7 @@ def _dialog_form(self, text, height=20, width=60, form_height=15, fields=[], item = field[1] item_len = field[2] cmd.extend((label, str(line), str(1), item, str(line), - str(label_len + 1), str(input_len), str(item_len))) + str(label_len + 1), str(input_len), str(item_len))) line += 1 code, output = self._perform(*(cmd,), **kwargs) @@ -218,14 +218,14 @@ def dialog_main(media, logfile, tmpdir): except Reset: log.output("Resetting everything ...") continue - except FatalError as e: - msg = textwrap.fill(str(e), width=WIDTH-4) + except FatalError as error: + msg = textwrap.fill(str(error), width=WIDTH-4) d.infobox(msg, width=WIDTH, title="Fatal Error") return 1 def main(): - + """Entrance Point""" if os.geteuid() != 0: sys.stderr.write("Error: You must run %s as root\n" % PROGNAME) sys.exit(2) @@ -252,9 +252,9 @@ def main(): try: logfile = open(opts.logfile, 'w') if opts.logfile is not None else None - except IOError as e: + except IOError as error: parser.error("Unable to open logfile `%s' for writing. Reason: %s" % - (opts.logfile, e.strerror)) + (opts.logfile, error.strerror)) try: # Save the terminal attributes diff --git a/image_creator/dialog_menu.py b/image_creator/dialog_menu.py index 831e583f9104159252ecbfde633d57752333539a..5fb44b3ab9e908c4dbb7603fdb060a292756c8da 100644 --- a/image_creator/dialog_menu.py +++ b/image_creator/dialog_menu.py @@ -37,17 +37,17 @@ from image_creator.dialog_util import SMALL_WIDTH, WIDTH, \ CONFIGURATION_TASKS = [ ("Partition table manipulation", ["FixPartitionTable"], - ["linux", "windows"]), + ["linux", "windows"]), ("File system resize", - ["FilesystemResizeUnmounted", "FilesystemResizeMounted"], - ["linux", "windows"]), + ["FilesystemResizeUnmounted", "FilesystemResizeMounted"], + ["linux", "windows"]), ("Swap partition configuration", ["AddSwap"], ["linux"]), ("SSH keys removal", ["DeleteSSHKeys"], ["linux"]), ("Temporal RDP disabling", ["DisableRemoteDesktopConnections"], - ["windows"]), + ["windows"]), ("SELinux relabeling at next boot", ["SELinuxAutorelabel"], ["linux"]), ("Hostname/Computer Name assignment", ["AssignHostname"], - ["windows", "linux"]), + ["windows", "linux"]), ("Password change", ["ChangePassword"], ["windows", "linux"]), ("File injection", ["EnforcePersonality"], ["windows", "linux"]) ] @@ -60,13 +60,14 @@ class MetadataMonitor(object): def __init__(self, session, meta): self.session = session self.meta = meta + self.old = {} def __enter__(self): self.old = {} for (k, v) in self.meta.items(): self.old[k] = v - def __exit__(self, type, value, traceback): + def __exit__(self, exc_type, exc_val, exc_tb): d = self.session['dialog'] altered = {} @@ -102,8 +103,6 @@ def upload_image(session): """Upload the image to the storage service""" d = session["dialog"] image = session['image'] - meta = session['metadata'] - size = image.size if "account" not in session: d.msgbox("You need to select a valid cloud before you can upload " @@ -113,8 +112,8 @@ def upload_image(session): while 1: if 'upload' in session: init = session['upload'] - elif 'OS' in meta: - init = "%s.diskdump" % meta['OS'] + elif 'OS' in session['metadata']: + init = "%s.diskdump" % session['metadata']['OS'] else: init = "" (code, answer) = d.inputbox("Please provide a filename:", init=init, @@ -151,13 +150,13 @@ def upload_image(session): try: if 'checksum' not in session: md5 = MD5(out) - session['checksum'] = md5.compute(image.device, size) + session['checksum'] = md5.compute(image.device, image.size) try: # Upload image file with open(image.device, 'rb') as f: session["pithos_uri"] = \ - kamaki.upload(f, size, filename, + kamaki.upload(f, image.size, filename, "Calculating block hashes", "Uploading missing blocks") # Upload md5sum file @@ -206,9 +205,8 @@ def register_image(session): session['metadata'] else "" while 1: - fields = [ - ("Registration name:", name, 60), - ("Description (optional):", description, 80)] + fields = [("Registration name:", name, 60), + ("Description (optional):", description, 80)] (code, output) = d.form( "Please provide the following registration info:", height=11, @@ -225,14 +223,13 @@ def register_image(session): d.msgbox("Registration name cannot be empty", width=SMALL_WIDTH) continue - ret = d.yesno("Make the image public?\\nA public image is accessible " - "by every user of the service.", defaultno=1, - width=WIDTH) - if ret not in (0, 1): + answer = d.yesno("Make the image public?\\nA public image is " + "accessible by every user of the service.", + defaultno=1, width=WIDTH) + if answer not in (0, 1): continue - is_public = True if ret == 0 else False - + is_public = (answer == 0) break session['metadata']['DESCRIPTION'] = description @@ -267,8 +264,8 @@ def register_image(session): kamaki.share("%s.meta" % session['upload']) kamaki.share("%s.md5sum" % session['upload']) out.success('done') - except ClientError as e: - d.msgbox("Error in storage service client: %s" % e.message) + except ClientError as error: + d.msgbox("Error in storage service client: %s" % error.message) return False finally: out.remove(gauge) @@ -336,8 +333,8 @@ def delete_clouds(session): d.msgbox("Nothing selected!", width=SMALL_WIDTH) return False - if not d.yesno("Are you sure you want to remove the selected cloud " - "accounts?", width=WIDTH, defaultno=1): + if not d.yesno("Are you sure you want to remove the selected accounts?", + width=WIDTH, defaultno=1): for i in to_delete: Kamaki.remove_cloud(i) if 'cloud' in session and session['cloud'] == i: @@ -602,13 +599,14 @@ def exclude_tasks(session): index += 1 while 1: + text = "Please choose which configuration tasks you would like to " \ + "prevent from running during image deployment. " \ + "Press <No Config> to suppress any configuration. " \ + "Press <Help> for more help on the image deployment " \ + "configuration tasks." + (code, tags) = d.checklist( - text="Please choose which configuration tasks you would like to " - "prevent from running during image deployment. " - "Press <No Config> to suppress any configuration. " - "Press <Help> for more help on the image deployment " - "configuration tasks.", - choices=choices, height=19, list_height=8, width=WIDTH, + text=text, choices=choices, height=19, list_height=8, width=WIDTH, help_button=1, extra_button=1, extra_label="No Config", title="Exclude Configuration Tasks") tags = map(lambda x: x.strip('"'), tags) # Needed for OpenSUSE @@ -680,8 +678,7 @@ def sysprep_params(session): if code in (d.DIALOG_CANCEL, d.DIALOG_ESC): return True - # Details button - elif code == d.DIALOG_OK: + elif code == d.DIALOG_OK: # Details button d.msgbox(image.os.sysprep_params[choice].description, width=WIDTH) else: # Update button update_sysprep_param(session, choice) @@ -823,13 +820,13 @@ def sysprep(session): help_title = "System Preparation Tasks" sysprep_help = "%s\n%s\n\n" % (help_title, '=' * len(help_title)) - for sysprep in syspreps: - name, descr = image.os.sysprep_info(sysprep) + for task in syspreps: + name, descr = image.os.sysprep_info(task) display_name = name.replace('-', ' ').capitalize() sysprep_help += "%s\n" % display_name sysprep_help += "%s\n" % ('-' * len(display_name)) sysprep_help += "%s\n\n" % wrapper.fill(" ".join(descr.split())) - enabled = 1 if sysprep.enabled else 0 + enabled = 1 if task.enabled else 0 choices.append((str(index + 1), display_name, enabled)) index += 1 @@ -875,10 +872,10 @@ def sysprep(session): try: image.os.do_sysprep() infobox.finalize() - except FatalError as e: - title = "System Preparation" - d.msgbox("System Preparation failed: %s" % e, - title=title, width=SMALL_WIDTH) + except FatalError as error: + d.msgbox("System Preparation failed: %s" % error, + title="System Preparation", + width=SMALL_WIDTH) finally: image.out.remove(infobox) finally: @@ -976,13 +973,13 @@ def main_menu(session): actions = {"Customize": customization_menu, "Register": kamaki_menu, "Extract": extract_image} + title = "Image Creator for Synnefo (snf-image-creator v%s)" % version while 1: (code, choice) = d.menu( text="Choose one of the following or press <Exit> to exit.", width=WIDTH, choices=choices, cancel="Exit", height=13, default_item=default_item, menu_height=len(choices), - title="Image Creator for Synnefo (snf-image-creator version %s)" % - version) + title=title) if code in (d.DIALOG_CANCEL, d.DIALOG_ESC): if confirm_exit(d): diff --git a/image_creator/dialog_util.py b/image_creator/dialog_util.py index faef57612b0498199f0ad410ae574fa0d6a5744c..9b659398036b2286a80a87196097ae7df4c41b22 100644 --- a/image_creator/dialog_util.py +++ b/image_creator/dialog_util.py @@ -228,10 +228,10 @@ def extract_image(session): return True -def _check_cloud(session, name, description, url, token): +def _check_cloud(session, name, url, token): """Checks if the provided info for a cloud are valid""" d = session['dialog'] - regexp = re.compile('^[~@#$:\-\w]+$') + regexp = re.compile(r'^[~@#$:\-\w]+$') if not re.match(regexp, name): d.msgbox("Allowed characters for name: a-zA-Z0-9_~@#$:-", width=WIDTH) @@ -283,7 +283,7 @@ def add_cloud(session): url = url.strip() token = token.strip() - if _check_cloud(session, name, description, url, token): + if _check_cloud(session, name, url, token): if name in Kamaki.get_clouds().keys(): d.msgbox("A cloud with name `%s' already exists. If you want " "to edit the existing cloud account, use the edit " @@ -328,7 +328,7 @@ def edit_cloud(session, name): url = url.strip() token = token.strip() - if _check_cloud(session, name, description, url, token): + if _check_cloud(session, name, url, token): Kamaki.save_cloud(name, url, token, description) break diff --git a/image_creator/dialog_wizard.py b/image_creator/dialog_wizard.py index 470eb76fc22b6fe4d36012d972523833664e784e..ee2fcef6489fa914566a6ef3ce833dde628e9955 100644 --- a/image_creator/dialog_wizard.py +++ b/image_creator/dialog_wizard.py @@ -22,6 +22,7 @@ snf-image-creator. import time import StringIO import json +import re from image_creator.kamaki_wrapper import Kamaki, ClientError from image_creator.util import MD5, FatalError @@ -43,83 +44,97 @@ class WizardReloadPage(Exception): pass -class Wizard: +class Wizard(object): """Represents a dialog-based wizard The wizard is a collection of pages that have a "Next" and a "Back" button on them. The pages are used to collect user data. """ - def __init__(self, session): - self.session = session - self.pages = [] - self.session['wizard'] = {} - self.d = session['dialog'] + def __init__(self, dialog): + """Initialize the Wizard""" + self._pages = [] + self.dialog = dialog def add_page(self, page): """Add a new page to the wizard""" - self.pages.append(page) + self._pages.append(page) def run(self): """Run the wizard""" idx = 0 while True: try: - total = len(self.pages) - title = "(%d/%d) %s" % (idx + 1, total, self.pages[idx].title) - idx += self.pages[idx].run(self.session, title) + total = len(self._pages) + title = "(%d/%d) %s" % (idx + 1, total, self._pages[idx].title) + idx += self._pages[idx].show(self.dialog, title) except WizardExit: return False except WizardReloadPage: continue - if idx >= len(self.pages): + if idx >= len(self._pages): text = "All necessary information has been gathered:\n\n" - for page in self.pages: - text += " * %s\n" % page.info + for page in self._pages: + text += " * %s\n" % page text += "\nContinue with the image creation process?" - ret = self.d.yesno( - text, width=PAGE_WIDTH, height=8 + len(self.pages), + ret = self.dialog.yesno( + text, width=PAGE_WIDTH, height=8 + len(self._pages), ok_label="Yes", cancel="Back", extra_button=1, extra_label="Quit", title="Confirmation") - if ret == self.d.DIALOG_CANCEL: + if ret == self.dialog.DIALOG_CANCEL: idx -= 1 - elif ret == self.d.DIALOG_EXTRA: + elif ret == self.dialog.DIALOG_EXTRA: return False - elif ret == self.d.DIALOG_OK: + elif ret == self.dialog.DIALOG_OK: return True if idx < 0: return False + @property + def answers(self): + """Returns the answers the user provided""" + return dict((page.name, page.answer) for page in self._pages) + class WizardPage(object): """Represents a page in a wizard""" NEXT = 1 PREV = -1 - def __init__(self, name, display_name, text, **kargs): + def __init__(self, name, text, **kargs): self.name = name - self.display_name = display_name + self.answer = None self.text = text - - self.title = kargs['title'] if 'title' in kargs else "" + self.print_name = kargs['print_name'] if 'print_name' in kargs \ + else " ".join(re.findall('[A-Z][^A-Z]*', name)) + self.title = kargs['title'] if 'title' in kargs else self.print_name self.default = kargs['default'] if 'default' in kargs else "" self.extra = kargs['extra'] if 'extra' in kargs else None - self.extra_label = \ - kargs['extra_label'] if 'extra_label' in kargs else 'Extra' + self.validate = \ + kargs['validate'] if 'validate' in kargs else lambda x: x + self.display = kargs['display'] if 'display' in kargs else lambda x: x + + self.dargs = {} + self.dargs['ok_label'] = 'Next' + self.dargs['cancel'] = 'Back' + self.dargs['width'] = PAGE_WIDTH + self.dargs['height'] = PAGE_HEIGHT - self.info = "%s: <none>" % self.display_name + if 'extra' in kargs: + self.dargs['extra_button'] = 1 - validate = kargs['validate'] if 'validate' in kargs else lambda x: x - setattr(self, "validate", validate) + if 'extra_label' in kargs: + self.dargs['extra_label'] = kargs['extra_label'] - display = kargs['display'] if 'display' in kargs else lambda x: x - setattr(self, "display", display) + def __str__(self): + """Prints the answer""" + return "%s: %s" % (self.print_name, self.display(self.answer)) - def run(self, session, title): + def show(self, dialog, title): """Display this wizard page This function is used by the wizard program when accessing a page. @@ -127,34 +142,76 @@ class WizardPage(object): raise NotImplementedError +class WizardInputPage(WizardPage): + """Represents an input field in a wizard""" + + def show(self, dialog, title): + """Display this wizard page""" + (code, answer) = dialog.inputbox(self.text, init=self.default, + title=title, **self.dargs) + + if code in (dialog.DIALOG_CANCEL, dialog.DIALOG_ESC): + return self.PREV + + self.answer = self.validate(answer.strip()) + self.default = self.answer + + return self.NEXT + + class WizardInfoPage(WizardPage): """Represents a Wizard Page that just displays some user-defined information. + + The user-defined information is created by the info function. """ - def __init__(self, name, display_name, text, body, **kargs): - super(WizardInfoPage, self).__init__(name, display_name, text, **kargs) - self.body = body + def __init__(self, name, text, info, **kargs): + """Initialize the WizardInfoPage instance""" + super(WizardInfoPage, self).__init__(name, text, **kargs) + self.info = info - def run(self, session, title): - d = session['dialog'] - w = session['wizard'] + def show(self, dialog, title): + """Display this wizard page""" - extra_button = 1 if self.extra else 0 - text = "%s\n\n%s" % (self.text, self.body()) + text = "%s\n\n%s" % (self.text, self.info()) - ret = d.yesno(text, width=PAGE_WIDTH, ok_label="Next", - cancel="Back", extra_button=extra_button, title=title, - extra_label=self.extra_label, height=PAGE_HEIGHT) + ret = dialog.yesno(text, title=title, **self.dargs) - if ret in (d.DIALOG_CANCEL, d.DIALOG_ESC): + if ret in (dialog.DIALOG_CANCEL, dialog.DIALOG_ESC): return self.PREV - elif ret == d.DIALOG_EXTRA: + elif ret == dialog.DIALOG_EXTRA: self.extra() raise WizardReloadPage # DIALOG_OK - w[self.name] = self.validate(None) - self.info = "%s: %s" % (self.display_name, self.display(w[self.name])) + self.answer = self.validate(None) + return self.NEXT + + +class WizardFormPage(WizardPage): + """Represents a Form in a wizard""" + + def __init__(self, name, text, fields, **kargs): + """Initialize the WizardFormPage instance""" + super(WizardFormPage, self).__init__(name, text, **kargs) + self.fields = fields + + def show(self, dialog, title): + """Display this wizard page""" + field_lenght = len(self.fields()) + form_height = field_lenght if field_lenght < PAGE_HEIGHT - 4 \ + else PAGE_HEIGHT - 4 + + (code, output) = dialog.form(self.text, form_height=form_height, + fields=self.fields(), title=title, + default_item=self.default, **self.dargs) + + if code in (dialog.DIALOG_CANCEL, dialog.DIALOG_ESC): + return self.PREV + + self.answer = self.validate(output) + self.default = output + return self.NEXT @@ -166,9 +223,9 @@ class WizardPageWthChoices(WizardPage): the choices variable. If the choices function returns an empty list, a fallback function is executed if available. """ - def __init__(self, name, display_name, text, choices, **kargs): - super(WizardPageWthChoices, self).__init__(name, display_name, text, - **kargs) + def __init__(self, name, text, choices, **kargs): + """Initialize the WizardPageWthChoices instance""" + super(WizardPageWthChoices, self).__init__(name, text, **kargs) self.choices = choices self.fallback = kargs['fallback'] if 'fallback' in kargs else None @@ -176,77 +233,21 @@ class WizardPageWthChoices(WizardPage): class WizardRadioListPage(WizardPageWthChoices): """Represent a Radio List in a wizard""" - def run(self, session, title): - d = session['dialog'] - w = session['wizard'] - + def show(self, dialog, title): + """Display this wizard page""" choices = [] for choice in self.choices(): default = 1 if choice[0] == self.default else 0 choices.append((choice[0], choice[1], default)) - (code, answer) = d.radiolist( - self.text, width=PAGE_WIDTH, ok_label="Next", cancel="Back", - choices=choices, height=PAGE_HEIGHT, title=title) + (code, answer) = dialog.radiolist(self.text, choices=choices, + title=title, **self.dargs) - if code in (d.DIALOG_CANCEL, d.DIALOG_ESC): + if code in (dialog.DIALOG_CANCEL, dialog.DIALOG_ESC): return self.PREV - w[self.name] = self.validate(answer) + self.answer = self.validate(answer) self.default = answer - self.info = "%s: %s" % (self.display_name, self.display(w[self.name])) - - return self.NEXT - - -class WizardInputPage(WizardPage): - """Represents an input field in a wizard""" - - def run(self, session, title): - d = session['dialog'] - w = session['wizard'] - - (code, answer) = d.inputbox( - self.text, init=self.default, width=PAGE_WIDTH, ok_label="Next", - cancel="Back", height=PAGE_HEIGHT, title=title) - - if code in (d.DIALOG_CANCEL, d.DIALOG_ESC): - return self.PREV - - value = answer.strip() - self.default = value - w[self.name] = self.validate(value) - self.info = "%s: %s" % (self.display_name, self.display(w[self.name])) - - 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 PAGE_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 @@ -254,11 +255,8 @@ class WizardFormPage(WizardPage): class WizardMenuPage(WizardPageWthChoices): """Represents a menu dialog with available choices in a wizard""" - def run(self, session, title): - d = session['dialog'] - w = session['wizard'] - - extra_button = 1 if self.extra else 0 + def show(self, dialog, title): + """Display this wizard page""" choices = self.choices() @@ -271,21 +269,17 @@ class WizardMenuPage(WizardPageWthChoices): default_item = self.default if self.default else choices[0][0] - (code, choice) = d.menu( - self.text, width=PAGE_WIDTH, ok_label="Next", cancel="Back", - title=title, choices=choices, height=PAGE_HEIGHT, - default_item=default_item, extra_label=self.extra_label, - extra_button=extra_button) + (code, choice) = dialog.menu(self.text, title=title, choices=choices, + default_item=default_item, **self.dargs) - if code in (d.DIALOG_CANCEL, d.DIALOG_ESC): + if code in (dialog.DIALOG_CANCEL, dialog.DIALOG_ESC): return self.PREV - elif code == d.DIALOG_EXTRA: + elif code == dialog.DIALOG_EXTRA: self.extra() raise WizardReloadPage + self.answer = self.validate(choice) self.default = choice - w[self.name] = self.validate(choice) - self.info = "%s: %s" % (self.display_name, self.display(w[self.name])) return self.NEXT @@ -293,12 +287,13 @@ class WizardMenuPage(WizardPageWthChoices): def start_wizard(session): """Run the image creation wizard""" - image = session['image'] - distro = image.distro - ostype = image.ostype + metadata = session['metadata'] + distro = session['image'].distro + ostype = session['image'].ostype # Create Cloud Wizard Page def cloud_choices(): + """Returns the available clouds""" choices = [] for (name, cloud) in Kamaki.get_clouds().items(): descr = cloud['description'] if 'description' in cloud else '' @@ -306,10 +301,8 @@ def start_wizard(session): return choices - def cloud_add(): - return add_cloud(session) - - def cloud_none_available(): + def no_clouds(): + """Fallback function when no cloud account exists""" if not session['dialog'].yesno( "No available clouds found. Would you like to add one now?", width=PAGE_WIDTH, defaultno=0): @@ -317,38 +310,35 @@ def start_wizard(session): return False def cloud_validate(cloud): + """Checks if a cloud is valid""" if not Kamaki.get_account(cloud): if not session['dialog'].yesno( "The cloud you have selected is not valid! Would you " "like to edit it now?", width=PAGE_WIDTH, defaultno=0): if edit_cloud(session, cloud): return cloud - raise WizardReloadPage - return cloud cloud = WizardMenuPage( - "Cloud", "Cloud", + "Cloud", "Please select a cloud account or press <Add> to add a new one:", - choices=cloud_choices, extra_label="Add", extra=cloud_add, - title="Clouds", validate=cloud_validate, fallback=cloud_none_available) + cloud_choices, extra_label="Add", extra=lambda: add_cloud(session), + title="Clouds", validate=cloud_validate, fallback=no_clouds) # 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) + name = WizardInputPage("ImageName", "Please provide a name for the image:", + 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 '') + "ImageDescription", "Please provide a description for the image:", + default=metadata['DESCRIPTION'] if 'DESCRIPTION' in metadata else '') # Create VirtIO Installation Page def display_installed_drivers(): """Returnes the installed virtio drivers""" + image = session['image'] versions = virtio_versions(image.os.virtio_state) ret = "Installed Block Device Driver: %(netkvm)s\n" \ @@ -362,6 +352,8 @@ def start_wizard(session): return ret def validate_virtio(_): + """Checks the state of the VirtIO drivers""" + image = session['image'] netkvm = len(image.os.virtio_state['netkvm']) != 0 viostor = len(image.os.virtio_state['viostor']) != 0 drv_dir = image.os.sysprep_params['virtio'].value @@ -371,68 +363,64 @@ def start_wizard(session): new_viostor = len(new['viostor']) != 0 if new else False new_netkvm = len(new['netkvm']) != 0 if new else False - d = session['dialog'] + dialog = session['dialog'] title = "VirtIO driver missing" msg = "Image creation cannot proceed unless a VirtIO %s driver " \ "is installed on the media!" if not (viostor or new_viostor): - d.msgbox(msg % "Block Device", width=PAGE_WIDTH, - height=PAGE_HEIGHT, title=title) + dialog.msgbox(msg % "Block Device", width=PAGE_WIDTH, + height=PAGE_HEIGHT, title=title) raise WizardReloadPage if not(netkvm or new_netkvm): - d.msgbox(msg % "Network Device", width=PAGE_WIDTH, - height=PAGE_HEIGHT, title=title) + dialog.msgbox(msg % "Network Device", width=PAGE_WIDTH, + height=PAGE_HEIGHT, title=title) raise WizardReloadPage return drv_dir virtio = WizardInfoPage( - "virtio", "VirtIO Drivers Path", - "Press <New> to install new VirtIO drivers.", + "virtio", "Press <New> to install new VirtIO drivers.", display_installed_drivers, title="VirtIO Drivers", extra_label='New', extra=lambda: update_sysprep_param(session, 'virtio'), - validate=validate_virtio) + validate=validate_virtio, print_name="VirtIO Drivers Path") # Create Image Registration Wizard Page def registration_choices(): + """Choices for the registration wizard page""" return [("Private", "Image is accessible only by this user"), ("Public", "Everyone can create VMs from this image")] - registration = WizardRadioListPage( - "ImageRegistration", "Registration Type", - "Please provide a registration type:", registration_choices, - title="Registration Type", default="Private") + registration = WizardRadioListPage("RegistrationType", + "Please provide a registration type:", + registration_choices, default="Private") - wizard = Wizard(session) + wizard = Wizard(session['dialog']) wizard.add_page(cloud) wizard.add_page(name) wizard.add_page(descr) - if hasattr(image.os, 'install_virtio_drivers'): + if hasattr(session['image'].os, 'install_virtio_drivers'): wizard.add_page(virtio) wizard.add_page(registration) if wizard.run(): - create_image(session) + create_image(session, wizard.answers) else: return False return True -def create_image(session): +def create_image(session, answers): """Create an image using the information collected by the wizard""" - d = session['dialog'] image = session['image'] - wizard = session['wizard'] with_progress = OutputWthProgress(True) - out = image.out - out.add(with_progress) + image.out.add(with_progress) try: - out.clear() + image.out.clear() - if 'virtio' in wizard and image.os.sysprep_params['virtio'].value: + if 'virtio' in answers and image.os.sysprep_params['virtio'].value: image.os.install_virtio_drivers() # Sysprep @@ -445,69 +433,65 @@ def create_image(session): update_background_title(session) metadata.update(image.meta) - metadata['DESCRIPTION'] = wizard['ImageDescription'] + metadata['DESCRIPTION'] = answers['ImageDescription'] # MD5 - md5 = MD5(out) - session['checksum'] = md5.compute(image.device, size) + session['checksum'] = MD5(image.out).compute(image.device, size) - out.output() + image.out.output() try: - out.output("Uploading image to the cloud:") - account = Kamaki.get_account(wizard['Cloud']) - assert account, "Cloud: %s is not valid" % wizard['Cloud'] - kamaki = Kamaki(account, out) + image.out.output("Uploading image to the cloud:") + account = Kamaki.get_account(answers['Cloud']) + assert account, "Cloud: %s is not valid" % answers['Cloud'] + kamaki = Kamaki(account, image.out) - name = "%s-%s.diskdump" % (wizard['ImageName'], + name = "%s-%s.diskdump" % (answers['ImageName'], time.strftime("%Y%m%d%H%M")) - pithos_file = "" - with open(image.device, 'rb') as f: - pithos_file = kamaki.upload(f, size, name, - "(1/3) Calculating block hashes", - "(2/3) Uploading missing blocks") + with open(image.device, 'rb') as device: + remote = kamaki.upload(device, size, name, + "(1/3) Calculating block hashes", + "(2/3) Uploading image blocks") - out.output("(3/3) Uploading md5sum file ...", False) + image.out.output("(3/3) Uploading md5sum file ...", False) md5sumstr = '%s %s\n' % (session['checksum'], name) kamaki.upload(StringIO.StringIO(md5sumstr), size=len(md5sumstr), remote_path="%s.%s" % (name, 'md5sum')) - out.success('done') - out.output() - - is_public = True if wizard['ImageRegistration'] == "Public" else \ - False - out.output('Registering %s image with the cloud ...' % - wizard['ImageRegistration'].lower(), False) - result = kamaki.register(wizard['ImageName'], pithos_file, - metadata, is_public) - out.success('done') - out.output("Uploading metadata file ...", False) + image.out.success('done') + image.out.output() + + image.out.output('Registering %s image with the cloud ...' % + answers['RegistrationType'].lower(), False) + result = kamaki.register(answers['ImageName'], remote, metadata, + answers['RegistrationType'] == "Public") + image.out.success('done') + image.out.output("Uploading metadata file ...", False) metastring = unicode(json.dumps(result, ensure_ascii=False)) kamaki.upload(StringIO.StringIO(metastring), size=len(metastring), remote_path="%s.%s" % (name, 'meta')) - out.success('done') + image.out.success('done') - if is_public: - out.output("Sharing md5sum file ...", False) + if answers['RegistrationType'] == "Public": + image.out.output("Sharing md5sum file ...", False) kamaki.share("%s.md5sum" % name) - out.success('done') - out.output("Sharing metadata file ...", False) + image.out.success('done') + image.out.output("Sharing metadata file ...", False) kamaki.share("%s.meta" % name) - out.success('done') + image.out.success('done') - out.output() + image.out.output() - except ClientError as e: + except ClientError as error: raise FatalError("Storage service client: %d %s" % - (e.status, e.message)) + (error.status, error.message)) finally: - out.remove(with_progress) + image.out.remove(with_progress) text = "The %s image was successfully uploaded to the storage service " \ "and registered with the compute service of %s. Would you like " \ "to keep a local copy?" % \ - (wizard['Cloud'], wizard['ImageRegistration'].lower()) + (answers['Cloud'], answers['RegistrationType'].lower()) - if not d.yesno(text, width=PAGE_WIDTH): + if not session['dialog'].yesno(text, width=PAGE_WIDTH): extract_image(session) # vim: set sta sts=4 shiftwidth=4 sw=4 et ai :