[et-mgmt-tools] [PATCH] modular virt-convert

Cole Robinson crobinso at redhat.com
Thu Jul 3 16:48:23 UTC 2008


John Levon wrote:
> This is merged with current tip and now installs properly.
> 
> thanks
> john
> 
> 
> Create config parser plugin system
> 
> Start a plugin system for config formats parsing, and implement enough
> of it to get to where we were with virt-convert.
> 
> Signed-off-by: John Levon <john.levon at sun.com>
> 

This looks good to me. Seems like the way to go to expand this in
the future.

If Dan and Joey are ok with this as well, I'll commit it.

Thanks,
Cole


> diff --git a/setup.py b/setup.py
> --- a/setup.py
> +++ b/setup.py
> @@ -9,7 +9,8 @@ import os, sys
>  import os, sys
>  import tests.coverage as coverage
>  
> -pkgs = ['virtinst']
> +pkgs = ['virtinst', 'virtconv', 'virtconv.parsers' ]
> +
>  datafiles = [('share/man/man1', ['man/en/virt-install.1',
>                                   'man/en/virt-clone.1',
>                                   'man/en/virt-image.1',
> diff --git a/virt-convert b/virt-convert
> --- a/virt-convert
> +++ b/virt-convert
> @@ -21,56 +21,58 @@
>  # MA 02110-1301 USA.
>  
>  import sys
> -from string import ascii_letters
> -import virtinst.cli as cli
>  import os
>  import logging
>  import errno
>  from optparse import OptionParser
>  
> +import virtinst.cli as cli
> +import virtconv
> +import virtconv.vmconfig as vmconfig
> +
>  def parse_args():
> -    parser = OptionParser()
> -    parser.set_usage("%prog [options] inputdir|input.vmx "
> +    opts = OptionParser()
> +    opts.set_usage("%prog [options] inputdir|input.vmx "
>          "[outputdir|output.xml]")
> -    parser.add_option("-a", "--arch", type="string", dest="arch",
> -                      help=("Machine Architecture Type (i686/x86_64/ppc)"))
> -    parser.add_option("-t", "--type", type="string", dest="type",
> -                      help=("Output virtualization type (hvm, paravirt"))
> -    parser.add_option("-d", "--debug", action="store_true", dest="debug",
> -                      help=("Print debugging information"))
> -    parser.add_option("-i", "--input-format", action="store",
> -                      dest="inputformat", default="vmx",
> -                      help=("Input format, e.g. 'vmx'"))
> -    parser.add_option("-o", "--output-format", action="store",
> -                      dest="outputformat", default="virt-image",
> -                      help=("Output format, e.g. 'virt-image'"))
> -    parser.add_option("-v", "--hvm", action="store_true", dest="fullvirt",
> -                      help=("This guest should be a fully virtualized guest"))
> -    parser.add_option("-p", "--paravirt", action="store_true", dest="paravirt",
> -                      help=("This guest should be a paravirtualized guest"))
> +    opts.add_option("-a", "--arch", type="string", dest="arch",
> +                    help=("Machine Architecture Type (i686/x86_64/ppc)"))
> +    opts.add_option("-t", "--type", type="string", dest="type",
> +                    help=("Output virtualization type (hvm, paravirt"))
> +    opts.add_option("-d", "--debug", action="store_true", dest="debug",
> +                    help=("Print debugging information"))
> +    opts.add_option("-i", "--input-format", action="store",
> +                    dest="inputformat", default="vmx",
> +                    help=("Input format, e.g. 'vmx'"))
> +    opts.add_option("-o", "--output-format", action="store",
> +                    dest="outputformat", default="virt-image",
> +                    help=("Output format, e.g. 'virt-image'"))
> +    opts.add_option("-v", "--hvm", action="store_true", dest="fullvirt",
> +                    help=("This guest should be a fully virtualized guest"))
> +    opts.add_option("-p", "--paravirt", action="store_true", dest="paravirt",
> +                    help=("This guest should be a paravirtualized guest"))
>  
> -    (options, args) = parser.parse_args()
> +    (options, args) = opts.parse_args()
>      if len(args) < 1:
> -        parser.error(("You need to provide an input VM definition"))
> +        opts.error(("You need to provide an input VM definition"))
>      if len(args) > 2:
> -        parser.error(("Too many arguments provided"))
> +        opts.error(("Too many arguments provided"))
>      
>      if (options.arch is None):
> -        parser.error(("Missing option value \n\nArchitecture: " +
> +        opts.error(("Missing option value \n\nArchitecture: " +
>         str(options.arch)))
>  
>      # hard-code for now
>      if options.inputformat != "vmx":
> -        parser.error(("Unsupported input format \"%s\"" % options.inputformat))
> +        opts.error(("Unsupported input format \"%s\"" % options.inputformat))
>      if options.outputformat != "virt-image":
> -        parser.error(("Unsupported output format \"%s\""
> +        opts.error(("Unsupported output format \"%s\""
>              % options.outputformat))
>      if os.path.isdir(args[0]):
>          vmx_files = [x for x in os.listdir(args[0]) if x.endswith(".vmx") ]
>          if (len(vmx_files)) == 0:
> -            parser.error(("No VM definition file was found in %s" % args[0]))
> +            opts.error(("No VM definition file was found in %s" % args[0]))
>          if (len(vmx_files)) > 1:
> -            parser.error(("Too many .vmx definitions found in %s" % args[0]))
> +            opts.error(("Too many .vmx definitions found in %s" % args[0]))
>          options.input_file = os.path.join(args[0], vmx_files[0])
>          options.input_dir =  args[0]
>      else:
> @@ -91,197 +93,78 @@ def parse_args():
>  
>      return options
>  
> -# Begin creation of xml template from parsed vmx config file
> -def vmx_to_image_xml(disks_list, record, options, hvm):
> -    pv_disk_list = []
> -    fv_disk_list = []
> -    storage_disk_list = []
> -
> -    infile = options.input_file
> -
> -    # validate required values for conversion are in the input vmx file
> -    if record.has_key("displayName"):
> -        name = record["displayName"]
> -    else:
> -        logging.error("displayName key not parsed from %s" % infile)
> -        sys.exit(1)
> -
> -    if record.has_key("memsize"):
> -        memory = int(record["memsize"]) * 1024
> -    else:
> -        logging.error("memsize key not parsed from %s" % infile)
> -        sys.exit(1)
> -
> -    if record.has_key("annotation"):
> -        annotation = record["annotation"]
> -    else:
> -        annotation = ""
> -
> -    if record.has_key("numvcpus"):
> -        vcpus = record["numvcpus"]
> -    else:
> -        vcpus = "1"
> -
> -
> -# create disk filename lists for xml template
> -    for (number, dfile) in enumerate(disks_list):
> -        dfile = str(dfile.replace(".vmdk","")).strip()
> -        pv_disk_list.append("""<drive disk="%s.img" target="xvd%s"/>""" % \
> -                               (dfile, ascii_letters[number % 26]))
> -        fv_disk_list.append("""<drive disk="%s.img" target="hd%s"/>""" % \
> -                               (dfile, ascii_letters[number % 26]))
> -        storage_disk_list.append("""<disk file="%s.img" use="system" format="raw"/>""" % (dfile))
> -
> -# determine virtualization type for image.boot section
> -    if hvm is False:
> -        virt_boot_template = """<boot type="xen">
> -          <guest>
> -            <arch>%(vm_arch)s</arch>
> -            <features>
> -              <pae/>
> -            </features>
> -          </guest>
> -          <os>
> -            <loader>pygrub</loader>
> -          </os>
> -         %(vm_pv_disks)s
> -         </boot>"""
> -    elif hvm is True:
> -        virt_boot_template = """<boot type="hvm">
> -          <guest>
> -            <arch>%(vm_arch)s</arch>
> -          </guest>
> -          <os>
> -            <loader dev="hd"/>
> -          </os>
> -          %(vm_fv_disks)s
> -        </boot>"""
> -
> -
> -# xml replacements dictionaries
> -    virt_boot_xml_dict = {
> -        "vm_pv_disks" : "".join(pv_disk_list),
> -        "vm_fv_disks" : "".join(fv_disk_list),
> -        "vm_arch" : options.arch,
> -         }
> -    virt_boot_template = virt_boot_template % virt_boot_xml_dict
> -    virt_image_xml_dict = {
> -        "virt_boot_template" : virt_boot_template,
> -        "vm_displayName": name.replace(" ","_"),
> -        "vm_annotation" : annotation,
> -        "vm_vcpus" : vcpus,
> -        "vm_mem"  : memory,
> -        "vm_storage" : "".join(storage_disk_list),
> -         }
> -    
> -    virt_image_xml_template = """<image>
> - <name>%(vm_displayName)s</name>
> -  <label>%(vm_displayName)s</label>
> -    <description>
> -        %(vm_annotation)s
> -    </description>
> -      <domain>
> -       %(virt_boot_template)s
> -        <devices>
> -         <vcpu>%(vm_vcpus)s</vcpu>
> -         <memory>%(vm_mem)s</memory>
> -          <interface/>
> -          <graphics/>
> -        </devices>
> -      </domain>
> -  <storage>
> -  %(vm_storage)s
> -  </storage>
> -</image>
> -"""
> -    
> -    virtimage_xml_template = virt_image_xml_template % virt_image_xml_dict
> -    return virtimage_xml_template
> -
> -# parse input vmware configuration
> -def parse_vmware_config(options):
> -    if not os.access(options.input_file, os.R_OK):
> -        raise ValueError, "Could not read file: %s" % options.input_file
> -    infile = open(options.input_file, "r")
> -    contents = infile.readlines()
> -    infile.close()
> -    record = {}
> -    vm_config = []
> -    disks_list = []
> -
> -    # strip out comment and blank lines for easy splitting of values
> -    for line in contents:
> -        if not line.strip() or line.startswith("#"):
> -            continue
> -        else:
> -            vm_config.append(line)
> -    
> -    for line in vm_config:
> -        before_eq, after_eq = line.split("=", 1)
> -        key = before_eq.replace(" ","")
> -        value = after_eq.replace('"',"")
> -        value = value.strip()
> -        record[key] = value
> -        logging.debug("Key: %s      Value: \"%s\"" % (key, value))
> -        if value.endswith("vmdk"): # separate disks from config
> -            disks_list.append(value)
> -    return record, disks_list
> -
> -
> -def convert_disks(disks_list, options):
> -    for disk in disks_list:
> -        infile = disk.strip()
> -        if not os.path.isabs(infile):
> -            infile = os.path.join(options.input_dir, infile)
> -
> -        outfile = disk.replace(".vmdk","").strip()
> -        outfile += ".img"
> -        if not os.path.isabs(outfile):
> -            outfile = os.path.join(options.output_dir, outfile)
> -        convert_cmd = "qemu-img convert %s -O raw %s" % (infile, outfile)
> -        ret = os.system(convert_cmd)
> -        print ret
> -
> -
>  def main():
>      options = parse_args()
>      cli.setupLogging("virt-convert", options.debug)
>  
> -    vm_config = parse_vmware_config(options)
> -    record, disks_list = vm_config
> +    try:
> +        inp = virtconv.vmconfig.find_parser_by_name(options.inputformat)
> +    except:
> +        logging.error("No parser of format \"%s\" was found." %
> +            options.inputformat)
> +        sys.exit(1)
> + 
> +    try:
> +        outp = virtconv.vmconfig.find_parser_by_name(options.outputformat)
> +    except:
> +        logging.error("No parser of format \"%s\" was found." %
> +            options.outputformat)
> +        sys.exit(1)
> +
> +    vmdef = None
> +
> +    try:
> +        vmdef = inp.import_file(options.input_file)
> +    except IOError, e:
> +        logging.error("Couldn't import file \"%s\": %s" %
> +            (options.input_file, e.strerror))
> +        sys.exit(1)
> +    except Exception, e:
> +        logging.error("Couldn't import file \"%s\": %s" %
> +            (options.input_file, e.message))
> +        sys.exit(1)
>  
>      if options.paravirt:
> -        hvm = False
> +        vmdef.type = vmconfig.VM_TYPE_PV
>      else:
> -        hvm = True
> -    out_contents = vmx_to_image_xml(disks_list, record, options, hvm)
> +        vmdef.type = vmconfig.VM_TYPE_HVM
>  
> -    name = record["displayName"].replace(" ","-")
> +    vmdef.arch = options.arch
> +
> +    unixname = vmdef.name.replace(" ", "-")
>      if not options.output_dir:
> -        options.output_dir = name
> +        options.output_dir = unixname
>      try:
>          logging.debug("Creating directory %s" % options.output_dir)
>          os.mkdir(options.output_dir)
>      except OSError, e:
>          if (e.errno != errno.EEXIST):
>              logging.error("Could not create directory %s: %s" %
> -                (options.output_dir, str(e)))
> +                (options.output_dir, e.strerror))
>              sys.exit(1)
>  
>      if not options.output_file:
>          options.output_file = os.path.join(options.output_dir,
> -       "%s.virt-image.xml" % name)
> +           "%s%s" % (unixname, outp.suffix))
>  
>      logging.debug("input_file: %s" % options.input_file)
>      logging.debug("input_dir: %s" % options.input_dir)
>      logging.debug("output_file: %s" % options.output_file)
>      logging.debug("output_dir: %s" % options.input_dir)
>  
> -    # configuration completed, ready to write config file and convert disks
> -    out = open(options.output_file, "w")
> -    out.writelines(out_contents)
> -    out.close()
> -    convert_disks(disks_list, options)
> +    try:
> +        for d in vmdef.disks:
> +            d.convert(options.input_dir, options.output_dir,
> +                vmconfig.DISK_TYPE_RAW)
> +    except Exception, e:
> +        logging.error(e)
> +        sys.exit(1)
> + 
> +    try:
> +        outp.export_file(vmdef, options.output_file)
> +    except Exception, e:
> +        logging.error(e)
> +        sys.exit(1)
>  
>      print "\n\nConversion completed and placed in: %s" % options.output_dir
>  
> diff --git a/virtconv/__init__.py b/virtconv/__init__.py
> new file mode 100644
> --- /dev/null
> +++ b/virtconv/__init__.py
> @@ -0,0 +1,29 @@
> +#
> +# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
> +# Use is subject to license terms.
> +#
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free  Software Foundation; either version 2 of the License, or
> +# (at your option)  any later version.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program; if not, write to the Free Software
> +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
> +# MA 02110-1301 USA.
> +#
> +
> +import pkgutil
> +import imp
> +import os
> +
> +parsers_path = [os.path.join(__path__[0], "parsers/")]
> +
> +for loader, name, ispkg in pkgutil.iter_modules(parsers_path):
> +    filename, pathname, desc = imp.find_module(name, parsers_path)
> +    imp.load_module(name, filename, pathname, desc)
> diff --git a/virtconv/parsers/virtimage.py b/virtconv/parsers/virtimage.py
> new file mode 100644
> --- /dev/null
> +++ b/virtconv/parsers/virtimage.py
> @@ -0,0 +1,152 @@
> +#
> +# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
> +# Use is subject to license terms.
> +#
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free  Software Foundation; either version 2 of the License, or
> +# (at your option)  any later version.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program; if not, write to the Free Software
> +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
> +# MA 02110-1301 USA.
> +#
> +
> +from string import ascii_letters
> +import virtconv.vmconfig as vmconfig
> +
> +pv_boot_template = """
> +  <boot type="xen">
> +   <guest>
> +    <arch>%(arch)s</arch>
> +    <features>
> +     <pae/>
> +    </features>
> +   </guest>
> +   <os>
> +    <loader>pygrub</loader>
> +   </os>
> +   %(pv_disks)s
> +  </boot>
> +"""
> +
> +hvm_boot_template = """
> +  <boot type="hvm">
> +   <guest>
> +    <arch>%(arch)s</arch>
> +   </guest>
> +   <os>
> +    <loader dev="hd"/>
> +   </os>
> +   %(hvm_disks)s
> +  </boot>
> +"""
> +
> +image_template = """
> +<image>
> + <name>%(name)s</name>
> + <label>%(name)s</label>
> + <description>
> +  %(description)s
> + </description>
> + <domain>
> +  %(boot_template)s
> +  <devices>
> +   <vcpu>%(nr_vcpus)s</vcpu>
> +   <memory>%(memory)s</memory>
> +   <interface/>
> +   <graphics/>
> +  </devices>
> + </domain>
> + <storage>
> +  %(storage)s
> + </storage>
> +</image>
> +"""
> +
> +class virtimage_parser(vmconfig.parser):
> +    """
> +    Support for virt-install's image format (see virt-image man page).
> +    """
> +    name = "virt-image"
> +    suffix = ".virt-image.xml"
> +
> +    @staticmethod
> +    def identify_file(input_file):
> +        """
> +        Return True if the given file is of this format.
> +        """
> +        raise NotImplementedError
> +
> +    @staticmethod
> +    def import_file(input_file):
> +        """
> +        Import a configuration file.  Raises if the file couldn't be
> +        opened, or parsing otherwise failed.
> +        """
> +        raise NotImplementedError
> +
> +    @staticmethod
> +    def export_file(vm, output_file):
> +        """
> +        Export a configuration file.
> +        @vm vm configuration instance
> +        @file Output file
> +
> +        Raises ValueError if configuration is not suitable, or another
> +        exception on failure to write the output file.
> +        """
> +
> +        if not vm.memory:
> +            raise ValueError("VM must have a memory setting")
> +
> +        pv_disks = []
> +        hvm_disks = []
> +        storage_disks = []
> +
> +        # create disk filename lists for xml template
> +        for disk in vm.disks:
> +            number = disk.number
> +            path = disk.path
> +
> +            # FIXME: needs updating for later Xen enhancements; need to
> +            # implement capabilities checking for max disks etc.
> +            pv_disks.append("""<drive disk="%s" target="xvd%s" />""" %
> +                (path, ascii_letters[number % 26]))
> +            hvm_disks.append("""<drive disk="%s" target="hd%s" />""" %
> +                (path, ascii_letters[number % 26]))
> +            storage_disks.append(
> +                """<disk file="%s" use="system" format="raw"/>""" % path)
> +
> +        if vm.type == vmconfig.VM_TYPE_PV:
> +            boot_template = pv_boot_template
> +        else:
> +            boot_template = hvm_boot_template
> +
> +        boot_xml = boot_template % {
> +            "pv_disks" : "".join(pv_disks),
> +            "hvm_disks" : "".join(hvm_disks),
> +            "arch" : vm.arch,
> +        }
> +
> +        out = image_template % {
> +            "boot_template": boot_xml,
> +            "name" : vm.name,
> +            "description" : vm.description,
> +            "nr_vcpus" : vm.nr_vcpus,
> +            # Mb to Kb
> +            "memory" : int(vm.memory) * 1024,
> +            "storage" : "".join(storage_disks),
> +        }
> +
> +        outfile = open(output_file, "w")
> +        outfile.writelines(out)
> +        outfile.close()
> +
> +vmconfig.register_parser(virtimage_parser)
> diff --git a/virtconv/parsers/vmx.py b/virtconv/parsers/vmx.py
> new file mode 100644
> --- /dev/null
> +++ b/virtconv/parsers/vmx.py
> @@ -0,0 +1,107 @@
> +#
> +# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
> +# Use is subject to license terms.
> +#
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free  Software Foundation; either version 2 of the License, or
> +# (at your option)  any later version.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program; if not, write to the Free Software
> +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
> +# MA 02110-1301 USA.
> +#
> +
> +import virtconv.vmconfig as vmconfig
> +
> +class vmx_parser(vmconfig.parser):
> +    """
> +    Support for VMWare .vmx files.  Note that documentation is
> +    particularly sparse on this format, with pretty much the best
> +    resource being http://sanbarrow.com/vmx.html
> +    """
> +
> +    name = "vmx"
> +    suffix = ".vmx"
> +
> +    @staticmethod
> +    def identify_file(input_file):
> +        """
> +        Return True if the given file is of this format.
> +        """
> +        raise NotImplementedError
> +
> +    @staticmethod
> +    def import_file(input_file):
> +        """
> +        Import a configuration file.  Raises if the file couldn't be
> +        opened, or parsing otherwise failed.
> +        """
> +
> +        vm = vmconfig.vm()
> +
> +        infile = open(input_file, "r")
> +        contents = infile.readlines()
> +        infile.close()
> +
> +        lines = []
> +
> +        # strip out comment and blank lines for easy splitting of values
> +        for line in contents:
> +            if not line.strip() or line.startswith("#"):
> +                continue
> +            else:
> +                lines.append(line)
> +    
> +        config = {}
> +
> +        # split out all remaining entries of key = value form
> +        for (line_nr, line) in enumerate(lines):
> +            try:
> +                before_eq, after_eq = line.split("=", 1)
> +                key = before_eq.replace(" ","")
> +                value = after_eq.replace('"',"")
> +                value = value.strip()
> +                config[key] = value
> +            except:
> +                raise Exception("Syntax error at line %d: %s" %
> +                    (line_nr + 1, line.strip()))
> +
> +        if not config.get("displayName"):
> +            raise ValueError("No displayName defined in \"%s\"" % input_file)
> +        vm.name = config.get("displayName")
> +
> +        vm.memory = config.get("memsize")
> +        vm.description = config.get("annotation")
> +        vm.nr_vcpus = config.get("numvcpus")
> +
> +        # FIXME: this should probably be a lot smarter. I don't think
> +        # this even gets disk numbering right.
> +        disks = [ d for d in config.values() if d.endswith(".vmdk") ]
> +
> +        for (number, path) in enumerate(disks):
> +            vm.disks += [ vmconfig.disk(path, number, vmconfig.DISK_TYPE_VMDK) ]
> +
> +        vm.validate()
> +        return vm
> +
> +    @staticmethod
> +    def export_file(vm, output_file):
> +        """
> +        Export a configuration file.
> +        @vm vm configuration instance
> +        @file Output file
> +
> +        Raises ValueError if configuration is not suitable, or another
> +        exception on failure to write the output file.
> +        """
> +
> +        raise NotImplementedError
> +
> +vmconfig.register_parser(vmx_parser)
> diff --git a/virtconv/vmconfig.py b/virtconv/vmconfig.py
> new file mode 100644
> --- /dev/null
> +++ b/virtconv/vmconfig.py
> @@ -0,0 +1,193 @@
> +#
> +# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
> +# Use is subject to license terms.
> +#
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free  Software Foundation; either version 2 of the License, or
> +# (at your option)  any later version.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program; if not, write to the Free Software
> +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
> +# MA 02110-1301 USA.
> +#
> +
> +import os
> +
> +_parsers = [ ]
> +
> +VM_TYPE_PV = 0
> +VM_TYPE_HVM = 1
> +
> +DISK_TYPE_RAW = 0
> +DISK_TYPE_VMDK = 1
> +
> +disk_suffixes = {
> +    DISK_TYPE_RAW: ".img",
> +    DISK_TYPE_VMDK: ".vmdk",
> +}
> +
> +qemu_formats = {
> +    DISK_TYPE_RAW: "raw",
> +    DISK_TYPE_VMDK: "vmdk",
> +}
> +
> +class disk(object):
> +    """Definition of an individual disk instance."""
> +
> +    def __init__(self, path = None, number = None, type = None):
> +        self.path = path
> +        self.number = number
> +        self.type = type
> +
> +    def convert(self, input_dir, output_dir, output_type):
> +        """
> +        Convert a disk into the requested format if possible, in the
> +        given output directory.  Raises NotImplementedError or other
> +        failures.
> +        """
> +
> +        if self.type == output_type:
> +            return
> +
> +        if output_type != DISK_TYPE_RAW:
> +            raise NotImplementedError("Cannot convert to disk type %d" %
> +                output_type)
> +
> +        infile = self.path
> +
> +        if not os.path.isabs(infile):
> +            infile = os.path.join(input_dir, infile)
> +
> +        outfile = self.path
> +
> +        if os.path.isabs(outfile):
> +            outfile = os.path.basename(outfile)
> +
> +        outfile = outfile.replace(disk_suffixes[self.type],
> +            disk_suffixes[output_type]).strip()
> +
> +        convert_cmd = ("qemu-img convert \"%s\" -O %s \"%s\"" %
> +            (infile, qemu_formats[output_type],
> +            os.path.join(output_dir, outfile)))
> +
> +        os.system(convert_cmd)
> +
> +        # Note: this is the *relative* path still
> +        self.path = outfile
> +        self.type = output_type
> +
> +
> +class vm(object):
> +    """
> +    Generic configuration for a particular VM instance.
> +
> +    At export, a plugin is guaranteed to have the at least the following
> +    values set (any others needed should be checked for, raising
> +    ValueError on failure):
> +
> +    vm.name
> +    vm.description (defaults to empty string)
> +    vm.nr_vcpus (defaults to 1)
> +    vm.type
> +    vm.arch
> +
> +    If vm.memory is set, it is in Mb units.
> +    """
> +
> +    name = None
> +    suffix = None
> +
> +    def __init__(self):
> +        self.name = None
> +        self.description = None
> +        self.memory = None
> +        self.nr_vcpus = None
> +        self.disks = [ ]
> +        self.type = VM_TYPE_HVM
> +        self.arch = "i686" # FIXME?
> +
> +    def validate(self):
> +        """
> +        Validate all parameters, and fix up any unset values to meet the
> +        guarantees we make above.
> +        """
> +
> +        if not self.name:
> +            raise ValueError("VM name is not set")
> +        if not self.description:
> +            self.description = ""
> +        if not self.nr_vcpus:
> +            self.nr_vcpus = 1
> +        if not self.type:
> +            raise ValueError("VM type is not set")
> +        if not self.arch:
> +            raise ValueError("VM arch is not set")
> +
> +        
> +class parser(object):
> +    """
> +    Base class for particular config file format definitions of
> +    a VM instance.
> +
> +    Warning: this interface is not (yet) considered stable and may
> +    change at will.
> +    """
> +
> +    @staticmethod
> +    def identify_file(input_file):
> +        """
> +        Return True if the given file is of this format.
> +        """
> +        raise NotImplementedError
> +
> +    @staticmethod
> +    def import_file(input_file):
> +        """
> +        Import a configuration file.  Raises if the file couldn't be
> +        opened, or parsing otherwise failed.
> +        """
> +        raise NotImplementedError
> +
> +    @staticmethod
> +    def export_file(vm, output_file):
> +        """
> +        Export a configuration file.
> +        @vm vm configuration instance
> +        @output_file Output file
> +
> +        Raises ValueError if configuration is not suitable, or another
> +        exception on failure to write the output file.
> +        """
> +        raise NotImplementedError
> +            
> +
> +def register_parser(parser):
> +    """
> +    Register a particular config format parser.  This should be called by each
> +    config plugin on import.
> +    """
> +
> +    global _parsers
> +    _parsers += [ parser ]
> +
> +def find_parser_by_name(name):
> +    """
> +    Return the parser of the given name
> +    """
> +    return [p for p in _parsers if p.name == name][0] or None
> +
> +def find_parser_by_file(input_file):
> +    """
> +    Return the parser that is capable of comprehending the given file.
> +    """
> +    for p in _parsers:
> +        if p.identify_file(input_file):
> +            return p
> +    return None
> 




More information about the et-mgmt-tools mailing list