| #!/usr/bin/python |
| # |
| |
| # Copyright (C) 2011 Google Inc. |
| # All rights reserved. |
| # |
| # Redistribution and use in source and binary forms, with or without |
| # modification, are permitted provided that the following conditions are |
| # met: |
| # |
| # 1. Redistributions of source code must retain the above copyright notice, |
| # this list of conditions and the following disclaimer. |
| # |
| # 2. Redistributions in binary form must reproduce the above copyright |
| # notice, this list of conditions and the following disclaimer in the |
| # documentation and/or other materials provided with the distribution. |
| # |
| # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS |
| # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
| # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR |
| # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
| # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
| # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| |
| """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 |
| from ganeti import utils |
| |
| |
| 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 main(): |
| """Main routine. |
| |
| """ |
| (mode, input_path, options) = ParseOptions() |
| |
| utils.SetupToolLogging(options.debug, options.verbose) |
| |
| 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() |