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

Joey Boggs jboggs at redhat.com
Thu Jul 3 16:57:22 UTC 2008


I'm more than happy with it, surprised it took off so fast actually :)

Thanks for you contributions John

Cole Robinson wrote:
> 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