Signed-off-by: Agata Murawska <[email protected]>
---
Makefile.am | 2 +
lib/ovf.py | 110 ++++++++++++++++++++++++++++
tools/ovfconverter | 202 ++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 314 insertions(+), 0 deletions(-)
create mode 100644 lib/ovf.py
create mode 100755 tools/ovfconverter
diff --git a/Makefile.am b/Makefile.am
index a9ef001..dbd2d70 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -185,6 +185,7 @@ pkgpython_PYTHON = \
lib/netutils.py \
lib/objects.py \
lib/opcodes.py \
+ lib/ovf.py \
lib/qlang.py \
lib/query.py \
lib/rpc.py \
@@ -503,6 +504,7 @@ dist_tools_PYTHON = \
tools/cluster-merge \
tools/lvmstrap \
tools/move-instance \
+ tools/ovfconverter \
tools/setup-ssh \
tools/sanitize-config
diff --git a/lib/ovf.py b/lib/ovf.py
new file mode 100644
index 0000000..962f2f0
--- /dev/null
+++ b/lib/ovf.py
@@ -0,0 +1,110 @@
+#!/usr/bin/python
+#
+
+# Copyright (C) 2011 Google Inc.
+#
+# 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.
+
+
+"""Converter tools between ovf and ganeti config file
+
+"""
+
+import os.path
+
+from ganeti import errors
+
+class Converter(object):
+ """Converter class for OVF packages.
+
+ Converter is a class above both ImporterOVF and ExporterOVF. It's purpose is
+ to provide a common interface for the two.
+
+ @type options: optparse.Values
+ @ivar options: options parsed from the command line
+
+ """
+ def __init__(self, input_path, options):
+ """Initialize the converter.
+
+ @type input_path: string
+ @param input_path: path to the Converter input file
+ @type options: optparse.Values
+ @param options: command line options
+
+ @raise errors.OpPrereqError: if file does not exist
+
+ """
+ input_path = os.path.abspath(input_path)
+ if not (os.path.exists(input_path) and os.path.isfile(input_path)):
+ raise errors.OpPrereqError("File %s does not exist" % input_path)
+ self.options = options
+ self._ReadInputData(input_path)
+
+ def _ReadInputData(self, input_path):
+ """Reads the data on which the conversion will take place.
+
+ @type input_path: string
+ @param input_path: absolute path to the Converter input file
+
+ """
+ raise NotImplementedError()
+
+ def Parse(self):
+ """Parses the data and creates a structure containing all required info.
+
+ """
+ raise NotImplementedError()
+
+ def Save(self):
+ """Saves the gathered configuration in an apropriate format.
+
+ """
+ raise NotImplementedError()
+
+ def Cleanup(self):
+ """Cleans the temporary directory, if one was created.
+
+ """
+ raise NotImplementedError()
+
+
+class OVFImporter(Converter):
+ def _ReadInputData(self, input_path):
+ pass
+
+ def Parse(self):
+ pass
+
+ def Save(self):
+ pass
+
+ def Cleanup(self):
+ pass
+
+
+class OVFExporter(Converter):
+ def _ReadInputData(self, input_path):
+ pass
+
+ def Parse(self):
+ pass
+
+ def Save(self):
+ pass
+
+ def Cleanup(self):
+ pass
diff --git a/tools/ovfconverter b/tools/ovfconverter
new file mode 100755
index 0000000..57ec3f0
--- /dev/null
+++ b/tools/ovfconverter
@@ -0,0 +1,202 @@
+#!/usr/bin/python
+#
+
+# Copyright (C) 2011 Google Inc.
+#
+# 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.
+
+
+"""Tool to translate between ovf and ganeti backup format.
+
+"""
+
+import logging
+import optparse
+import os
+
+from ganeti import cli
+from ganeti import constants
+from ganeti import errors
+from ganeti import ovf
+
+
+def CheckOptions(parser, options_dict, required, forbidden, excluding, mode):
+ """Performes check on the command line options.
+
+ Checks whether the required arguments are present and if none of the
arguments
+ not supported for the current mode are given.
+
+ @type options_dict: list
+ @param options_dict: dictionary containing all the options from the command
+ line
+ @type required: list
+ @param required: list of pairs (option, argument) where 'option' is required
+ in mode 'mode'
+ @type forbidden: list
+ @param forbidden: list of pairs (option, argument) which are not allowed in
+ mode 'mode'
+ @type excluding: list
+ @param excluding: list of pairs (argument1, argument2); each pair contains
+ mutually exclusive arguments
+ @type mode: string
+ @param mode: current mode of the converter
+
+ """
+ for (option, argument) in required:
+ if not options_dict[option]:
+ parser.error("Argument %s is required for %s" % (argument, mode))
+ for (option, argument) in forbidden:
+ if options_dict[option]:
+ parser.error("Argument %s is not allowed in %s mode" % (argument, mode))
+ for (arg1, arg2) in excluding:
+ if options_dict[arg1] and options_dict[arg2]:
+ parser.error("Arguments %s and %s exclude each other" % (arg1, arg2))
+
+
+def ParseOptions():
+ """Parses the command line options and arguments.
+
+ In case of mismatching parameters, it will show the correct usage and exit.
+
+ @rtype: tuple
+ @return: (mode, sourcefile to read from, additional options)
+
+ """
+ parser = optparse.OptionParser(usage="%prog {import|export}
<source-cfg-file>"
+ " [options...]")
+ #global options
+ parser.add_option(cli.DEBUG_OPT)
+ parser.add_option(cli.VERBOSE_OPT)
+ parser.add_option("-n", "--name", dest="name", action="store",
+ help="Name of the instance")
+ parser.add_option("--output-dir", dest="output_dir",
+ help="Path to the output directory")
+
+ #import options
+ import_group = optparse.OptionGroup(parser, "Import options")
+ import_group.add_option(cli.BACKEND_OPT)
+ import_group.add_option(cli.DISK_OPT)
+ import_group.add_option(cli.DISK_TEMPLATE_OPT)
+ import_group.add_option(cli.HYPERVISOR_OPT)
+ import_group.add_option(cli.NET_OPT)
+ import_group.add_option(cli.NONICS_OPT)
+ import_group.add_option(cli.OS_OPT)
+ import_group.add_option(cli.OSPARAMS_OPT)
+ parser.add_option_group(import_group)
+
+ #export options
+ export_group = optparse.OptionGroup(parser, "Export options")
+ export_group.add_option("--compress", dest="compression",
+ action="store_true", default=False,
+ help="The exported disk will be compressed to
tar.gz")
+ export_group.add_option("--external", dest="ext_usage",
+ action="store_true", default=False,
+ help="The package will be used externally (ommits
the"
+ " Ganeti-specific parts of configuration)")
+ export_group.add_option("-f", "--format", dest="disk_format",
+ action="store",
+ choices=("raw", "cow", "vmdk"),
+ help="Disk format for export (one of raw/cow/vmdk)")
+ export_group.add_option("--ova", dest="ova_package",
+ action="store_true", default=False,
+ help="Export everything into OVA package")
+ parser.add_option_group(export_group)
+
+ options, args = parser.parse_args()
+ if len(args) != 2:
+ parser.error("Wrong number of arguments")
+ mode = args.pop(0)
+ input_path = os.path.abspath(args.pop(0))
+
+ if mode == "import":
+ required = []
+ forbidden = [
+ ("compression", "--compress"),
+ ("disk_format", "--format"),
+ ("ext_usage", "--external"),
+ ("ova_package", "--ova"),
+ ]
+ excluding = [("nics", "no_nics")]
+ elif mode == "export":
+ required = [("disk_format", "--format")]
+ forbidden = [
+ ("beparams", "--backend-parameters"),
+ ("disk_template", "--disk-template"),
+ ("disks", "--disk"),
+ ("hvparams", "--hypervisor-parameters"),
+ ("nics", "--net"),
+ ("no_nics", "--no-nics"),
+ ("os", "--os-type"),
+ ("osparams", "--os-parameters"),
+ ]
+ excluding = []
+ else:
+ parser.error("First argument should be either 'import' or 'export'")
+
+ options_dict = vars(options)
+ CheckOptions(parser, options_dict, required, forbidden, excluding, mode)
+
+ return (mode, input_path, options)
+
+
+def SetupLogging(options):
+ """Setting up logging infrastructure.
+
+ @type options: optparse.Values
+ @param options: parsed command line options
+
+ """
+ formatter = logging.Formatter("%(asctime)s: %(levelname)s %(message)s")
+
+ stderr_handler = logging.StreamHandler()
+ stderr_handler.setFormatter(formatter)
+ if options.debug:
+ stderr_handler.setLevel(logging.NOTSET)
+ elif options.verbose:
+ stderr_handler.setLevel(logging.INFO)
+ else:
+ stderr_handler.setLevel(logging.WARNING)
+
+ root_logger = logging.getLogger("")
+ root_logger.setLevel(logging.NOTSET)
+ root_logger.addHandler(stderr_handler)
+
+
+def main():
+ """Main routine.
+
+ """
+ (mode, input_path, options) = ParseOptions()
+ SetupLogging(options)
+ logging.info("Chosen %s mode, reading the %s file", mode, input_path)
+ assert mode in ("import" "export")
+ converter = None
+ try:
+ if mode == "import":
+ converter = ovf.OVFImporter(input_path, options)
+ elif mode == "export":
+ converter = ovf.OVFExporter(input_path, options)
+ converter.Parse()
+ converter.Save()
+ except errors.OpPrereqError, err:
+ if converter:
+ converter.Cleanup()
+ logging.exception(err)
+ return constants.EXIT_FAILURE
+
+
+if __name__ == "__main__":
+ main()
--
1.7.3.1