diff --git a/Makefile.am b/Makefile.am
index 23aae310b1933627021453a1426041d220d4b0cd..9e6dcdc5553ae8f8b76b71bb90c6d58851cabe95 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -193,6 +193,7 @@ pkgpython_PYTHON = \
 	lib/netutils.py \
 	lib/objects.py \
 	lib/opcodes.py \
+	lib/ovf.py \
 	lib/qlang.py \
 	lib/query.py \
 	lib/rpc.py \
@@ -513,6 +514,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 0000000000000000000000000000000000000000..21816e8a68d7d056e32f68082770889775daab5e
--- /dev/null
+++ b/lib/ovf.py
@@ -0,0 +1,121 @@
+#!/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
+import shutil
+
+from ganeti import errors
+from ganeti import utils
+
+
+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
+  @type output_dir: string
+  @ivar output_dir: directory to which the results of conversion shall be
+    written
+  @type temp_file_manager: L{utils.TemporaryFileManager}
+  @ivar temp_file_manager: container for temporary files created during
+    conversion
+  @type temp_dir: string
+  @ivar temp_dir: temporary directory created then we deal with OVA
+
+  """
+  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.isfile(input_path):
+      raise errors.OpPrereqError("File does not exist: %s" % input_path)
+    self.options = options
+    self.temp_file_manager = utils.TemporaryFileManager()
+    self.temp_dir = None
+    self.output_dir = None
+    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.
+
+    """
+    self.temp_file_manager.Cleanup()
+    if self.temp_dir:
+      shutil.rmtree(self.temp_dir)
+      self.temp_dir = None
+
+
+class OVFImporter(Converter):
+  def _ReadInputData(self, input_path):
+    pass
+
+  def Parse(self):
+    pass
+
+  def Save(self):
+    pass
+
+
+class OVFExporter(Converter):
+  def _ReadInputData(self, input_path):
+    pass
+
+  def Parse(self):
+    pass
+
+  def Save(self):
+    pass
diff --git a/tools/ovfconverter b/tools/ovfconverter
new file mode 100755
index 0000000000000000000000000000000000000000..17024bf202bd9ed20fa07f4f4a833b3ed456cdf1
--- /dev/null
+++ b/tools/ovfconverter
@@ -0,0 +1,211 @@
+#!/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
+
+
+IMPORT_MODE = "import"
+EXPORT_MODE = "export"
+
+
+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)
+
+  """
+  usage = ("%%prog {%s|%s} <source-cfg-file> [options...]" %
+           (IMPORT_MODE, EXPORT_MODE))
+  parser = optparse.OptionParser(usage=usage)
+
+  #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)
+  import_group.add_option(cli.TAG_ADD_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_MODE:
+    required = []
+    forbidden = [
+      ("compression", "--compress"),
+      ("disk_format", "--format"),
+      ("ext_usage", "--external"),
+      ("ova_package", "--ova"),
+    ]
+    excluding = [("nics", "no_nics")]
+  elif mode == EXPORT_MODE:
+    required = [("disk_format", "--format")]
+    forbidden = [
+      ("beparams", "--backend-parameters"),
+      ("disk_template", "--disk-template"),
+      ("disks", "--disk"),
+      ("hypervisor", "--hypervisor-parameters"),
+      ("nics", "--net"),
+      ("no_nics", "--no-nics"),
+      ("os", "--os-type"),
+      ("osparams", "--os-parameters"),
+      ("tags", "--tags"),
+    ]
+    excluding = []
+  else:
+    parser.error("First argument should be either '%s' or '%s'" %
+                 (IMPORT_MODE, EXPORT_MODE))
+
+  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_MODE, EXPORT_MODE)
+  converter = None
+  try:
+    if mode == IMPORT_MODE:
+      converter = ovf.OVFImporter(input_path, options)
+    elif mode == EXPORT_MODE:
+      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()