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

Reply via email to