def create_image(d, media, out, tmp, snapshot): """Create an image out of `media'""" d.setBackgroundTitle('snf-image-creator') gauge = GaugeOutput(d, "Initialization", "Initializing...") out.append(gauge) disk = Disk(media, out, tmp) # pylint: disable=unused-argument def signal_handler(signum, frame): gauge.cleanup() disk.cleanup() signal.signal(signal.SIGINT, signal_handler) signal.signal(signal.SIGTERM, signal_handler) try: device = disk.file if not snapshot else disk.snapshot() image = disk.get_image(device) gauge.cleanup() out.remove(gauge) # Make sure the signal handler does not call gauge.cleanup again def dummy(self): # pylint: disable=unused-argument pass gauge.cleanup = type(GaugeOutput.cleanup)(dummy, gauge, GaugeOutput) session = {"dialog": d, "disk": disk, "image": image} 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 d.yesno(msg, width=WIDTH, defaultno=1, height=12) == d.OK: 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," \ " <Expert> to run snf-image-creator in expert mode or press " \ "ESC to quit the program." \ % (image.ostype.capitalize() if image.ostype == image.distro or image.distro == "unknown" else "%s (%s)" % (image.ostype.capitalize(), image.distro.capitalize())) update_background_title(session) while True: code = d.yesno(msg, width=WIDTH, height=12, yes_label="Wizard", no_label="Expert") if code == d.OK: if start_wizard(session): break elif code == d.CANCEL: main_menu(session) break if confirm_exit(d): break d.infobox("Thank you for using snf-image-creator. Bye", width=53) finally: disk.cleanup() return 0
def image_creator(options, out): """snf-mkimage main function""" if os.geteuid() != 0: raise FatalError("You must run %s as root" % os.path.basename(sys.argv[0])) # Check if the authentication info is valid. The earlier the better if options.token is not None and options.url is not None: try: account = Kamaki.create_account(options.url, options.token) if account is None: raise FatalError("The authentication token and/or URL you " "provided is not valid!") else: kamaki = Kamaki(account, out) except ClientError as e: raise FatalError("Astakos client: %d %s" % (e.status, e.message)) elif options.cloud: avail_clouds = Kamaki.get_clouds() if options.cloud not in avail_clouds.keys(): raise FatalError( "Cloud: `%s' does not exist.\n\nAvailable clouds:\n\n\t%s\n" % (options.cloud, "\n\t".join(avail_clouds.keys()))) try: account = Kamaki.get_account(options.cloud) if account is None: raise FatalError( "Cloud: `%s' exists but is not valid!" % options.cloud) else: kamaki = Kamaki(account, out) except ClientError as e: raise FatalError("Astakos client: %d %s" % (e.status, e.message)) if options.upload and not options.force: if kamaki.object_exists(options.container, options.upload): raise FatalError("Remote storage service object: `%s' exists " "(use --force to overwrite it)." % options.upload) if kamaki.object_exists(options.container, "%s.md5sum" % options.upload): raise FatalError("Remote storage service object: `%s.md5sum' " "exists (use --force to overwrite it)." % options.upload) if options.register and not options.force: if kamaki.object_exists(options.container, "%s.meta" % options.upload): raise FatalError("Remote storage service object `%s.meta' exists " "(use --force to overwrite it)." % options.upload) disk = Disk(options.source, out, options.tmp) # pylint: disable=unused-argument def signal_handler(signum, frame): disk.cleanup() signal.signal(signal.SIGINT, signal_handler) signal.signal(signal.SIGTERM, signal_handler) try: # There is no need to snapshot the media if it was created by the Disk # instance as a temporary object. device = disk.file if not options.snapshot else disk.snapshot() image = disk.get_image(device, 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.")) if len(options.host_run) != 0 and not image.mount_local_support: raise FatalError("Running scripts against the guest media is not " "supported for this build of libguestfs.") if len(options.host_run) != 0: for script in options.host_run: if not os.path.isfile(script): raise FatalError("File: `%s' does not exist." % script) if not os.access(script, os.X_OK): raise FatalError("File: `%s' is not executable." % script) for name in options.disabled_syspreps: sysprep = image.os.get_sysprep_by_name(name) if sysprep is not None: image.os.disable_sysprep(sysprep) else: out.warn("Sysprep: `%s' does not exist. Can't disable it." % name) for name in options.enabled_syspreps: sysprep = image.os.get_sysprep_by_name(name) if sysprep is not None: image.os.enable_sysprep(sysprep) else: out.warn("Sysprep: `%s' does not exist. Can't enable it." % name) if image.is_unsupported(): image.meta['EXCLUDE_ALL_TASKS'] = "yes" # Add command line metadata to the collected ones... image.meta.update(options.metadata) if options.print_syspreps: image.os.print_syspreps() out.info() if options.print_sysprep_params: image.os.print_sysprep_params() out.info() if options.print_metadata: image.os.print_metadata() out.info() if options.outfile is None and not options.upload: return 0 if options.virtio is not None and \ hasattr(image.os, 'install_virtio_drivers'): image.os.install_virtio_drivers() if len(options.host_run) != 0: # Export image metadata as environment variables to make them # visible by the scripts for key, value in image.meta.items(): os.environ["SNF_IMAGE_CREATOR_METADATA_%s" % key] = str(value) out.info("Running scripts on the input media:") mpoint = tempfile.mkdtemp() try: image.mount(mpoint) if not image.is_mounted(): raise FatalError("Mounting the media on the host failed.") try: size = len(options.host_run) cnt = 1 for script in options.host_run: script = os.path.abspath(script) out.info(("(%d/%d)" % (cnt, size)).ljust(7), False) out.info("Running `%s'" % script) ret = subprocess.Popen([script], cwd=mpoint).wait() if ret != 0: raise FatalError("Script: `%s' failed (rc=%d)" % (script, ret)) cnt += 1 finally: while not image.umount(): out.warn("Unable to umount the media. Retrying ...") time.sleep(1) out.info() finally: os.rmdir(mpoint) if options.sysprep: image.os.do_sysprep() checksum = image.md5() image_meta = {} for k, v in image.meta.items(): image_meta[str(k)] = str(v) metastring = json.dumps( {'properties': image_meta, 'disk-format': 'diskdump'}, ensure_ascii=False) img_properties = json.dumps(image_meta, ensure_ascii=False) if options.outfile is not None: if os.path.realpath(options.outfile) == '/dev/null': out.warn('Not dumping file to /dev/null') else: image.dump(options.outfile) out.info('Dumping metadata file ...', False) with open('%s.%s' % (options.outfile, 'meta'), 'w') as f: f.write(metastring) out.success('done') out.info('Dumping md5sum file ...', False) with open('%s.%s' % (options.outfile, 'md5sum'), 'w') as f: f.write('%s %s\n' % (checksum, os.path.basename(options.outfile))) out.success('done') out.info('Dumping variant file ...', False) with open('%s.%s' % (options.outfile, 'variant'), 'w') as f: f.write(to_shell(IMG_ID=options.outfile, IMG_FORMAT="diskdump", IMG_PROPERTIES=img_properties)) out.success('done') out.info() try: if options.upload: out.info("Uploading image to the storage service:") with image.raw_device() as raw: with open(raw, 'rb') as f: remote = kamaki.upload( f, image.size, options.upload, options.container, None, "(1/3) Calculating block hashes", "(2/3) Uploading missing blocks") out.info("(3/3) Uploading md5sum file ...", False) md5sumstr = '%s %s\n' % (checksum, os.path.basename(options.upload)) kamaki.upload(StringIO.StringIO(md5sumstr), size=len(md5sumstr), remote_path="%s.%s" % (options.upload, 'md5sum'), container=options.container, content_type="text/plain") out.success('done') out.info() if options.register: img_type = 'public' if options.public else 'private' out.info('Registering %s image with the compute service ...' % img_type, False) result = kamaki.register(options.register, remote, image.meta, options.public) out.success('done') out.info("Uploading metadata file ...", False) metastring = unicode(json.dumps(result, ensure_ascii=False, indent=4)) kamaki.upload(StringIO.StringIO(metastring.encode('utf8')), size=len(metastring), remote_path="%s.%s" % (options.upload, 'meta'), container=options.container, content_type="application/json") out.success('done') if options.public: out.info("Sharing md5sum file ...", False) kamaki.share("%s.md5sum" % options.upload) out.success('done') out.info("Sharing metadata file ...", False) kamaki.share("%s.meta" % options.upload) out.success('done') out.result(json.dumps(result, indent=4, ensure_ascii=False)) out.info() except ClientError as e: raise FatalError("Service client: %d %s" % (e.status, e.message)) finally: out.info('cleaning up ...') disk.cleanup() out.success("snf-image-creator exited without errors") return 0
def image_creator(options, out): """snf-mkimage main function""" if os.geteuid() != 0: raise FatalError("You must run %s as root" % os.path.basename(sys.argv[0])) # Check if the authentication info is valid. The earlier the better if options.token is not None and options.url is not None: try: account = Kamaki.create_account(options.url, options.token) if account is None: raise FatalError("The authentication token and/or URL you " "provided is not valid!") else: kamaki = Kamaki(account, out) except ClientError as e: raise FatalError("Astakos client: %d %s" % (e.status, e.message)) elif options.cloud: avail_clouds = Kamaki.get_clouds() if options.cloud not in avail_clouds.keys(): raise FatalError( "Cloud: `%s' does not exist.\n\nAvailable clouds:\n\n\t%s\n" % (options.cloud, "\n\t".join(avail_clouds.keys()))) try: account = Kamaki.get_account(options.cloud) if account is None: raise FatalError("Cloud: `%s' exists but is not valid!" % options.cloud) else: kamaki = Kamaki(account, out) except ClientError as e: raise FatalError("Astakos client: %d %s" % (e.status, e.message)) if options.upload and not options.force: if kamaki.object_exists(options.container, options.upload): raise FatalError("Remote storage service object: `%s' exists " "(use --force to overwrite it)." % options.upload) if kamaki.object_exists(options.container, "%s.md5sum" % options.upload): raise FatalError("Remote storage service object: `%s.md5sum' " "exists (use --force to overwrite it)." % options.upload) if options.register and not options.force: if kamaki.object_exists(options.container, "%s.meta" % options.upload): raise FatalError("Remote storage service object `%s.meta' exists " "(use --force to overwrite it)." % options.upload) disk = Disk(options.source, out, options.tmp) # pylint: disable=unused-argument def signal_handler(signum, frame): disk.cleanup() signal.signal(signal.SIGINT, signal_handler) signal.signal(signal.SIGTERM, signal_handler) try: # There is no need to snapshot the media if it was created by the Disk # instance as a temporary object. device = disk.file if not options.snapshot else disk.snapshot() image = disk.get_image(device, 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.")) if len(options.host_run) != 0 and not image.mount_local_support: raise FatalError("Running scripts against the guest media is not " "supported for this build of libguestfs.") if len(options.host_run) != 0: for script in options.host_run: if not os.path.isfile(script): raise FatalError("File: `%s' does not exist." % script) if not os.access(script, os.X_OK): raise FatalError("File: `%s' is not executable." % script) for name in options.disabled_syspreps: sysprep = image.os.get_sysprep_by_name(name) if sysprep is not None: image.os.disable_sysprep(sysprep) else: out.warn("Sysprep: `%s' does not exist. Can't disable it." % name) for name in options.enabled_syspreps: sysprep = image.os.get_sysprep_by_name(name) if sysprep is not None: image.os.enable_sysprep(sysprep) else: out.warn("Sysprep: `%s' does not exist. Can't enable it." % name) if image.is_unsupported(): image.meta['EXCLUDE_ALL_TASKS'] = "yes" # Add command line metadata to the collected ones... image.meta.update(options.metadata) if options.print_syspreps: image.os.print_syspreps() out.info() if options.print_sysprep_params: image.os.print_sysprep_params() out.info() if options.print_metadata: image.os.print_metadata() out.info() if options.outfile is None and not options.upload: return 0 if options.virtio is not None and \ hasattr(image.os, 'install_virtio_drivers'): image.os.install_virtio_drivers() if len(options.host_run) != 0: # Export image metadata as environment variables to make them # visible by the scripts for key, value in image.meta.items(): os.environ["SNF_IMAGE_CREATOR_METADATA_%s" % key] = str(value) out.info("Running scripts on the input media:") mpoint = tempfile.mkdtemp() try: image.mount(mpoint) if not image.is_mounted(): raise FatalError("Mounting the media on the host failed.") try: size = len(options.host_run) cnt = 1 for script in options.host_run: script = os.path.abspath(script) out.info(("(%d/%d)" % (cnt, size)).ljust(7), False) out.info("Running `%s'" % script) ret = subprocess.Popen([script], cwd=mpoint).wait() if ret != 0: raise FatalError("Script: `%s' failed (rc=%d)" % (script, ret)) cnt += 1 finally: while not image.umount(): out.warn("Unable to umount the media. Retrying ...") time.sleep(1) out.info() finally: os.rmdir(mpoint) if options.sysprep: image.os.do_sysprep() checksum = image.md5() image_meta = {} for k, v in image.meta.items(): image_meta[str(k)] = str(v) metastring = json.dumps( { 'properties': image_meta, 'disk-format': 'diskdump' }, ensure_ascii=False) img_properties = json.dumps(image_meta, ensure_ascii=False) if options.outfile is not None: if os.path.realpath(options.outfile) == '/dev/null': out.warn('Not dumping file to /dev/null') else: image.dump(options.outfile) out.info('Dumping metadata file ...', False) with open('%s.%s' % (options.outfile, 'meta'), 'w') as f: f.write(metastring) out.success('done') out.info('Dumping md5sum file ...', False) with open('%s.%s' % (options.outfile, 'md5sum'), 'w') as f: f.write('%s %s\n' % (checksum, os.path.basename(options.outfile))) out.success('done') out.info('Dumping variant file ...', False) with open('%s.%s' % (options.outfile, 'variant'), 'w') as f: f.write( to_shell(IMG_ID=options.outfile, IMG_FORMAT="diskdump", IMG_PROPERTIES=img_properties)) out.success('done') out.info() try: if options.upload: out.info("Uploading image to the storage service:") with image.raw_device() as raw: with open(raw, 'rb') as f: remote = kamaki.upload( f, image.size, options.upload, options.container, None, "(1/3) Calculating block hashes", "(2/3) Uploading missing blocks") out.info("(3/3) Uploading md5sum file ...", False) md5sumstr = '%s %s\n' % (checksum, os.path.basename(options.upload)) kamaki.upload(StringIO.StringIO(md5sumstr), size=len(md5sumstr), remote_path="%s.%s" % (options.upload, 'md5sum'), container=options.container, content_type="text/plain") out.success('done') out.info() if options.register: img_type = 'public' if options.public else 'private' out.info( 'Registering %s image with the compute service ...' % img_type, False) result = kamaki.register(options.register, remote, image.meta, options.public) out.success('done') out.info("Uploading metadata file ...", False) metastring = unicode( json.dumps(result, ensure_ascii=False, indent=4)) kamaki.upload(StringIO.StringIO(metastring.encode('utf8')), size=len(metastring), remote_path="%s.%s" % (options.upload, 'meta'), container=options.container, content_type="application/json") out.success('done') if options.public: out.info("Sharing md5sum file ...", False) kamaki.share("%s.md5sum" % options.upload) out.success('done') out.info("Sharing metadata file ...", False) kamaki.share("%s.meta" % options.upload) out.success('done') out.result(json.dumps(result, indent=4, ensure_ascii=False)) out.info() except ClientError as e: raise FatalError("Service client: %d %s" % (e.status, e.message)) finally: out.info('cleaning up ...') disk.cleanup() out.success("snf-image-creator exited without errors") return 0