class Template(object):
    uuid_pattern = '([0-9a-f]{8})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{12})'

    identifier = props.prop("_identifier", "The identifier property.")
    url = props.prop("_url", "The url property.")
    xml = props.prop("_xml", "The xml property.")
    @property
    def name(self):
        """The property name"""
        return self._content_at_path('/template/name')
    @property
    def os_name(self):
        """The property os_name"""
        return self._content_at_path('/template/os/name')
    @property
    def os_version(self):
        """The property os_version"""
        return self._content_at_path('/template/os/version')
    @property
    def os_arch(self):
        """The property os_arch"""
        return self._content_at_path('/template/os/arch')
    @property
    def install_type(self):
        """The type of install ('url' or 'iso')"""
        result = libxml2.parseDoc(self.xml).xpathEval('/template/os/install')[0]
        if result:
            return result.prop('type')
        else:
            return None
    @property
    def install_url(self):
        """OS install URL"""
        return self._content_at_path('/template/os/install/url')
    @property
    def install_iso(self):
        """OS install ISO"""
        return self._content_at_path('/template/os/install/iso')
    @property
    def install_location(self):
        """Either OS install URL or ISO"""
        return self._content_at_path('/template/os/install/%s' % self.install_type)

    def __repr__(self):
        if(self.xml):
            return self.xml
        else:
            return super(Template, self).__repr__

    def _content_at_path(self, path):
        try:
            return libxml2.parseDoc(self.xml).xpathEval(path)[0].content
        except Exception as e:
            self.log.exception('Could not parse document for path (%s):\n%s' % (path, e))
            return None

    def __init__(self, template=None, uuid=None, url=None, xml=None):
        self.log = logging.getLogger('%s.%s' % (__name__, self.__class__.__name__))

        self.identifier = None
        self.url = None
        self.xml = None

        path = None
        if(template):
            template_string = str(template)
            template_string_type = self.__template_string_type(template_string)
            if(template_string_type == "UUID"):
                uuid = template_string
            elif(template_string_type == "URL"):
                url = template_string
            elif(template_string_type == "XML"):
                xml = template_string
            elif(template_string_type == "PATH"):
                path = template_string

        if(uuid):
            uuid_string = uuid
            self.identifier, self.xml = self.__fetch_template_for_uuid(uuid_string)
            if((not self.identifier) and (not self.xml)):
                raise RuntimeError("Could not create a template with the uuid %s" % (uuid, ))
        elif(url):
            self.url = url
            self.identifier, self.xml = self.__fetch_template_with_url(url)
        elif(xml):
            self.xml = xml
        elif(path):
            template_file = open(path, "r")
            file_content = template_file.read()
            template_file.close()
            if(self.__string_is_xml_template(file_content)):
                self.xml = file_content
            else:
                raise ValueError("File %s does not contain properly formatted template xml:\n%s" % (path, self.__abbreviated_template(file_content)))
        else:
            raise ValueError("'template' must be a UUID, URL, XML string or XML document path...")

    def __template_string_type(self, template_string):
        regex = re.compile(Template.uuid_pattern)
        match = regex.search(template_string)

        if(template_string.lower().startswith("http")):
            return "URL"
        elif(("<template" in template_string.lower()) and ("</template>" in template_string.lower())):
            return "XML"
        elif(match):
            return "UUID"
        elif(os.path.exists(template_string)):
            return "PATH"
        else:
            raise ValueError("'template_string' must be a UUID, URL, or XML document...\n--- TEMPLATE STRING ---\n%s\n-----------------" % (template_string, ))

    def __string_is_xml_template(self, text):
        return (("<template" in text.lower()) and ("</template>" in text.lower()))

    def __abbreviated_template(self, template_string):
        lines = template_string.splitlines(True)
        if(len(lines) > 20):
            return "%s\n...\n...\n...\n%s" % ("".join(lines[0:10]), "".join(lines[-10:len(lines)]))
        else:
            return template_string class ApplicationConfiguration(Singleton):
    configuration = props.prop("_configuration", "The configuration property.")

    def _singleton_init(self, configuration = None):
        super(ApplicationConfiguration, self)._singleton_init()
        self.log = logging.getLogger('%s.%s' % (__name__, self.__class__.__name__))
        self.jeos_images = { }

        if configuration:
            if not isinstance(configuration, dict):
                raise Exception("ApplicationConfiguration configuration argument must be a dict")
            self.log.debug("ApplicationConfiguration passed a dictionary - ignoring any local config files including JEOS configs")
            self.configuration = configuration
        else:
            self.configuration = self.__parse_arguments()
            self.__parse_jeos_images()

        if not 'debug' in self.configuration:
            # This most likely means we are being used as a module/library and are not running CLI or daemon
            self.configuration['debug'] = False

        if not 'secondary' in self.configuration:
            # We use this in the non-daemon context so it needs to be set
            # TODO: Something cleaner?
            self.configuration['secondary'] = False

    def __init__(self, configuration = None):
        pass

    def __new_argument_parser(self, appname):
        main_description = """Image Factory is an application for creating system images for use on public and private clouds."""

        argparser = argparse.ArgumentParser(description=main_description, prog=appname)
        argparser.add_argument('--version', action='version', default=argparse.SUPPRESS, version=VERSION, help='Show the version number and exit')
        argparser.add_argument('--verbose', action='store_true', default=False, help='Set verbose logging.')
        argparser.add_argument('--config', default='/etc/imagefactory/imagefactory.conf', help='Configuration file to use. (default: %(default)s)')
        argparser.add_argument('--imgdir', default='/tmp', help='Build image files in location specified. (default: %(default)s)')
        argparser.add_argument('--timeout', type=int, default=3600, help='Set the timeout period for image building in seconds. (default: %(default)s)')
        argparser.add_argument('--tmpdir', default='/tmp', help='Use the specified location for temporary files.  (default: %(default)s)')
        argparser.add_argument('--plugins', default='/etc/imagefactory/plugins.d', help='Plugin directory. (default: %(default)s)')

        group_ec2 = argparser.add_argument_group(title='EC2 settings')
        group_ec2.add_argument('--ec2-32bit-util', default = 'm1.small', help='Instance type to use when launching a 32 bit utility instance')
        group_ec2.add_argument('--ec2-64bit-util', default = 'm1.large', help='Instance type to use when launching a 64 bit utility instance')

        if(appname == 'imagefactoryd'):
            debug_group = argparser.add_mutually_exclusive_group()
            debug_group.add_argument('--debug', action='store_true', default=False, help='Set really verbose logging for debugging.')
            debug_group.add_argument('--nodebug', dest='debug', action='store_false', help='Turn off the default verbose CLI logging')
            argparser.add_argument('--foreground', action='store_true', default=False, help='Stay in the foreground and avoid launching a daemon. (default: %(default)s)')
            group_rest = argparser.add_argument_group(title='REST service options')
            group_rest.add_argument('--port', type=int, default=8075, help='Port to attach the RESTful http interface to. (default: %(default)s)')
            group_rest.add_argument('--address', default='0.0.0.0', help='Interface address to listen to. (default: %(default)s)')
            group_rest.add_argument('--no_ssl', action='store_true', default=False, help='Turn off SSL. (default: %(default)s)')
            group_rest.add_argument('--ssl_pem', default='*', help='PEM certificate file to use for HTTPS access to the REST interface. (default: A transient certificate is generated at runtime.)')
            group_rest.add_argument('--no_oauth', action='store_true', default=False, help='Use 2 legged OAuth to protect the REST interface. (default: %(default)s)')
            group_rest.add_argument('--secondary', action='store_true', default=False, help='Operate as a secondary/helper factory. (default: %(default)s)')
        elif(appname == 'imagefactory'):
            debug_group = argparser.add_mutually_exclusive_group()
            debug_group.add_argument('--debug', action='store_true', default=True, help='Set really verbose logging for debugging.')
            debug_group.add_argument('--nodebug', dest='debug', action='store_false', help='Turn off the default verbose CLI logging')
            argparser.add_argument('--output', choices=('log', 'json'), default='log', help='Choose between log or json output. (default: %(default)s)')
            argparser.add_argument('--raw', action='store_true', default=False, help='Turn off pretty printing.')
            subparsers = argparser.add_subparsers(title='commands', dest='command')
            template_help = 'A file containing the image template or component outline, compatible with the TDL schema (http://imgfac.org/documentation/tdl).'

            cmd_base = subparsers.add_parser('base_image', help='Build a generic image.')
            cmd_base.add_argument('template', type=argparse.FileType(), help=template_help)
            self.__add_param_arguments(cmd_base)

            cmd_target = subparsers.add_parser('target_image', help='Customize an image for a given cloud.')
            cmd_target.add_argument('target', help='The name of the target cloud for which to customize the image.')
            target_group = cmd_target.add_mutually_exclusive_group(required=True)
            target_group.add_argument('--id', help='The uuid of the BaseImage to customize.')
            target_group.add_argument('--template', type=argparse.FileType(), help=template_help)
            self.__add_param_arguments(cmd_target)

            cmd_provider = subparsers.add_parser('provider_image', help='Push an image to a cloud provider.')
            cmd_provider.add_argument('target', help='The target type of the given cloud provider')
            cmd_provider.add_argument('provider', help="A file containing the cloud provider description or a string literal starting with '@' such as '@ec2-us-east-1'.")
            cmd_provider.add_argument('credentials', type=argparse.FileType(), help='A file containing the cloud provider credentials')
            provider_group = cmd_provider.add_mutually_exclusive_group(required=True)
            provider_group.add_argument('--id', help='The uuid of the TargetImage to push.')
            provider_group.add_argument('--template', type=argparse.FileType(), help=template_help)
            self.__add_param_arguments(cmd_provider)
            cmd_provider.add_argument('--snapshot', action='store_true', default=False, help='Use snapshot style building. (default: %(default)s)')

            cmd_import = subparsers.add_parser('import_base_image', help='Import a base image from a local disk image file')
            cmd_import.add_argument('image_file', help='A disk image file to import as a base image')
            self.__add_param_arguments(cmd_import)

            cmd_list = subparsers.add_parser('images', help='List images of a given type or get details of an image.')
            cmd_list.add_argument('fetch_spec', help='JSON formatted string of key/value pairs')

            cmd_delete = subparsers.add_parser('delete', help='Delete an image.')
            cmd_delete.add_argument('id', help='UUID of the image to delete')
            cmd_delete.add_argument('--provider', help="A file containing the cloud provider description or a string literal starting with '@' such as '@ec2-us-east-1'.")
            cmd_delete.add_argument('--credentials', type=argparse.FileType(), help='A file containing the cloud provider credentials')
            cmd_delete.add_argument('--target', help='The name of the target cloud for which to customize the image.')
            self.__add_param_arguments(cmd_delete)

            cmd_plugins = subparsers.add_parser('plugins', help='List active plugins or get details of a specific plugin.')
            cmd_plugins.add_argument('--id')
        return argparser

    def __add_param_arguments(self, parser):
        # We do this for all three image types so lets make it a util function
        parameters_help = 'An optional JSON file containing additional parameters to pass to the builders.'
        parser.add_argument('--parameters', type=argparse.FileType(), help=parameters_help)
        parser.add_argument('--parameter', nargs=2, action='append', help='A parameter name and the literal value to assign it.  Can be used more than once.')
        parser.add_argument('--file-parameter', nargs=2, action='append', help='A parameter name and a file to insert into it.  Can be used more than once.')

    def __parse_arguments(self):
        appname = sys.argv[0].rpartition('/')[2]
        argparser = self.__new_argument_parser(appname)
        if((appname == 'imagefactory') and (len(sys.argv) == 1)):
            argparser.print_help()
            sys.exit()
        configuration = argparser.parse_args()
        if (os.path.isfile(configuration.config)):
            try:
                def dencode(a_dict, encoding='ascii'):
                    new_dict = {}
                    for k,v in a_dict.items():
                        ek = k.encode(encoding)
                        if(isinstance(v, str)):
                            new_dict[ek] = v.encode(encoding)
                        elif(isinstance(v, dict)):
                            new_dict[ek] = dencode(v)
                        else:
                            new_dict[ek] = v
                    return new_dict

                config_file = open(configuration.config)
                uconfig = json.load(config_file, encoding="utf-8")
                config_file.close()
                defaults = uconfig
                print(defaults)
                argparser.set_defaults(**defaults)
                configuration = argparser.parse_args()
            except Exception as e:
                self.log.exception(e)
        return configuration.__dict__

    def __add_jeos_image(self, image_detail):
        log = logging.getLogger('%s.%s' % (__name__, self.__class__.__name__))
        # our multi-dimensional-dict has the following keys
        # target - provider - os - version - arch - provider_image_id - user - cmd_prefix
        for i in range(8):
            try:
                image_detail[i] = image_detail[i].strip()
            except IndexError:
                image_detail.append(None)

        (target, provider, os, version, arch, provider_image_id, user, cmd_prefix) = image_detail
        if not (target in self.jeos_images):
            self.jeos_images[target] = {}
        if not (provider in self.jeos_images[target]):
            self.jeos_images[target][provider] = {}
        if not (os in self.jeos_images[target][provider]):
            self.jeos_images[target][provider][os] = {}
        if not (version in self.jeos_images[target][provider][os]):
            self.jeos_images[target][provider][os][version] = {}
        if arch in self.jeos_images[target][provider][os][version]:
            log.warning("JEOS image defined more than once for %s - %s - %s - %s - %s" % (target, provider, os, version, arch))
            log.warning("Replacing (%s) with (%s)" % (self.jeos_images[target][provider][os][version][arch], provider_image_id))

        self.jeos_images[target][provider][os][version][arch] = {'img_id':provider_image_id,
                                                                 'user':user,
                                                                 'cmd_prefix':cmd_prefix}

    def __parse_jeos_images(self):
        log = logging.getLogger('%s.%s' % (__name__, self.__class__.__name__))
        print(self.configuration)
        config_urls = self.configuration['jeos_config']
        # Expand directories from the config and url-ify files
        # Read inlist - replace directories with their contents
        nextlist = []
        for path in config_urls:
            if os.path.isdir(path):
                for filename in os.listdir(path):
                    fullname = os.path.join(path, filename)
                    if os.path.isfile(fullname):
                        nextlist.append(fullname)
            else:
                nextlist.append(path)

        # Read nextlist - replace files with file:// URLs
        finalist = []
        for path in nextlist:
            if os.path.isfile(path):
                finalist.append("file://" + path)
            else:
                finalist.append(path)

        for url in finalist:
            try:
                filehandle = urllib.requst.urlopen(str(url))
                line = filehandle.readline().strip()
            except:
                log.warning("Failed to open JEOS URL (%s)" % url)
                continue
            line_number = 1

            while line:
                # Lines that start with '#' are a comment
                if line[0] == "#":
                    pass
                # Lines that are zero length are whitespace
                elif len(line.split()) == 0:
                    pass
                else:
                    image_detail = line.split(":")
                    if len(image_detail) >= 6:
                        self.__add_jeos_image(image_detail)
                    else:
                        log.warning("Failed to parse line %d in JEOS config (%s):\n%s" % (line_number, url, line))

                line = filehandle.readline()
                line_number += 1

            filehandle.close() class BuildDispatcher(Singleton):

    def _singleton_init(self):
        self.log = logging.getLogger('%s.%s' % (__name__, self.__class__.__name__))
        self.builders = dict()
        self.builders_lock = BoundedSemaphore()
        NotificationCenter().add_observer(self, 'handle_state_change', 'image.status')

    def handle_state_change(self, notification):
        status = notification.user_info['new_status']
        if(status in ('COMPLETED', 'FAILED', 'DELETED', 'DELETEFAILED')):
            self.builders_lock.acquire()
            image_id = notification.sender.identifier
            if(image_id in self.builders):
                del self.builders[image_id]
                self.log.debug('Removed builder from BuildDispatcher on notification from image %s: %s' % (image_id, status))
            self.builders_lock.release()

    def builder_for_base_image(self, template, parameters=None):
        builder = Builder()
        builder.build_image_from_template(template, parameters=parameters)
        self.builders_lock.acquire()
        try:
            self.builders[builder.base_image.identifier] = builder
        finally:
            self.builders_lock.release()
        return builder

    def builder_for_target_image(self, target, image_id=None, template=None, parameters=None):
        builder = Builder()
        builder.customize_image_for_target(target, image_id, template, parameters)
        self.builders_lock.acquire()
        try:
            self.builders[builder.target_image.identifier] = builder
        finally:
            self.builders_lock.release()
        return builder

    def builder_for_provider_image(self, provider, credentials, target, image_id=None, template=None, parameters=None, my_image_id=None):
        builder = Builder()
        builder.create_image_on_provider(provider, credentials, target, image_id, template, parameters, my_image_id)
        self.builders_lock.acquire()
        try:
            self.builders[builder.provider_image.identifier] = builder
        finally:
            self.builders_lock.release()
        return builder def launch_inspect_and_mount(diskfile, readonly=False):
    g = guestfs.GuestFS()
    # Added to allow plugins that wish to inspect base images without modifying them
    # (once FINISHED images should never be changed)
    if readonly:
        g.add_drive_ro(diskfile)
    else:
        g.add_drive(diskfile)
    g.launch()
    return inspect_and_mount(g, diskfile=diskfile) def shutdown_and_close(guestfs_handle):
    shutdown_result = guestfs_handle.shutdown()
    guestfs_handle.close()
    if shutdown_result:
        raise Exception("Error encountered during guestfs shutdown - data may not have been written out") def remove_net_persist(guestfs_handle):
    # In the cloud context we currently never need or want persistent net device names
    # This is known to break networking in RHEL/VMWare and could potentially do so elsewhere
    # Just delete the file to be safe
    g = guestfs_handle
    if g.is_file("/etc/udev/rules.d/70-persistent-net.rules"):
        g.rm("/etc/udev/rules.d/70-persistent-net.rules")

    # Also clear out the MAC address this image was bound to.
    g.aug_init("/", 0)
    # This silently fails, without an exception, if the HWADDR is already gone
    g.aug_rm("/files/etc/sysconfig/network-scripts/ifcfg-eth0/HWADDR")
    g.aug_save()
    g.aug_close() def create_cloud_info(guestfs_handle, target):
    tmpl = 'CLOUD_TYPE="%s"\n' % (target)
    guestfs_handle.write("/etc/sysconfig/cloud-info", tmpl) def check_qcow_size(filename):
    # Return the size of the underlying disk image in qcow file
    # Return None if the file is not a qcow disk image
    unpack = unpack_qcow_header(filename)
    if unpack:
        return unpack[5]
    else:
        return None def subprocess_check_output(*popenargs, **kwargs):
    if 'stdout' in kwargs:
        raise ValueError('stdout argument not allowed, it will be overridden.')

    process = subprocess.Popen(stdout=subprocess.PIPE, stderr=subprocess.STDOUT, *popenargs, **kwargs)

    stdout, stderr = process.communicate()
    retcode = process.poll()

    if retcode:
        cmd = ' '.join(*popenargs)
        raise ImageFactoryException("'%s' failed(%d): %s" % (cmd, retcode, stderr))
    return (stdout, stderr, retcode) def qemu_convert_cmd(inputfn, outputfn, compress=False):
    convert_cmd = [ 'qemu-img', 'convert', '-O', 'qcow2' ]
    if compress:
        convert_cmd += [ '-c' ]
    if _qemu_compat_supported():
        convert_cmd += [ '-o', 'compat=0.10' ]
    convert_cmd += [ inputfn, outputfn ]
    return convert_cmd
import logging
import libxml2
import json
import os
    import glanceclient as glance_client
from zope.interface import implementer
from xml.etree.ElementTree import fromstring
from imgfac.Template import Template
from imgfac.ApplicationConfiguration import ApplicationConfiguration
from imgfac.BuildDispatcher import BuildDispatcher
from imgfac.ImageFactoryException import ImageFactoryException
from imgfac.CloudDelegate import CloudDelegate
from imgfac.FactoryUtils import launch_inspect_and_mount, shutdown_and_close, remove_net_persist, create_cloud_info
from imgfac.FactoryUtils import check_qcow_size, subprocess_check_output, qemu_convert_cmd
    from keystoneclient.v2_0 import client
       from glance import client as glance_client
        # Note that we are now missing ( template, target, config_block = None):
        super(OpenStack, self).__init__()
        self.app_config = ApplicationConfiguration().configuration
        self.log = logging.getLogger('%s.%s' % (__name__, self.__class__.__name__))

        self.version = GLANCE_VERSION
        if self.version == 2:
            self.credentials_attrs = [ 'auth_url', 'password', 'tenant', 'username']
        elif self.version == 1:
             self.credentials_attrs = [ 'auth_url', 'password', 'strategy', 'tenant', 'username']

    def activity(self, activity):
        # Simple helper function
        # Activity should be a one line human-readable string indicating the task in progress
        # We log it at DEBUG and also set it as the status_detail on our active image
        self.log.debug(activity)
        self.active_image.status_detail['activity'] = activity

    def push_image_to_provider(self, builder, provider, credentials, target, target_image, parameters):
        # Fail gracefully if the Glance/Keystone clients are not installed.
        if self.version is None:
            raise ImageFactoryException('Keystone/Glance clients not available - cannot push to provider')

        # Our target_image is already a raw KVM image.  All we need to do is upload to glance
        self.builder = builder
        self.active_image = self.builder.provider_image
        self.openstack_decode_credentials(credentials)

        provider_data = self.get_dynamic_provider_data(provider)
        if provider_data is None:
            raise ImageFactoryException("OpenStack KVM instance not found in XML or JSON provided")

        # Image is always here and it is the target_image datafile
        input_image = self.builder.target_image.data

        # If the template species a name, use that, otherwise create a name
        # using provider_image.identifier.
        template = Template(self.builder.provider_image.template)
        if template.name:
            image_name = template.name
        else:
            image_name = 'ImageFactory created image - %s' % (self.builder.provider_image.identifier)

        if check_qcow_size(input_image):
            self.log.debug("Uploading image to glance, detected qcow format")
            disk_format='qcow2'
        else:
            self.log.debug("Uploading image to glance, assuming raw format")
            disk_format='raw'

        # Support openstack grizzly keystone authentication and glance upload
        if self.version == 2:
            if self.credentials_token is None:
                self.credentials_token = self.keystone_authenticate(**self.credentials_dict)

            provider_data['name']  = image_name
            provider_data['disk_format'] = disk_format

            image_id = self.glance_upload_v2(input_image, self.credentials_token, **provider_data)
        elif self.version == 1:
            # Also support backward compatible for folsom
            image_id = self.glance_upload(input_image, creds = self.credentials_dict, token = self.credentials_token,
                                     host=provider_data['glance-host'], port=provider_data['glance-port'],
                                     name=image_name, disk_format=disk_format)

        self.builder.provider_image.identifier_on_provider = image_id
        if 'username' in self.credentials_dict:
            self.builder.provider_image.provider_account_identifier = self.credentials_dict['username']
        self.percent_complete=100

    def openstack_decode_credentials(self, credentials):
        self.activity("Preparing OpenStack credentials")
        # TODO: Validate these - in particular, ensure that if some nodes are missing at least
        #       a minimal acceptable set of auth is present
        doc = libxml2.parseDoc(credentials)

        self.credentials_dict = { }
        for authprop in self.credentials_attrs:
            value = self._get_xml_node(doc, authprop)
            if value is not None:
                self.credentials_dict[authprop] = value
        self.credentials_token = self._get_xml_node(doc, 'token')

    def _get_xml_node(self, doc, credtype):
        nodes = doc.xpathEval("//provider_credentials/openstack_credentials/%s" % (credtype))
        # OpenStack supports multiple auth schemes so not all nodes are required
        if len(nodes) < 1:
            return None

        return nodes[0].content

    def snapshot_image_on_provider(self, builder, provider, credentials, template, parameters):
        # TODO: Implement snapshot builds
        raise ImageFactoryException("Snapshot builds not currently supported on OpenStack KVM")

    def builder_should_create_target_image(self, builder, target, image_id, template, parameters):
        return True

    def builder_will_create_target_image(self, builder, target, image_id, template, parameters):
        pass

    def builder_did_create_target_image(self, builder, target, image_id, template, parameters):
        self.target=target
        self.builder=builder 
        self.modify_oz_filesystem()

        # OS plugin has already provided the initial file for us to work with
        # which we can currently assume is a raw image
        input_image = builder.target_image.data

        # Support conversion to alternate preferred image format
        # Currently only handle qcow2, but the size reduction of
        # using this avoids the performance penalty of uploading
        # (and launching) raw disk images on slow storage
        if self.app_config.get('openstack_image_format', 'raw') == 'qcow2':
            # None of the existing input base_image plugins produce compressed qcow2 output
            # the step below is either going from raw to compressed qcow2 or
            # uncompressed qcow2 to compressed qcow2
            self.log.debug("Converting image to compressed qcow2 format")
            tmp_output = input_image + ".tmp.qcow2"

            convert_cmd = qemu_convert_cmd(input_image, tmp_output, True)