bash_completion: Enable extglob while parsing file
[ganeti-local] / tools / ovfconverter
1 #!/usr/bin/python
2 #
3
4 # Copyright (C) 2011 Google Inc.
5 #
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 # General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 # 02110-1301, USA.
20
21
22 """Tool to translate between ovf and ganeti backup format.
23
24 """
25
26 import logging
27 import optparse
28 import os
29
30 from ganeti import cli
31 from ganeti import constants
32 from ganeti import errors
33 from ganeti import ovf
34
35
36 IMPORT_MODE = "import"
37 EXPORT_MODE = "export"
38
39
40 def CheckOptions(parser, options_dict, required, forbidden, excluding, mode):
41   """Performes check on the command line options.
42
43   Checks whether the required arguments are present and if none of the arguments
44   not supported for the current mode are given.
45
46   @type options_dict: list
47   @param options_dict: dictionary containing all the options from the command
48     line
49   @type required: list
50   @param required: list of pairs (option, argument) where 'option' is required
51     in mode 'mode'
52   @type forbidden: list
53   @param forbidden: list of pairs (option, argument) which are not allowed in
54     mode 'mode'
55   @type excluding: list
56   @param excluding: list of pairs (argument1, argument2); each pair contains
57     mutually exclusive arguments
58   @type mode: string
59   @param mode: current mode of the converter
60
61   """
62   for (option, argument) in required:
63     if not options_dict[option]:
64       parser.error("Argument %s is required for %s" % (argument, mode))
65   for (option, argument) in forbidden:
66     if options_dict[option]:
67       parser.error("Argument %s is not allowed in %s mode" % (argument, mode))
68   for (arg1, arg2) in excluding:
69     if options_dict[arg1] and options_dict[arg2]:
70       parser.error("Arguments %s and %s exclude each other" % (arg1, arg2))
71
72
73 def ParseOptions():
74   """Parses the command line options and arguments.
75
76   In case of mismatching parameters, it will show the correct usage and exit.
77
78   @rtype: tuple
79   @return: (mode, sourcefile to read from, additional options)
80
81   """
82   usage = ("%%prog {%s|%s} <source-cfg-file> [options...]" %
83            (IMPORT_MODE, EXPORT_MODE))
84   parser = optparse.OptionParser(usage=usage)
85
86   #global options
87   parser.add_option(cli.DEBUG_OPT)
88   parser.add_option(cli.VERBOSE_OPT)
89   parser.add_option("-n", "--name", dest="name", action="store",
90                     help="Name of the instance")
91   parser.add_option("--output-dir", dest="output_dir",
92                     help="Path to the output directory")
93
94   #import options
95   import_group = optparse.OptionGroup(parser, "Import options")
96   import_group.add_option(cli.BACKEND_OPT)
97   import_group.add_option(cli.DISK_OPT)
98   import_group.add_option(cli.DISK_TEMPLATE_OPT)
99   import_group.add_option(cli.HYPERVISOR_OPT)
100   import_group.add_option(cli.NET_OPT)
101   import_group.add_option(cli.NONICS_OPT)
102   import_group.add_option(cli.OS_OPT)
103   import_group.add_option(cli.OSPARAMS_OPT)
104   import_group.add_option(cli.TAG_ADD_OPT)
105   parser.add_option_group(import_group)
106
107   #export options
108   export_group = optparse.OptionGroup(parser, "Export options")
109   export_group.add_option("--compress", dest="compression",
110                           action="store_true", default=False,
111                           help="The exported disk will be compressed to tar.gz")
112   export_group.add_option("--external", dest="ext_usage",
113                           action="store_true", default=False,
114                           help="The package will be used externally (ommits the"
115                                " Ganeti-specific parts of configuration)")
116   export_group.add_option("-f", "--format", dest="disk_format",
117                           action="store",
118                           choices=("raw", "cow", "vmdk"),
119                           help="Disk format for export (one of raw/cow/vmdk)")
120   export_group.add_option("--ova", dest="ova_package",
121                           action="store_true", default=False,
122                           help="Export everything into OVA package")
123   parser.add_option_group(export_group)
124
125   options, args = parser.parse_args()
126   if len(args) != 2:
127     parser.error("Wrong number of arguments")
128   mode = args.pop(0)
129   input_path = os.path.abspath(args.pop(0))
130
131   if mode == IMPORT_MODE:
132     required = []
133     forbidden = [
134       ("compression", "--compress"),
135       ("disk_format", "--format"),
136       ("ext_usage", "--external"),
137       ("ova_package", "--ova"),
138     ]
139     excluding = [("nics", "no_nics")]
140   elif mode == EXPORT_MODE:
141     required = [("disk_format", "--format")]
142     forbidden = [
143       ("beparams", "--backend-parameters"),
144       ("disk_template", "--disk-template"),
145       ("disks", "--disk"),
146       ("hypervisor", "--hypervisor-parameters"),
147       ("nics", "--net"),
148       ("no_nics", "--no-nics"),
149       ("os", "--os-type"),
150       ("osparams", "--os-parameters"),
151       ("tags", "--tags"),
152     ]
153     excluding = []
154   else:
155     parser.error("First argument should be either '%s' or '%s'" %
156                  (IMPORT_MODE, EXPORT_MODE))
157
158   options_dict = vars(options)
159   CheckOptions(parser, options_dict, required, forbidden, excluding, mode)
160
161   return (mode, input_path, options)
162
163
164 def SetupLogging(options):
165   """Setting up logging infrastructure.
166
167   @type options: optparse.Values
168   @param options: parsed command line options
169
170   """
171   formatter = logging.Formatter("%(asctime)s: %(levelname)s %(message)s")
172
173   stderr_handler = logging.StreamHandler()
174   stderr_handler.setFormatter(formatter)
175   if options.debug:
176     stderr_handler.setLevel(logging.NOTSET)
177   elif options.verbose:
178     stderr_handler.setLevel(logging.INFO)
179   else:
180     stderr_handler.setLevel(logging.WARNING)
181
182   root_logger = logging.getLogger("")
183   root_logger.setLevel(logging.NOTSET)
184   root_logger.addHandler(stderr_handler)
185
186
187 def main():
188   """Main routine.
189
190   """
191   (mode, input_path, options) = ParseOptions()
192   SetupLogging(options)
193   logging.info("Chosen %s mode, reading the %s file", mode, input_path)
194   assert mode in (IMPORT_MODE, EXPORT_MODE)
195   converter = None
196   try:
197     if mode == IMPORT_MODE:
198       converter = ovf.OVFImporter(input_path, options)
199     elif mode == EXPORT_MODE:
200       converter = ovf.OVFExporter(input_path, options)
201     converter.Parse()
202     converter.Save()
203   except errors.OpPrereqError, err:
204     if converter:
205       converter.Cleanup()
206     logging.exception(err)
207     return constants.EXIT_FAILURE
208
209
210 if __name__ == "__main__":
211   main()