Add more auto-completion metadata
[ganeti-local] / scripts / gnt-backup
index b32f109..4e1fe53 100755 (executable)
@@ -24,7 +24,6 @@
 # W0614: Unused import %s from wildcard import (since we need cli)
 
 import sys
-from optparse import make_option
 
 from ganeti.cli import *
 from ganeti import opcodes
@@ -35,6 +34,7 @@ from ganeti import utils
 
 _VALUE_TRUE = "true"
 
+
 def PrintExportList(opts, args):
   """Prints a list of all the exported system images.
 
@@ -45,7 +45,7 @@ def PrintExportList(opts, args):
   @return: the desired exit code
 
   """
-  exports = GetClient().QueryExports(opts.nodes)
+  exports = GetClient().QueryExports(opts.nodes, False)
   retcode = 0
   for node in exports:
     ToStdout("Node: %s", node)
@@ -74,8 +74,27 @@ def ExportInstance(opts, args):
                                 target_node=opts.node,
                                 shutdown=opts.shutdown)
 
-  SubmitOpCode(op)
-
+  fin_resu, dlist = SubmitOpCode(op)
+  if not isinstance(dlist, list):
+    ToStderr("Cannot parse execution results")
+    return 1
+  tot_dsk = len(dlist)
+  # TODO: handle diskless instances
+  if dlist.count(False) == 0:
+    # all OK
+    rcode = 0
+  elif dlist.count(True) == 0:
+    ToStderr("Error: No disks were backed up successfully."
+             " The export doesn't have any valid data,"
+             " it is recommended to retry the operation.")
+    rcode = 1
+  else:
+    ToStderr("Partial export failure: %d disks backed up, %d disks failed.",
+             dlist.count(True), dlist.count(False))
+    rcode = 2
+  if not fin_resu:
+    rcode = 1
+  return rcode
 
 def ImportInstance(opts, args):
   """Add an instance to the cluster.
@@ -102,8 +121,11 @@ def ImportInstance(opts, args):
     except ValueError, err:
       raise errors.OpPrereqError("Invalid NIC index passed: %s" % str(err))
     nics = [{}] * nic_max
-    for nidx, ndict in opts.nics.items():
+    for nidx, ndict in opts.nics:
       nidx = int(nidx)
+      if not isinstance(ndict, dict):
+        msg = "Invalid nic/%d value: expected dict, got %s" % (nidx, ndict)
+        raise errors.OpPrereqError(msg)
       nics[nidx] = ndict
   elif opts.no_nics:
     # no nics
@@ -113,13 +135,18 @@ def ImportInstance(opts, args):
     nics = [{}]
 
   if opts.disk_template == constants.DT_DISKLESS:
-    if opts.disks:
+    if opts.disks or opts.sd_size is not None:
       raise errors.OpPrereqError("Diskless instance but disk"
                                  " information passed")
     disks = []
   else:
-    if not opts.disks:
+    if not opts.disks and not opts.sd_size:
       raise errors.OpPrereqError("No disk information specified")
+    if opts.disks and opts.sd_size is not None:
+      raise errors.OpPrereqError("Please use either the '--disk' or"
+                                 " '-s' option")
+    if opts.sd_size is not None:
+      opts.disks = [(0, {"size": opts.sd_size})]
     try:
       disk_max = max(int(didx[0])+1 for didx in opts.disks)
     except ValueError, err:
@@ -127,7 +154,10 @@ def ImportInstance(opts, args):
     disks = [{}] * disk_max
     for didx, ddict in opts.disks:
       didx = int(didx)
-      if "size" not in ddict:
+      if not isinstance(ddict, dict):
+        msg = "Invalid disk/%d value: expected dict, got %s" % (didx, ddict)
+        raise errors.OpPrereqError(msg)
+      elif "size" not in ddict:
         raise errors.OpPrereqError("Missing size for disk %d" % didx)
       try:
         ddict["size"] = utils.ParseUnit(ddict["size"])
@@ -136,7 +166,8 @@ def ImportInstance(opts, args):
                                    (didx, err))
       disks[didx] = ddict
 
-  ValidateBeParams(opts.beparams)
+  utils.ForceDictType(opts.beparams, constants.BES_PARAMETER_TYPES)
+  utils.ForceDictType(hvparams, constants.HVS_PARAMETER_TYPES)
 
   op = opcodes.OpCreateInstance(instance_name=instance,
                                 disk_template=opts.disk_template,
@@ -180,76 +211,82 @@ def RemoveExport(opts, args):
 # this is defined separately due to readability only
 import_opts = [
   DEBUG_OPT,
-  make_option("-n", "--node", dest="node",
-              help="Target node and optional secondary node",
-              metavar="<pnode>[:<snode>]"),
-  keyval_option("-B", "--backend", dest="beparams",
-                type="keyval", default={},
-                help="Backend parameters"),
-  make_option("-t", "--disk-template", dest="disk_template",
-              help="Custom disk setup (diskless, file, plain, drbd)",
-              default=None, metavar="TEMPL"),
-  ikv_option("--disk", help="Disk information",
+  cli_option("-n", "--node", dest="node",
+             help="Target node and optional secondary node",
+             metavar="<pnode>[:<snode>]"),
+  cli_option("-B", "--backend", dest="beparams",
+             type="keyval", default={},
+             help="Backend parameters"),
+  cli_option("-t", "--disk-template", dest="disk_template",
+             help="Custom disk setup (diskless, file, plain, drbd)",
+             default=None, metavar="TEMPL",
+             choices=list(constants.DISK_TEMPLATES)),
+  cli_option("--disk", help="Disk information",
              default=[], dest="disks",
              action="append",
              type="identkeyval"),
-  ikv_option("--net", help="NIC information",
+  cli_option("-s", "--os-size", dest="sd_size", help="Disk size for a"
+             " single-disk configuration, when not using the --disk option,"
+             " in MiB unless a suffix is used",
+             default=None, type="unit", metavar="<size>"),
+  cli_option("--net", help="NIC information",
              default=[], dest="nics",
              action="append",
              type="identkeyval"),
-  make_option("--no-nics", default=False, action="store_true",
-              help="Do not create any network cards for the instance"),
-  make_option("--no-wait-for-sync", dest="wait_for_sync", default=True,
-              action="store_false", help="Don't wait for sync (DANGEROUS!)"),
-  make_option("--src-node", dest="src_node", help="Source node",
-              metavar="<node>"),
-  make_option("--src-dir", dest="src_dir", help="Source directory",
-              metavar="<dir>"),
-  make_option("--no-ip-check", dest="ip_check", default=True,
-              action="store_false", help="Don't check that the instance's IP"
-              " is alive"),
-  make_option("--iallocator", metavar="<NAME>",
-              help="Select nodes for the instance automatically using the"
-              " <NAME> iallocator plugin", default=None, type="string"),
-  make_option("--file-storage-dir", dest="file_storage_dir",
-              help="Relative path under default cluster-wide file storage dir"
-              " to store file-based disks", default=None,
-              metavar="<DIR>"),
-  make_option("--file-driver", dest="file_driver", help="Driver to use"
-              " for image files", default="loop", metavar="<DRIVER>"),
-  ikv_option("-H", "--hypervisor", dest="hypervisor",
-              help="Hypervisor and hypervisor options, in the format"
-              " hypervisor:option=value,option=value,...", default=None,
-              type="identkeyval"),
+  cli_option("--no-nics", default=False, action="store_true",
+             help="Do not create any network cards for the instance"),
+  cli_option("--no-wait-for-sync", dest="wait_for_sync", default=True,
+             action="store_false", help="Don't wait for sync (DANGEROUS!)"),
+  cli_option("--src-node", dest="src_node", help="Source node",
+             metavar="<node>"),
+  cli_option("--src-dir", dest="src_dir", help="Source directory",
+             metavar="<dir>"),
+  cli_option("--no-ip-check", dest="ip_check", default=True,
+             action="store_false", help="Don't check that the instance's IP"
+             " is alive"),
+  cli_option("-I", "--iallocator", metavar="<NAME>",
+             help="Select nodes for the instance automatically using the"
+             " <NAME> iallocator plugin", default=None, type="string"),
+  cli_option("--file-storage-dir", dest="file_storage_dir",
+             help="Relative path under default cluster-wide file storage dir"
+             " to store file-based disks", default=None,
+             metavar="<DIR>"),
+  cli_option("--file-driver", dest="file_driver", help="Driver to use"
+             " for image files", default="loop", metavar="<DRIVER>",
+             choices=list(constants.FILE_DRIVER)),
+  cli_option("-H", "--hypervisor", dest="hypervisor",
+             help="Hypervisor and hypervisor options, in the format"
+             " hypervisor:option=value,option=value,...", default=None,
+             type="identkeyval"),
   ]
 
 commands = {
-  'list': (PrintExportList, ARGS_NONE,
+  'list': (PrintExportList, [],
            [DEBUG_OPT,
-            make_option("--node", dest="nodes", default=[], action="append",
-                        help="List only backups stored on this node"
-                             " (can be used multiple times)"),
+            cli_option("--node", dest="nodes", default=[], action="append",
+                       help="List only backups stored on this node"
+                            " (can be used multiple times)"),
             ],
            "", "Lists instance exports available in the ganeti cluster"),
-  'export': (ExportInstance, ARGS_ONE,
+  'export': (ExportInstance, [ArgInstance(min=1, max=1)],
              [DEBUG_OPT, FORCE_OPT,
-              make_option("-n", "--node", dest="node", help="Target node",
-                          metavar="<node>"),
-              make_option("","--noshutdown", dest="shutdown",
-                          action="store_false", default=True,
-                          help="Don't shutdown the instance (unsafe)"), ],
+              cli_option("-n", "--node", dest="node", help="Target node",
+                         metavar="<node>"),
+              cli_option("","--noshutdown", dest="shutdown",
+                         action="store_false", default=True,
+                         help="Don't shutdown the instance (unsafe)"), ],
              "-n <target_node> [opts...] <name>",
              "Exports an instance to an image"),
-  'import': (ImportInstance, ARGS_ONE, import_opts,
+  'import': (ImportInstance, [ArgInstance(min=1, max=1)], import_opts,
              ("[...] -t disk-type -n node[:secondary-node]"
-              " --src-node node --src-dir dir"
               " <name>"),
              "Imports an instance from an exported image"),
-  'remove': (RemoveExport, ARGS_ONE,
+  'remove': (RemoveExport, [ArgUnknown(min=1, max=1)],
              [DEBUG_OPT],
              "<name>",
              "Remove exports of named instance from the filesystem."),
   }
 
+
 if __name__ == '__main__':
   sys.exit(GenericMain(commands))