Update gnt-instance batch-create for NIC params
[ganeti-local] / scripts / gnt-instance
index e2c6eca..3ac16d7 100755 (executable)
@@ -90,7 +90,7 @@ def _ExpandMultiNames(mode, names, client=None):
     if not names:
       raise errors.OpPrereqError("No node names passed")
     ndata = client.QueryNodes(names, ["name", "pinst_list", "sinst_list"],
-                              True)
+                              False)
     ipri = [row[1] for row in ndata]
     pri_names = list(itertools.chain(*ipri))
     isec = [row[2] for row in ndata]
@@ -116,7 +116,7 @@ def _ExpandMultiNames(mode, names, client=None):
   return inames
 
 
-def _ConfirmOperation(inames, text):
+def _ConfirmOperation(inames, text, extra=""):
   """Ask the user to confirm an operation on a list of instances.
 
   This function is used to request confirmation for doing an operation
@@ -133,8 +133,8 @@ def _ConfirmOperation(inames, text):
 
   """
   count = len(inames)
-  msg = ("The %s will operate on %d instances.\n"
-         "Do you want to continue?" % (text, count))
+  msg = ("The %s will operate on %d instances.\n%s"
+         "Do you want to continue?" % (text, count, extra))
   affected = ("\nAffected instances:\n" +
               "\n".join(["  %s" % name for name in inames]))
 
@@ -154,29 +154,6 @@ def _ConfirmOperation(inames, text):
   return choice
 
 
-def _TransformPath(user_input):
-  """Transform a user path into a canonical value.
-
-  This function transforms the a path passed as textual information
-  into the constants that the LU code expects.
-
-  """
-  if user_input:
-    if user_input.lower() == "default":
-      result_path = constants.VALUE_DEFAULT
-    elif user_input.lower() == "none":
-      result_path = constants.VALUE_NONE
-    else:
-      if not os.path.isabs(user_input):
-        raise errors.OpPrereqError("Path '%s' is not an absolute filename" %
-                                   user_input)
-      result_path = user_input
-  else:
-    result_path = constants.VALUE_DEFAULT
-
-  return result_path
-
-
 def _EnsureInstancesExist(client, names):
   """Check for and ensure the given instance names exist.
 
@@ -224,6 +201,7 @@ def ListInstances(opts, args):
       "oper_state": "Running",
       "oper_ram": "Memory", "disk_template": "Disk_template",
       "ip": "IP_address", "mac": "MAC_address",
+      "nic_mode": "NIC_Mode", "nic_link": "NIC_Link",
       "bridge": "Bridge",
       "sda_size": "Disk/0", "sdb_size": "Disk/1",
       "disk_usage": "DiskUsage",
@@ -245,6 +223,7 @@ def ListInstances(opts, args):
       "be/auto_balance": "Auto_balance",
       "disk.count": "Disks", "disk.sizes": "Disk_sizes",
       "nic.count": "NICs", "nic.ips": "NIC_IPs",
+      "nic.modes": "NIC_modes", "nic.links": "NIC_links",
       "nic.bridges": "NIC_bridges", "nic.macs": "NIC_MACs",
       }
   else:
@@ -254,8 +233,8 @@ def ListInstances(opts, args):
   numfields = ["be/memory", "oper_ram", "sd(a|b)_size", "be/vcpus",
                "serial_no", "(disk|nic)\.count", "disk\.size/.*"]
 
-  list_type_fields = ("tags", "disk.sizes",
-                      "nic.macs", "nic.ips", "nic.bridges")
+  list_type_fields = ("tags", "disk.sizes", "nic.macs", "nic.ips",
+                      "nic.modes", "nic.links", "nic.bridges")
   # change raw values to nicer strings
   for row in output:
     for idx, field in enumerate(selected_fields):
@@ -321,7 +300,7 @@ def AddInstance(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)
       nics[nidx] = ndict
   elif opts.no_nics:
@@ -417,9 +396,7 @@ def BatchCreate(opts, args):
                     "iallocator": None,
                     "primary_node": None,
                     "secondary_node": None,
-                    "ip": 'none',
-                    "mac": 'auto',
-                    "bridge": None,
+                    "nics": None,
                     "start": True,
                     "ip_check": True,
                     "hypervisor": None,
@@ -463,6 +440,8 @@ def BatchCreate(opts, args):
     ToStderr("Can't parse the instance definition file: %s" % str(err))
     return 1
 
+  jex = JobExecutor()
+
   # Iterate over the instances and do:
   #  * Populate the specs with default value
   #  * Validate the instance specs
@@ -485,11 +464,24 @@ def BatchCreate(opts, args):
                                    (elem, name, err))
       disks.append({"size": size})
 
-    nic0 = {'ip': specs['ip'], 'bridge': specs['bridge'], 'mac': specs['mac']}
-
     utils.ForceDictType(specs['backend'], constants.BES_PARAMETER_TYPES)
     utils.ForceDictType(hvparams, constants.HVS_PARAMETER_TYPES)
 
+    tmp_nics = []
+    for field in ('ip', 'mac', 'mode', 'link', 'bridge'):
+      if field in specs:
+        if not tmp_nics:
+          tmp_nics.append({})
+        tmp_nics[0][field] = specs[field]
+
+    if specs['nics'] is not None and tmp_nics:
+      raise errors.OpPrereqError("'nics' list incompatible with using"
+                                 " individual nic fields as well")
+    elif specs['nics'] is not None:
+      tmp_nics = specs['nics']
+    elif not tmp_nics:
+      tmp_nics = [{}]
+
     op = opcodes.OpCreateInstance(instance_name=name,
                                   disks=disks,
                                   disk_template=specs['template'],
@@ -497,7 +489,7 @@ def BatchCreate(opts, args):
                                   os_type=specs['os'],
                                   pnode=specs['primary_node'],
                                   snode=specs['secondary_node'],
-                                  nics=[nic0],
+                                  nics=tmp_nics,
                                   start=specs['start'],
                                   ip_check=specs['ip_check'],
                                   wait_for_sync=True,
@@ -508,7 +500,9 @@ def BatchCreate(opts, args):
                                   file_storage_dir=specs['file_storage_dir'],
                                   file_driver=specs['file_driver'])
 
-    ToStdout("%s: %s", name, cli.SendJob([op]))
+    jex.QueueJob(name, op)
+  # we never want to wait, just show the submitted job IDs
+  jex.WaitOrShow(False)
 
   return 0
 
@@ -524,8 +518,15 @@ def ReinstallInstance(opts, args):
   @return: the desired exit code
 
   """
-  instance_name = args[0]
+  # first, compute the desired name list
+  if opts.multi_mode is None:
+    opts.multi_mode = _SHUTDOWN_INSTANCES
+
+  inames = _ExpandMultiNames(opts.multi_mode, args)
+  if not inames:
+    raise errors.OpPrereqError("Selection filter does not match any instances")
 
+  # second, if requested, ask for an OS
   if opts.select_os is True:
     op = opcodes.OpDiagnoseOS(output_fields=["name", "valid"], names=[])
     result = SubmitOpCode(op)
@@ -543,27 +544,39 @@ def ReinstallInstance(opts, args):
       number = number + 1
 
     choices.append(('x', 'exit', 'Exit gnt-instance reinstall'))
-    selected = AskUser("Enter OS template name or number (or x to abort):",
+    selected = AskUser("Enter OS template number (or x to abort):",
                        choices)
 
     if selected == 'exit':
-      ToStdout("User aborted reinstall, exiting")
+      ToStderr("User aborted reinstall, exiting")
       return 1
 
     os_name = selected
   else:
     os_name = opts.os
 
-  if not opts.force:
-    usertext = ("This will reinstall the instance %s and remove"
-                " all data. Continue?") % instance_name
-    if not AskUser(usertext):
+  # third, get confirmation: multi-reinstall requires --force-multi
+  # *and* --force, single-reinstall just --force
+  multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
+  if multi_on:
+    warn_msg = "Note: this will remove *all* data for the below instances!\n"
+    if not ((opts.force_multi and opts.force) or
+            _ConfirmOperation(inames, "reinstall", extra=warn_msg)):
       return 1
+  else:
+    if not opts.force:
+      usertext = ("This will reinstall the instance %s and remove"
+                  " all data. Continue?") % inames[0]
+      if not AskUser(usertext):
+        return 1
+
+  jex = JobExecutor(verbose=multi_on)
+  for instance_name in inames:
+    op = opcodes.OpReinstallInstance(instance_name=instance_name,
+                                     os_type=os_name)
+    jex.QueueJob(instance_name, op)
 
-  op = opcodes.OpReinstallInstance(instance_name=instance_name,
-                                   os_type=os_name)
-  SubmitOrSend(op, opts)
-
+  jex.WaitOrShow(not opts.submit_only)
   return 0
 
 
@@ -709,8 +722,12 @@ def StartupInstance(opts, args):
   jex = cli.JobExecutor(verbose=multi_on, cl=cl)
   for name in inames:
     op = opcodes.OpStartupInstance(instance_name=name,
-                                   force=opts.force,
-                                   extra_args=opts.extra_args)
+                                   force=opts.force)
+    # do not add these parameters to the opcode unless they're defined
+    if opts.hvparams:
+      op.hvparams = opts.hvparams
+    if opts.beparams:
+      op.beparams = opts.beparams
     jex.QueueJob(name, op)
   jex.WaitOrShow(not opts.submit_only)
   return 0
@@ -1085,6 +1102,15 @@ def ShowInstanceConfig(opts, args):
   @return: the desired exit code
 
   """
+  if not args and not opts.show_all:
+    ToStderr("No instance selected."
+             " Please pass in --all if you want to query all instances.\n"
+             "Note that this can take a long time on a big cluster.")
+    return 1
+  elif args and opts.show_all:
+    ToStderr("Cannot use --all if you specify instance names.")
+    return 1
+
   retcode = 0
   op = opcodes.OpQueryInstanceData(instances=args, static=opts.static)
   result = SubmitOpCode(op)
@@ -1143,9 +1169,9 @@ def ShowInstanceConfig(opts, args):
     buf.write("    - memory: %dMiB\n" %
               instance["be_actual"][constants.BE_MEMORY])
     buf.write("    - NICs:\n")
-    for idx, (mac, ip, bridge) in enumerate(instance["nics"]):
-      buf.write("      - nic/%d: MAC: %s, IP: %s, bridge: %s\n" %
-                (idx, mac, ip, bridge))
+    for idx, (mac, ip, mode, link) in enumerate(instance["nics"]):
+      buf.write("      - nic/%d: MAC: %s, IP: %s, mode: %s, link: %s\n" %
+                (idx, mac, ip, mode, link))
     buf.write("  Disks:\n")
 
     for idx, device in enumerate(instance["disks"]):
@@ -1361,7 +1387,11 @@ commands = {
             make_option("-s", "--static", dest="static",
                         action="store_true", default=False,
                         help="Only show configuration data, not runtime data"),
-            ], "[-s] [<instance>...]",
+            make_option("--all", dest="show_all",
+                        default=False, action="store_true",
+                        help="Show info on all instances on the cluster."
+                        " This can take a long time to run, use wisely."),
+            ], "[-s] {--all | <instance>...}",
            "Show information on the specified instance(s)"),
   'list': (ListInstances, ARGS_ANY,
            [DEBUG_OPT, NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT, SYNC_OPT],
@@ -1369,13 +1399,16 @@ commands = {
            "Lists the instances and their status. The available fields are"
            " (see the man page for details): status, oper_state, oper_ram,"
            " name, os, pnode, snodes, admin_state, admin_ram, disk_template,"
-           " ip, mac, bridge, sda_size, sdb_size, vcpus, serial_no,"
+           " ip, mac, mode, link, sda_size, sdb_size, vcpus, serial_no,"
            " hypervisor."
            " The default field"
            " list is (in order): %s." % ", ".join(_LIST_DEF_FIELDS),
            ),
-  'reinstall': (ReinstallInstance, ARGS_ONE,
+  'reinstall': (ReinstallInstance, ARGS_ANY,
                 [DEBUG_OPT, FORCE_OPT, os_opt,
+                 m_force_multi,
+                 m_node_opt, m_pri_node_opt, m_sec_node_opt,
+                 m_clust_opt, m_inst_opt,
                  make_option("--select-os", dest="select_os",
                              action="store_true", default=False,
                              help="Interactive OS reinstall, lists available"
@@ -1417,9 +1450,9 @@ commands = {
                                  help=("Replace the disk(s) on the secondary"
                                        " node (only for the drbd template)")),
                      make_option("--disks", dest="disks", default=None,
-                                 help=("Comma-separated list of disks"
-                                       " to replace (e.g. sda) (optional,"
-                                       " defaults to all disks")),
+                                 help="Comma-separated list of disks"
+                                 " indices to replace (e.g. 0,2) (optional,"
+                                 " defaults to all disks)"),
                      make_option("-I", "--iallocator", metavar="<NAME>",
                                  help="Select new secondary for the instance"
                                  " automatically using the"
@@ -1457,20 +1490,20 @@ commands = {
                "<instance>", "Stops an instance"),
   'startup': (StartupInstance, ARGS_ANY,
               [DEBUG_OPT, FORCE_OPT, m_force_multi,
-               make_option("-e", "--extra", dest="extra_args",
-                           help="Extra arguments for the instance's kernel",
-                           default=None, type="string", metavar="<PARAMS>"),
                m_node_opt, m_pri_node_opt, m_sec_node_opt,
                m_clust_opt, m_inst_opt,
                SUBMIT_OPT,
+               keyval_option("-H", "--hypervisor", type="keyval",
+                             default={}, dest="hvparams",
+                             help="Temporary hypervisor parameters"),
+               keyval_option("-B", "--backend", type="keyval",
+                             default={}, dest="beparams",
+                             help="Temporary backend parameters"),
                ],
-            "<instance>", "Starts an instance"),
+              "<instance>", "Starts an instance"),
 
   'reboot': (RebootInstance, ARGS_ANY,
               [DEBUG_OPT, m_force_multi,
-               make_option("-e", "--extra", dest="extra_args",
-                           help="Extra arguments for the instance's kernel",
-                           default=None, type="string", metavar="<PARAMS>"),
                make_option("-t", "--type", dest="reboot_type",
                            help="Type of reboot: soft/hard/full",
                            default=constants.INSTANCE_REBOOT_HARD,