check-python-code: Report EOL whitespace
[ganeti-local] / scripts / gnt-instance
index fd8a455..4980614 100755 (executable)
@@ -27,7 +27,7 @@ import sys
 import os
 import itertools
 import simplejson
 import os
 import itertools
 import simplejson
-from optparse import make_option
+import time
 from cStringIO import StringIO
 
 from ganeti.cli import *
 from cStringIO import StringIO
 
 from ganeti.cli import *
@@ -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"],
     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]
     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
 
 
   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
   """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)
 
   """
   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]))
 
   affected = ("\nAffected instances:\n" +
               "\n".join(["  %s" % name for name in inames]))
 
@@ -154,36 +154,13 @@ def _ConfirmOperation(inames, text):
   return choice
 
 
   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.
 
   This function will raise an OpPrereqError in case they don't
   exist. Otherwise it will exit cleanly.
 
 def _EnsureInstancesExist(client, names):
   """Check for and ensure the given instance names exist.
 
   This function will raise an OpPrereqError in case they don't
   exist. Otherwise it will exit cleanly.
 
-  @type client: L{luxi.Client}
+  @type client: L{ganeti.luxi.Client}
   @param client: the client to use for the query
   @type names: list
   @param names: the list of instance names to query
   @param client: the client to use for the query
   @type names: list
   @param names: the list of instance names to query
@@ -224,6 +201,7 @@ def ListInstances(opts, args):
       "oper_state": "Running",
       "oper_ram": "Memory", "disk_template": "Disk_template",
       "ip": "IP_address", "mac": "MAC_address",
       "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",
       "bridge": "Bridge",
       "sda_size": "Disk/0", "sdb_size": "Disk/1",
       "disk_usage": "DiskUsage",
@@ -242,10 +220,13 @@ def ListInstances(opts, args):
       "hvparams": "Hypervisor_parameters",
       "be/memory": "Configured_memory",
       "be/vcpus": "VCPUs",
       "hvparams": "Hypervisor_parameters",
       "be/memory": "Configured_memory",
       "be/vcpus": "VCPUs",
+      "vcpus": "VCPUs",
       "be/auto_balance": "Auto_balance",
       "disk.count": "Disks", "disk.sizes": "Disk_sizes",
       "nic.count": "NICs", "nic.ips": "NIC_IPs",
       "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",
       "nic.bridges": "NIC_bridges", "nic.macs": "NIC_MACs",
+      "ctime": "CTime", "mtime": "MTime",
       }
   else:
     headers = None
       }
   else:
     headers = None
@@ -254,8 +235,8 @@ def ListInstances(opts, args):
   numfields = ["be/memory", "oper_ram", "sd(a|b)_size", "be/vcpus",
                "serial_no", "(disk|nic)\.count", "disk\.size/.*"]
 
   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):
   # change raw values to nicer strings
   for row in output:
     for idx, field in enumerate(selected_fields):
@@ -280,6 +261,8 @@ def ListInstances(opts, args):
       elif field == "sda_size" or field == "sdb_size":
         if val is None:
           val = "N/A"
       elif field == "sda_size" or field == "sdb_size":
         if val is None:
           val = "N/A"
+      elif field == "ctime" or field == "mtime":
+        val = utils.FormatTime(val)
       elif field in list_type_fields:
         val = ",".join(str(item) for item in val)
       elif val is None:
       elif field in list_type_fields:
         val = ",".join(str(item) for item in val)
       elif val is None:
@@ -321,8 +304,11 @@ def AddInstance(opts, args):
     except ValueError, err:
       raise errors.OpPrereqError("Invalid NIC index passed: %s" % str(err))
     nics = [{}] * nic_max
     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)
       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
       nics[nidx] = ndict
   elif opts.no_nics:
     # no nics
@@ -351,7 +337,10 @@ def AddInstance(opts, args):
     disks = [{}] * disk_max
     for didx, ddict in opts.disks:
       didx = int(didx)
     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"])
         raise errors.OpPrereqError("Missing size for disk %d" % didx)
       try:
         ddict["size"] = utils.ParseUnit(ddict["size"])
@@ -417,9 +406,7 @@ def BatchCreate(opts, args):
                     "iallocator": None,
                     "primary_node": None,
                     "secondary_node": None,
                     "iallocator": None,
                     "primary_node": None,
                     "secondary_node": None,
-                    "ip": 'none',
-                    "mac": 'auto',
-                    "bridge": None,
+                    "nics": None,
                     "start": True,
                     "ip_check": True,
                     "hypervisor": None,
                     "start": True,
                     "ip_check": True,
                     "hypervisor": None,
@@ -456,13 +443,13 @@ def BatchCreate(opts, args):
 
   json_filename = args[0]
   try:
 
   json_filename = args[0]
   try:
-    fd = open(json_filename, 'r')
-    instance_data = simplejson.load(fd)
-    fd.close()
+    instance_data = simplejson.loads(utils.ReadFile(json_filename))
   except Exception, err:
     ToStderr("Can't parse the instance definition file: %s" % str(err))
     return 1
 
   except Exception, err:
     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
   # Iterate over the instances and do:
   #  * Populate the specs with default value
   #  * Validate the instance specs
@@ -485,11 +472,24 @@ def BatchCreate(opts, args):
                                    (elem, name, err))
       disks.append({"size": size})
 
                                    (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)
 
     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'],
     op = opcodes.OpCreateInstance(instance_name=name,
                                   disks=disks,
                                   disk_template=specs['template'],
@@ -497,7 +497,7 @@ def BatchCreate(opts, args):
                                   os_type=specs['os'],
                                   pnode=specs['primary_node'],
                                   snode=specs['secondary_node'],
                                   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,
                                   start=specs['start'],
                                   ip_check=specs['ip_check'],
                                   wait_for_sync=True,
@@ -508,7 +508,9 @@ def BatchCreate(opts, args):
                                   file_storage_dir=specs['file_storage_dir'],
                                   file_driver=specs['file_driver'])
 
                                   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
 
 
   return 0
 
@@ -524,8 +526,15 @@ def ReinstallInstance(opts, args):
   @return: the desired exit code
 
   """
   @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)
   if opts.select_os is True:
     op = opcodes.OpDiagnoseOS(output_fields=["name", "valid"], names=[])
     result = SubmitOpCode(op)
@@ -543,27 +552,39 @@ def ReinstallInstance(opts, args):
       number = number + 1
 
     choices.append(('x', 'exit', 'Exit gnt-instance reinstall'))
       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':
                        choices)
 
     if selected == 'exit':
-      ToStdout("User aborted reinstall, exiting")
+      ToStderr("User aborted reinstall, exiting")
       return 1
 
     os_name = selected
   else:
     os_name = opts.os
 
       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
       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
 
 
   return 0
 
 
@@ -631,7 +652,8 @@ def ActivateDisks(opts, args):
 
   """
   instance_name = args[0]
 
   """
   instance_name = args[0]
-  op = opcodes.OpActivateInstanceDisks(instance_name=instance_name)
+  op = opcodes.OpActivateInstanceDisks(instance_name=instance_name,
+                                       ignore_size=opts.ignore_size)
   disks_info = SubmitOrSend(op, opts)
   for host, iname, nname in disks_info:
     ToStdout("%s:%s:%s", host, iname, nname)
   disks_info = SubmitOrSend(op, opts)
   for host, iname, nname in disks_info:
     ToStdout("%s:%s:%s", host, iname, nname)
@@ -639,7 +661,7 @@ def ActivateDisks(opts, args):
 
 
 def DeactivateDisks(opts, args):
 
 
 def DeactivateDisks(opts, args):
-  """Deactivate an instance's disks..
+  """Deactivate an instance's disks.
 
   This function takes the instance name, looks for its primary node
   and the tries to shutdown its block devices on that node.
 
   This function takes the instance name, looks for its primary node
   and the tries to shutdown its block devices on that node.
@@ -657,6 +679,32 @@ def DeactivateDisks(opts, args):
   return 0
 
 
   return 0
 
 
+def RecreateDisks(opts, args):
+  """Recreate an instance's disks.
+
+  @param opts: the command line options selected by the user
+  @type args: list
+  @param args: should contain only one element, the instance name
+  @rtype: int
+  @return: the desired exit code
+
+  """
+  instance_name = args[0]
+  if opts.disks:
+    try:
+      opts.disks = [int(v) for v in opts.disks.split(",")]
+    except (ValueError, TypeError), err:
+      ToStderr("Invalid disks value: %s" % str(err))
+      return 1
+  else:
+    opts.disks = []
+
+  op = opcodes.OpRecreateInstanceDisks(instance_name=instance_name,
+                                       disks=opts.disks)
+  SubmitOrSend(op, opts)
+  return 0
+
+
 def GrowDisk(opts, args):
   """Grow an instance's disks.
 
 def GrowDisk(opts, args):
   """Grow an instance's disks.
 
@@ -709,8 +757,12 @@ def StartupInstance(opts, args):
   jex = cli.JobExecutor(verbose=multi_on, cl=cl)
   for name in inames:
     op = opcodes.OpStartupInstance(instance_name=name,
   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
     jex.QueueJob(name, op)
   jex.WaitOrShow(not opts.submit_only)
   return 0
@@ -802,15 +854,20 @@ def ReplaceDisks(opts, args):
       disks = [int(i) for i in opts.disks.split(",")]
     except ValueError, err:
       raise errors.OpPrereqError("Invalid disk index passed: %s" % str(err))
       disks = [int(i) for i in opts.disks.split(",")]
     except ValueError, err:
       raise errors.OpPrereqError("Invalid disk index passed: %s" % str(err))
-  cnt = [opts.on_primary, opts.on_secondary,
+  cnt = [opts.on_primary, opts.on_secondary, opts.auto,
          new_2ndary is not None, iallocator is not None].count(True)
   if cnt != 1:
          new_2ndary is not None, iallocator is not None].count(True)
   if cnt != 1:
-    raise errors.OpPrereqError("One and only one of the -p, -s, -n and -i"
+    raise errors.OpPrereqError("One and only one of the -p, -s, -a, -n and -i"
                                " options must be passed")
   elif opts.on_primary:
     mode = constants.REPLACE_DISK_PRI
   elif opts.on_secondary:
     mode = constants.REPLACE_DISK_SEC
                                " options must be passed")
   elif opts.on_primary:
     mode = constants.REPLACE_DISK_PRI
   elif opts.on_secondary:
     mode = constants.REPLACE_DISK_SEC
+  elif opts.auto:
+    mode = constants.REPLACE_DISK_AUTO
+    if disks:
+      raise errors.OpPrereqError("Cannot specify disks when using automatic"
+                                 " mode")
   elif new_2ndary is not None or iallocator is not None:
     # replace secondary
     mode = constants.REPLACE_DISK_CHG
   elif new_2ndary is not None or iallocator is not None:
     # replace secondary
     mode = constants.REPLACE_DISK_CHG
@@ -892,6 +949,33 @@ def MigrateInstance(opts, args):
   return 0
 
 
   return 0
 
 
+def MoveInstance(opts, args):
+  """Move an instance.
+
+  @param opts: the command line options selected by the user
+  @type args: list
+  @param args: should contain only one element, the instance name
+  @rtype: int
+  @return: the desired exit code
+
+  """
+  cl = GetClient()
+  instance_name = args[0]
+  force = opts.force
+
+  if not force:
+    usertext = ("Instance %s will be moved."
+                " This requires a shutdown of the instance. Continue?" %
+                (instance_name,))
+    if not AskUser(usertext):
+      return 1
+
+  op = opcodes.OpMoveInstance(instance_name=instance_name,
+                              target_node=opts.target_node)
+  SubmitOrSend(op, opts, cl=cl)
+  return 0
+
+
 def ConnectToInstanceConsole(opts, args):
   """Connect to the console of an instance.
 
 def ConnectToInstanceConsole(opts, args):
   """Connect to the console of an instance.
 
@@ -971,7 +1055,7 @@ def _FormatBlockDevInfo(idx, top_level, dev, static):
     if not status:
       return "not active"
     txt = ""
     if not status:
       return "not active"
     txt = ""
-    (path, major, minor, syncp, estt, degr, ldisk) = status
+    (path, major, minor, syncp, estt, degr, ldisk_status) = status
     if major is None:
       major_string = "N/A"
     else:
     if major is None:
       major_string = "N/A"
     else:
@@ -996,13 +1080,15 @@ def _FormatBlockDevInfo(idx, top_level, dev, static):
         degr_text = "*DEGRADED*"
       else:
         degr_text = "ok"
         degr_text = "*DEGRADED*"
       else:
         degr_text = "ok"
-      if ldisk:
+      if ldisk_status == constants.LDS_FAULTY:
         ldisk_text = " *MISSING DISK*"
         ldisk_text = " *MISSING DISK*"
+      elif ldisk_status == constants.LDS_UNKNOWN:
+        ldisk_text = " *UNCERTAIN STATE*"
       else:
         ldisk_text = ""
       txt += (" %s, status %s%s" % (sync_text, degr_text, ldisk_text))
     elif dtype == constants.LD_LV:
       else:
         ldisk_text = ""
       txt += (" %s, status %s%s" % (sync_text, degr_text, ldisk_text))
     elif dtype == constants.LD_LV:
-      if ldisk:
+      if ldisk_status == constants.LDS_FAULTY:
         ldisk_text = " *FAILED* (failed drive?)"
       else:
         ldisk_text = ""
         ldisk_text = " *FAILED* (failed drive?)"
       else:
         ldisk_text = ""
@@ -1017,7 +1103,11 @@ def _FormatBlockDevInfo(idx, top_level, dev, static):
       txt = "disk %d" % idx
   else:
     txt = "child %d" % idx
       txt = "disk %d" % idx
   else:
     txt = "child %d" % idx
-  d1 = ["- %s: %s" % (txt, dev["dev_type"])]
+  if isinstance(dev["size"], int):
+    nice_size = utils.FormatUnit(dev["size"], "h")
+  else:
+    nice_size = dev["size"]
+  d1 = ["- %s: %s, size %s" % (txt, dev["dev_type"], nice_size)]
   data = []
   if top_level:
     data.append(("access mode", dev["mode"]))
   data = []
   if top_level:
     data.append(("access mode", dev["mode"]))
@@ -1074,6 +1164,7 @@ def _FormatList(buf, data, indent_level):
     elif isinstance(elem, list):
       _FormatList(buf, elem, indent_level+1)
 
     elif isinstance(elem, list):
       _FormatList(buf, elem, indent_level+1)
 
+
 def ShowInstanceConfig(opts, args):
   """Compute instance run-time status.
 
 def ShowInstanceConfig(opts, args):
   """Compute instance run-time status.
 
@@ -1085,6 +1176,15 @@ def ShowInstanceConfig(opts, args):
   @return: the desired exit code
 
   """
   @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)
   retcode = 0
   op = opcodes.OpQueryInstanceData(instances=args, static=opts.static)
   result = SubmitOpCode(op)
@@ -1097,6 +1197,9 @@ def ShowInstanceConfig(opts, args):
   for instance_name in result:
     instance = result[instance_name]
     buf.write("Instance name: %s\n" % instance["name"])
   for instance_name in result:
     instance = result[instance_name]
     buf.write("Instance name: %s\n" % instance["name"])
+    buf.write("Serial number: %s\n" % instance["serial_no"])
+    buf.write("Creation time: %s\n" % utils.FormatTime(instance["ctime"]))
+    buf.write("Modification time: %s\n" % utils.FormatTime(instance["mtime"]))
     buf.write("State: configured to be %s" % instance["config_state"])
     if not opts.static:
       buf.write(", actual state is %s" % instance["run_state"])
     buf.write("State: configured to be %s" % instance["config_state"])
     if not opts.static:
       buf.write(", actual state is %s" % instance["run_state"])
@@ -1110,51 +1213,42 @@ def ShowInstanceConfig(opts, args):
     if instance.has_key("network_port"):
       buf.write("  Allocated network port: %s\n" % instance["network_port"])
     buf.write("  Hypervisor: %s\n" % instance["hypervisor"])
     if instance.has_key("network_port"):
       buf.write("  Allocated network port: %s\n" % instance["network_port"])
     buf.write("  Hypervisor: %s\n" % instance["hypervisor"])
-    if instance["hypervisor"] == constants.HT_XEN_PVM:
-      hvattrs = ((constants.HV_KERNEL_PATH, "kernel path"),
-                 (constants.HV_INITRD_PATH, "initrd path"))
-    elif instance["hypervisor"] == constants.HT_XEN_HVM:
-      hvattrs = ((constants.HV_BOOT_ORDER, "boot order"),
-                 (constants.HV_ACPI, "ACPI"),
-                 (constants.HV_PAE, "PAE"),
-                 (constants.HV_CDROM_IMAGE_PATH, "virtual CDROM"),
-                 (constants.HV_NIC_TYPE, "NIC type"),
-                 (constants.HV_DISK_TYPE, "Disk type"),
-                 (constants.HV_VNC_BIND_ADDRESS, "VNC bind address"),
-                 )
-      # custom console information for HVM
-      vnc_bind_address = instance["hv_actual"][constants.HV_VNC_BIND_ADDRESS]
-      if vnc_bind_address == constants.BIND_ADDRESS_GLOBAL:
-        vnc_console_port = "%s:%s" % (instance["pnode"],
-                                      instance["network_port"])
-      elif vnc_bind_address == constants.LOCALHOST_IP_ADDRESS:
-        vnc_console_port = "%s:%s on node %s" % (vnc_bind_address,
-                                                 instance["network_port"],
-                                                 instance["pnode"])
+
+    # custom VNC console information
+    vnc_bind_address = instance["hv_actual"].get(constants.HV_VNC_BIND_ADDRESS,
+                                                 None)
+    if vnc_bind_address:
+      port = instance["network_port"]
+      display = int(port) - constants.VNC_BASE_PORT
+      if display > 0 and vnc_bind_address == constants.BIND_ADDRESS_GLOBAL:
+        vnc_console_port = "%s:%s (display %s)" % (instance["pnode"],
+                                                   port,
+                                                   display)
+      elif display > 0 and utils.IsValidIP(vnc_bind_address):
+        vnc_console_port = ("%s:%s (node %s) (display %s)" %
+                             (vnc_bind_address, port,
+                              instance["pnode"], display))
       else:
       else:
-        vnc_console_port = "%s:%s" % (vnc_bind_address,
-                                      instance["network_port"])
+        # vnc bind address is a file
+        vnc_console_port = "%s:%s" % (instance["pnode"],
+                                      vnc_bind_address)
       buf.write("    - console connection: vnc to %s\n" % vnc_console_port)
 
       buf.write("    - console connection: vnc to %s\n" % vnc_console_port)
 
-    else:
-      # auto-handle other hypervisor types
-      hvattrs = [(key, key) for key in instance["hv_actual"]]
-
-    for key, desc in hvattrs:
+    for key in instance["hv_actual"]:
       if key in instance["hv_instance"]:
         val = instance["hv_instance"][key]
       else:
         val = "default (%s)" % instance["hv_actual"][key]
       if key in instance["hv_instance"]:
         val = instance["hv_instance"][key]
       else:
         val = "default (%s)" % instance["hv_actual"][key]
-      buf.write("    - %s: %s\n" % (desc, val))
+      buf.write("    - %s: %s\n" % (key, val))
     buf.write("  Hardware:\n")
     buf.write("    - VCPUs: %d\n" %
               instance["be_actual"][constants.BE_VCPUS])
     buf.write("    - memory: %dMiB\n" %
               instance["be_actual"][constants.BE_MEMORY])
     buf.write("    - NICs:\n")
     buf.write("  Hardware:\n")
     buf.write("    - VCPUs: %d\n" %
               instance["be_actual"][constants.BE_VCPUS])
     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"]):
     buf.write("  Disks:\n")
 
     for idx, device in enumerate(instance["disks"]):
@@ -1235,282 +1329,333 @@ def SetInstanceParams(opts, args):
 
 
 # options used in more than one cmd
 
 
 # options used in more than one cmd
-node_opt = make_option("-n", "--node", dest="node", help="Target node",
-                       metavar="<node>")
+node_opt = cli_option("-n", "--node", dest="node", help="Target node",
+                      metavar="<node>",
+                      completion_suggest=OPT_COMPL_ONE_NODE)
 
 os_opt = cli_option("-o", "--os-type", dest="os", help="What OS to run",
 
 os_opt = cli_option("-o", "--os-type", dest="os", help="What OS to run",
-                    metavar="<os>")
+                    metavar="<os>",
+                    completion_suggest=OPT_COMPL_ONE_OS)
 
 # multi-instance selection options
 
 # multi-instance selection options
-m_force_multi = make_option("--force-multiple", dest="force_multi",
-                            help="Do not ask for confirmation when more than"
-                            " one instance is affected",
-                            action="store_true", default=False)
+m_force_multi = cli_option("--force-multiple", dest="force_multi",
+                           help="Do not ask for confirmation when more than"
+                           " one instance is affected",
+                           action="store_true", default=False)
 
 
-m_pri_node_opt = make_option("--primary", dest="multi_mode",
-                             help="Filter by nodes (primary only)",
-                             const=_SHUTDOWN_NODES_PRI, action="store_const")
+m_pri_node_opt = cli_option("--primary", dest="multi_mode",
+                            help="Filter by nodes (primary only)",
+                            const=_SHUTDOWN_NODES_PRI, action="store_const")
 
 
-m_sec_node_opt = make_option("--secondary", dest="multi_mode",
-                             help="Filter by nodes (secondary only)",
-                             const=_SHUTDOWN_NODES_SEC, action="store_const")
+m_sec_node_opt = cli_option("--secondary", dest="multi_mode",
+                            help="Filter by nodes (secondary only)",
+                            const=_SHUTDOWN_NODES_SEC, action="store_const")
 
 
-m_node_opt = make_option("--node", dest="multi_mode",
-                         help="Filter by nodes (primary and secondary)",
-                         const=_SHUTDOWN_NODES_BOTH, action="store_const")
+m_node_opt = cli_option("--node", dest="multi_mode",
+                        help="Filter by nodes (primary and secondary)",
+                        const=_SHUTDOWN_NODES_BOTH, action="store_const")
 
 
-m_clust_opt = make_option("--all", dest="multi_mode",
-                          help="Select all instances in the cluster",
-                          const=_SHUTDOWN_CLUSTER, action="store_const")
+m_clust_opt = cli_option("--all", dest="multi_mode",
+                         help="Select all instances in the cluster",
+                         const=_SHUTDOWN_CLUSTER, action="store_const")
 
 
-m_inst_opt = make_option("--instance", dest="multi_mode",
-                         help="Filter by instance name [default]",
-                         const=_SHUTDOWN_INSTANCES, action="store_const")
+m_inst_opt = cli_option("--instance", dest="multi_mode",
+                        help="Filter by instance name [default]",
+                        const=_SHUTDOWN_INSTANCES, action="store_const")
 
 
 # this is defined separately due to readability only
 add_opts = [
   DEBUG_OPT,
 
 
 # this is defined separately due to readability only
 add_opts = [
   DEBUG_OPT,
-  make_option("-n", "--node", dest="node",
-              help="Target node and optional secondary node",
-              metavar="<pnode>[:<snode>]"),
+  cli_option("-n", "--node", dest="node",
+             help="Target node and optional secondary node",
+             metavar="<pnode>[:<snode>]",
+             completion_suggest=OPT_COMPL_INST_ADD_NODES),
   os_opt,
   os_opt,
-  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 or drbd)",
-              default=None, metavar="TEMPL"),
+  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 or drbd)",
+             default=None, metavar="TEMPL",
+             choices=list(constants.DISK_TEMPLATES)),
   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("-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>"),
-  ikv_option("--disk", help="Disk information",
+  cli_option("--disk", help="Disk information",
              default=[], dest="disks",
              action="append",
              type="identkeyval"),
              default=[], dest="disks",
              action="append",
              type="identkeyval"),
-  ikv_option("--net", help="NIC information",
+  cli_option("--net", help="NIC information",
              default=[], dest="nics",
              action="append",
              type="identkeyval"),
              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("--no-start", dest="start", default=True,
-              action="store_false", help="Don't start the instance after"
-              " creation"),
-  make_option("--no-ip-check", dest="ip_check", default=True,
-              action="store_false", help="Don't check that the instance's IP"
-              " is alive (only valid with --no-start)"),
-  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>"),
-  make_option("-I", "--iallocator", metavar="<NAME>",
-              help="Select nodes for the instance automatically using the"
-              " <NAME> iallocator plugin", default=None, type="string"),
-  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("--no-start", dest="start", default=True,
+             action="store_false", help="Don't start the instance after"
+             " creation"),
+  cli_option("--no-ip-check", dest="ip_check", default=True,
+             action="store_false", help="Don't check that the instance's IP"
+             " is alive (only valid with --no-start)"),
+  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("-I", "--iallocator", metavar="<NAME>",
+             help="Select nodes for the instance automatically using the"
+             " <NAME> iallocator plugin", default=None, type="string",
+             completion_suggest=OPT_COMPL_ONE_IALLOCATOR),
+  cli_option("-H", "--hypervisor", dest="hypervisor",
+             help="Hypervisor and hypervisor options, in the format"
+             " hypervisor:option=value,option=value,...", default=None,
+             type="identkeyval"),
   SUBMIT_OPT,
   ]
 
 commands = {
   SUBMIT_OPT,
   ]
 
 commands = {
-  'add': (AddInstance, ARGS_ONE, add_opts,
+  'add': (AddInstance, [ArgHost(min=1, max=1)], add_opts,
           "[...] -t disk-type -n node[:secondary-node] -o os-type <name>",
           "Creates and adds a new instance to the cluster"),
           "[...] -t disk-type -n node[:secondary-node] -o os-type <name>",
           "Creates and adds a new instance to the cluster"),
-  'batch-create': (BatchCreate, ARGS_ONE,
+  'batch-create': (BatchCreate, [ArgFile(min=1, max=1)],
                    [DEBUG_OPT],
                    "<instances_file.json>",
                    "Create a bunch of instances based on specs in the file."),
                    [DEBUG_OPT],
                    "<instances_file.json>",
                    "Create a bunch of instances based on specs in the file."),
-  'console': (ConnectToInstanceConsole, ARGS_ONE,
+  'console': (ConnectToInstanceConsole, ARGS_ONE_INSTANCE,
               [DEBUG_OPT,
               [DEBUG_OPT,
-               make_option("--show-cmd", dest="show_command",
-                           action="store_true", default=False,
-                           help=("Show command instead of executing it"))],
+               cli_option("--show-cmd", dest="show_command",
+                          action="store_true", default=False,
+                          help=("Show command instead of executing it"))],
               "[--show-cmd] <instance>",
               "Opens a console on the specified instance"),
               "[--show-cmd] <instance>",
               "Opens a console on the specified instance"),
-  'failover': (FailoverInstance, ARGS_ONE,
+  'failover': (FailoverInstance, ARGS_ONE_INSTANCE,
                [DEBUG_OPT, FORCE_OPT,
                [DEBUG_OPT, FORCE_OPT,
-                make_option("--ignore-consistency", dest="ignore_consistency",
-                            action="store_true", default=False,
-                            help="Ignore the consistency of the disks on"
-                            " the secondary"),
+                cli_option("--ignore-consistency", dest="ignore_consistency",
+                           action="store_true", default=False,
+                           help="Ignore the consistency of the disks on"
+                           " the secondary"),
                 SUBMIT_OPT,
                 ],
                "[-f] <instance>",
                "Stops the instance and starts it on the backup node, using"
                " the remote mirror (only for instances of type drbd)"),
                 SUBMIT_OPT,
                 ],
                "[-f] <instance>",
                "Stops the instance and starts it on the backup node, using"
                " the remote mirror (only for instances of type drbd)"),
-  'migrate': (MigrateInstance, ARGS_ONE,
+  'migrate': (MigrateInstance, ARGS_ONE_INSTANCE,
                [DEBUG_OPT, FORCE_OPT,
                [DEBUG_OPT, FORCE_OPT,
-                make_option("--non-live", dest="live",
-                            default=True, action="store_false",
-                            help="Do a non-live migration (this usually means"
-                            " freeze the instance, save the state,"
-                            " transfer and only then resume running on the"
-                            " secondary node)"),
-                make_option("--cleanup", dest="cleanup",
-                            default=False, action="store_true",
-                            help="Instead of performing the migration, try to"
-                            " recover from a failed cleanup. This is safe"
-                            " to run even if the instance is healthy, but it"
-                            " will create extra replication traffic and "
-                            " disrupt briefly the replication (like during the"
-                            " migration"),
+                cli_option("--non-live", dest="live",
+                           default=True, action="store_false",
+                           help="Do a non-live migration (this usually means"
+                           " freeze the instance, save the state,"
+                           " transfer and only then resume running on the"
+                           " secondary node)"),
+                cli_option("--cleanup", dest="cleanup",
+                           default=False, action="store_true",
+                           help="Instead of performing the migration, try to"
+                           " recover from a failed cleanup. This is safe"
+                           " to run even if the instance is healthy, but it"
+                           " will create extra replication traffic and "
+                           " disrupt briefly the replication (like during the"
+                           " migration"),
                 ],
                "[-f] <instance>",
                "Migrate instance to its secondary node"
                " (only for instances of type drbd)"),
                 ],
                "[-f] <instance>",
                "Migrate instance to its secondary node"
                " (only for instances of type drbd)"),
-  'info': (ShowInstanceConfig, ARGS_ANY,
+  'move': (MoveInstance, ARGS_ONE_INSTANCE,
+           [DEBUG_OPT, FORCE_OPT, SUBMIT_OPT,
+            cli_option("-n", "--new-node", dest="target_node",
+                       help="Destinattion node", metavar="NODE",
+                       default=None,
+                       completion_suggest=OPT_COMPL_ONE_NODE),
+            ],
+           "[-f] <instance>",
+           "Move instance to an arbitrary node"
+           " (only for instances of type file and lv)"),
+  'info': (ShowInstanceConfig, ARGS_MANY_INSTANCES,
            [DEBUG_OPT,
            [DEBUG_OPT,
-            make_option("-s", "--static", dest="static",
-                        action="store_true", default=False,
-                        help="Only show configuration data, not runtime data"),
-            ], "[-s] [<instance>...]",
+            cli_option("-s", "--static", dest="static",
+                       action="store_true", default=False,
+                       help="Only show configuration data, not runtime data"),
+            cli_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)"),
            "Show information on the specified instance(s)"),
-  'list': (ListInstances, ARGS_ANY,
+  'list': (ListInstances, ARGS_MANY_INSTANCES,
            [DEBUG_OPT, NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT, SYNC_OPT],
            "[<instance>...]",
            "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,"
            [DEBUG_OPT, NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT, SYNC_OPT],
            "[<instance>...]",
            "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),
            ),
            " hypervisor."
            " The default field"
            " list is (in order): %s." % ", ".join(_LIST_DEF_FIELDS),
            ),
-  'reinstall': (ReinstallInstance, ARGS_ONE,
+  'reinstall': (ReinstallInstance, [ArgInstance(min=1)],
                 [DEBUG_OPT, FORCE_OPT, os_opt,
                 [DEBUG_OPT, FORCE_OPT, os_opt,
-                 make_option("--select-os", dest="select_os",
-                             action="store_true", default=False,
-                             help="Interactive OS reinstall, lists available"
-                             " OS templates for selection"),
+                 m_force_multi,
+                 m_node_opt, m_pri_node_opt, m_sec_node_opt,
+                 m_clust_opt, m_inst_opt,
+                 cli_option("--select-os", dest="select_os",
+                            action="store_true", default=False,
+                            help="Interactive OS reinstall, lists available"
+                            " OS templates for selection"),
                  SUBMIT_OPT,
                  ],
                 "[-f] <instance>", "Reinstall a stopped instance"),
                  SUBMIT_OPT,
                  ],
                 "[-f] <instance>", "Reinstall a stopped instance"),
-  'remove': (RemoveInstance, ARGS_ONE,
+  'remove': (RemoveInstance, ARGS_ONE_INSTANCE,
              [DEBUG_OPT, FORCE_OPT,
              [DEBUG_OPT, FORCE_OPT,
-              make_option("--ignore-failures", dest="ignore_failures",
-                          action="store_true", default=False,
-                          help=("Remove the instance from the cluster even"
-                                " if there are failures during the removal"
-                                " process (shutdown, disk removal, etc.)")),
+              cli_option("--ignore-failures", dest="ignore_failures",
+                         action="store_true", default=False,
+                         help=("Remove the instance from the cluster even"
+                               " if there are failures during the removal"
+                               " process (shutdown, disk removal, etc.)")),
               SUBMIT_OPT,
               ],
              "[-f] <instance>", "Shuts down the instance and removes it"),
               SUBMIT_OPT,
               ],
              "[-f] <instance>", "Shuts down the instance and removes it"),
-  'rename': (RenameInstance, ARGS_FIXED(2),
+  'rename': (RenameInstance,
+             [ArgInstance(min=1, max=1), ArgHost(min=1, max=1)],
              [DEBUG_OPT,
              [DEBUG_OPT,
-              make_option("--no-ip-check", dest="ignore_ip",
-                          help="Do not check that the IP of the new name"
-                          " is alive",
-                          default=False, action="store_true"),
+              cli_option("--no-ip-check", dest="ignore_ip",
+                         help="Do not check that the IP of the new name"
+                         " is alive",
+                         default=False, action="store_true"),
               SUBMIT_OPT,
               ],
              "<instance> <new_name>", "Rename the instance"),
               SUBMIT_OPT,
               ],
              "<instance> <new_name>", "Rename the instance"),
-  'replace-disks': (ReplaceDisks, ARGS_ONE,
+  'replace-disks': (ReplaceDisks, ARGS_ONE_INSTANCE,
                     [DEBUG_OPT,
                     [DEBUG_OPT,
-                     make_option("-n", "--new-secondary", dest="new_secondary",
-                                 help=("New secondary node (for secondary"
-                                       " node change)"), metavar="NODE",
-                                 default=None),
-                     make_option("-p", "--on-primary", dest="on_primary",
-                                 default=False, action="store_true",
-                                 help=("Replace the disk(s) on the primary"
-                                       " node (only for the drbd template)")),
-                     make_option("-s", "--on-secondary", dest="on_secondary",
-                                 default=False, action="store_true",
-                                 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")),
-                     make_option("-I", "--iallocator", metavar="<NAME>",
-                                 help="Select new secondary for the instance"
-                                 " automatically using the"
-                                 " <NAME> iallocator plugin (enables"
-                                 " secondary node replacement)",
-                                 default=None, type="string"),
+                     cli_option("-n", "--new-secondary", dest="new_secondary",
+                                help=("New secondary node (for secondary"
+                                      " node change)"), metavar="NODE",
+                                default=None,
+                                completion_suggest=OPT_COMPL_ONE_NODE),
+                     cli_option("-p", "--on-primary", dest="on_primary",
+                                default=False, action="store_true",
+                                help=("Replace the disk(s) on the primary"
+                                      " node (only for the drbd template)")),
+                     cli_option("-s", "--on-secondary", dest="on_secondary",
+                                default=False, action="store_true",
+                                help=("Replace the disk(s) on the secondary"
+                                      " node (only for the drbd template)")),
+                     cli_option("-a", "--auto", dest="auto",
+                                default=False, action="store_true",
+                                help=("Automatically replace faulty disks"
+                                      " (only for the drbd template)")),
+                     cli_option("--disks", dest="disks", default=None,
+                                help="Comma-separated list of disks"
+                                " indices to replace (e.g. 0,2) (optional,"
+                                " defaults to all disks)"),
+                     cli_option("-I", "--iallocator", metavar="<NAME>",
+                                help="Select new secondary for the instance"
+                                " automatically using the"
+                                " <NAME> iallocator plugin (enables"
+                                " secondary node replacement)",
+                                default=None, type="string",
+                                completion_suggest=OPT_COMPL_ONE_IALLOCATOR),
                      SUBMIT_OPT,
                      ],
                     "[-s|-p|-n NODE|-I NAME] <instance>",
                     "Replaces all disks for the instance"),
                      SUBMIT_OPT,
                      ],
                     "[-s|-p|-n NODE|-I NAME] <instance>",
                     "Replaces all disks for the instance"),
-  'modify': (SetInstanceParams, ARGS_ONE,
+  'modify': (SetInstanceParams, ARGS_ONE_INSTANCE,
              [DEBUG_OPT, FORCE_OPT,
              [DEBUG_OPT, FORCE_OPT,
-              keyval_option("-H", "--hypervisor", type="keyval",
-                            default={}, dest="hypervisor",
-                            help="Change hypervisor parameters"),
-              keyval_option("-B", "--backend", type="keyval",
-                            default={}, dest="beparams",
-                            help="Change backend parameters"),
-              ikv_option("--disk", help="Disk changes",
+              cli_option("-H", "--hypervisor", type="keyval",
+                         default={}, dest="hypervisor",
+                         help="Change hypervisor parameters"),
+              cli_option("-B", "--backend", type="keyval",
+                         default={}, dest="beparams",
+                         help="Change backend parameters"),
+              cli_option("--disk", help="Disk changes",
                          default=[], dest="disks",
                          action="append",
                          type="identkeyval"),
                          default=[], dest="disks",
                          action="append",
                          type="identkeyval"),
-              ikv_option("--net", help="NIC changes",
+              cli_option("--net", help="NIC changes",
                          default=[], dest="nics",
                          action="append",
                          type="identkeyval"),
               SUBMIT_OPT,
               ],
              "<instance>", "Alters the parameters of an instance"),
                          default=[], dest="nics",
                          action="append",
                          type="identkeyval"),
               SUBMIT_OPT,
               ],
              "<instance>", "Alters the parameters of an instance"),
-  'shutdown': (ShutdownInstance, ARGS_ANY,
+  'shutdown': (ShutdownInstance, [ArgInstance(min=1)],
                [DEBUG_OPT, m_node_opt, m_pri_node_opt, m_sec_node_opt,
                 m_clust_opt, m_inst_opt, m_force_multi,
                 SUBMIT_OPT,
                 ],
                "<instance>", "Stops an instance"),
                [DEBUG_OPT, m_node_opt, m_pri_node_opt, m_sec_node_opt,
                 m_clust_opt, m_inst_opt, m_force_multi,
                 SUBMIT_OPT,
                 ],
                "<instance>", "Stops an instance"),
-  'startup': (StartupInstance, ARGS_ANY,
+  'startup': (StartupInstance, [ArgInstance(min=1)],
               [DEBUG_OPT, FORCE_OPT, m_force_multi,
               [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,
                m_node_opt, m_pri_node_opt, m_sec_node_opt,
                m_clust_opt, m_inst_opt,
                SUBMIT_OPT,
+               cli_option("-H", "--hypervisor", type="keyval",
+                          default={}, dest="hvparams",
+                          help="Temporary hypervisor parameters"),
+               cli_option("-B", "--backend", type="keyval",
+                          default={}, dest="beparams",
+                          help="Temporary backend parameters"),
                ],
                ],
-            "<instance>", "Starts an instance"),
-
-  'reboot': (RebootInstance, ARGS_ANY,
+              "<instance>", "Starts an instance"),
+  'reboot': (RebootInstance, [ArgInstance(min=1)],
               [DEBUG_OPT, m_force_multi,
               [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,
-                           type="string", metavar="<REBOOT>"),
-               make_option("--ignore-secondaries", dest="ignore_secondaries",
-                           default=False, action="store_true",
-                           help="Ignore errors from secondaries"),
+               cli_option("-t", "--type", dest="reboot_type",
+                          help="Type of reboot: soft/hard/full",
+                          default=constants.INSTANCE_REBOOT_HARD,
+                          metavar="<REBOOT>",
+                          choices=list(constants.REBOOT_TYPES)),
+               cli_option("--ignore-secondaries", dest="ignore_secondaries",
+                          default=False, action="store_true",
+                          help="Ignore errors from secondaries"),
                m_node_opt, m_pri_node_opt, m_sec_node_opt,
                m_clust_opt, m_inst_opt,
                SUBMIT_OPT,
                ],
             "<instance>", "Reboots an instance"),
                m_node_opt, m_pri_node_opt, m_sec_node_opt,
                m_clust_opt, m_inst_opt,
                SUBMIT_OPT,
                ],
             "<instance>", "Reboots an instance"),
-  'activate-disks': (ActivateDisks, ARGS_ONE, [DEBUG_OPT, SUBMIT_OPT],
+  'activate-disks': (ActivateDisks, ARGS_ONE_INSTANCE,
+                     [DEBUG_OPT, SUBMIT_OPT,
+                      cli_option("--ignore-size", dest="ignore_size",
+                                 default=False, action="store_true",
+                                 help="Ignore current recorded size"
+                                 " (useful for forcing activation when"
+                                 " the recorded size is wrong)"),
+                      ],
                      "<instance>",
                      "Activate an instance's disks"),
                      "<instance>",
                      "Activate an instance's disks"),
-  'deactivate-disks': (DeactivateDisks, ARGS_ONE, [DEBUG_OPT, SUBMIT_OPT],
+  'deactivate-disks': (DeactivateDisks, ARGS_ONE_INSTANCE,
+                       [DEBUG_OPT, SUBMIT_OPT],
                        "<instance>",
                        "Deactivate an instance's disks"),
                        "<instance>",
                        "Deactivate an instance's disks"),
-  'grow-disk': (GrowDisk, ARGS_FIXED(3),
+  'recreate-disks': (RecreateDisks, ARGS_ONE_INSTANCE,
+                     [DEBUG_OPT, SUBMIT_OPT,
+                     cli_option("--disks", dest="disks", default=None,
+                                help="Comma-separated list of disks"
+                                " indices to replace (e.g. 0,2) (optional,"
+                                " defaults to all disks)"),
+                      ],
+                     "<instance>",
+                     "Recreate an instance's disks"),
+  'grow-disk': (GrowDisk,
+                [ArgInstance(min=1, max=1), ArgUnknown(min=1, max=1),
+                 ArgUnknown(min=1, max=1)],
                 [DEBUG_OPT, SUBMIT_OPT,
                 [DEBUG_OPT, SUBMIT_OPT,
-                 make_option("--no-wait-for-sync",
-                             dest="wait_for_sync", default=True,
-                             action="store_false",
-                             help="Don't wait for sync (DANGEROUS!)"),
+                 cli_option("--no-wait-for-sync",
+                            dest="wait_for_sync", default=True,
+                            action="store_false",
+                            help="Don't wait for sync (DANGEROUS!)"),
                  ],
                 "<instance> <disk> <size>", "Grow an instance's disk"),
                  ],
                 "<instance> <disk> <size>", "Grow an instance's disk"),
-  'list-tags': (ListTags, ARGS_ONE, [DEBUG_OPT],
+  'list-tags': (ListTags, ARGS_ONE_INSTANCE, [DEBUG_OPT],
                 "<instance_name>", "List the tags of the given instance"),
                 "<instance_name>", "List the tags of the given instance"),
-  'add-tags': (AddTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
+  'add-tags': (AddTags, [ArgInstance(min=1, max=1), ArgUnknown()],
+               [DEBUG_OPT, TAG_SRC_OPT],
                "<instance_name> tag...", "Add tags to the given instance"),
                "<instance_name> tag...", "Add tags to the given instance"),
-  'remove-tags': (RemoveTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
+  'remove-tags': (RemoveTags, [ArgInstance(min=1, max=1), ArgUnknown()],
+                  [DEBUG_OPT, TAG_SRC_OPT],
                   "<instance_name> tag...", "Remove tags from given instance"),
   }
 
                   "<instance_name> tag...", "Remove tags from given instance"),
   }
 
@@ -1522,6 +1667,7 @@ aliases = {
   'stop': 'shutdown',
   }
 
   'stop': 'shutdown',
   }
 
+
 if __name__ == '__main__':
   sys.exit(GenericMain(commands, aliases=aliases,
                        override={"tag_type": constants.TAG_INSTANCE}))
 if __name__ == '__main__':
   sys.exit(GenericMain(commands, aliases=aliases,
                        override={"tag_type": constants.TAG_INSTANCE}))