[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