commit 848cdc34c0be4fd1c28c35eb34a60116ae9a5e65
Author: Dimitris Aragiorgis <dimara@grnet.gr>
Date:   Thu Aug 9 14:04:10 2012 +0300

    Hotplug: client support
    
    Add --hotplug option. Only used in OpInstanceSetParams.
    If this is omitted modifications become effective after
    reboot.
    
    Ask user confirmation in case nic modify + hotplug
    
    Signed-off-by: Dimitris Aragiorgis <dimara@grnet.gr>

diff --git a/lib/cli.py b/lib/cli.py
index 2e436b6..332cd2c 100644
--- a/lib/cli.py
+++ b/lib/cli.py
@@ -95,6 +95,7 @@ __all__ = [
   "GLOBAL_FILEDIR_OPT",
   "HID_OS_OPT",
   "GLOBAL_SHARED_FILEDIR_OPT",
+  "HOTPLUG_OPT",
   "HVLIST_OPT",
   "HVOPTS_OPT",
   "HYPERVISOR_OPT",
@@ -1638,6 +1639,10 @@ INCLUDEDEFAULTS_OPT = cli_option("--include-defaults", dest="include_defaults",
                                  default=False, action="store_true",
                                  help="Include default values")
 
+HOTPLUG_OPT = cli_option("--hotplug", dest="hotplug",
+                         action="store_true", default=False,
+                         help="Try to hotplug device")
+
 #: Options provided by all commands
 COMMON_OPTS = [DEBUG_OPT, REASON_OPT]
 
diff --git a/lib/client/gnt_instance.py b/lib/client/gnt_instance.py
index e69f55e..ce24760 100644
--- a/lib/client/gnt_instance.py
+++ b/lib/client/gnt_instance.py
@@ -1319,6 +1319,14 @@ def SetInstanceParams(opts, args):
                       allowed_values=[constants.VALUE_DEFAULT])
 
   nics = _ConvertNicDiskModifications(opts.nics)
+  for action, identifier, params in nics:
+    if action == constants.DDM_MODIFY and opts.hotplug:
+      usertext = ("You are about to hot-modify a NIC. This will be done"
+                  " by removing the exisiting and then adding a new one."
+                  " Network connection might be lost. Continue?")
+    if not AskUser(usertext):
+      return 1
+
   disks = _ParseDiskSizes(_ConvertNicDiskModifications(opts.disks))
 
   if (opts.disk_template and
@@ -1338,6 +1346,7 @@ def SetInstanceParams(opts, args):
   op = opcodes.OpInstanceSetParams(instance_name=args[0],
                                    nics=nics,
                                    disks=disks,
+                                   hotplug=opts.hotplug,
                                    disk_template=opts.disk_template,
                                    remote_node=opts.node,
                                    pnode=opts.new_primary_node,
@@ -1360,10 +1369,11 @@ def SetInstanceParams(opts, args):
     ToStdout("Modified instance %s", args[0])
     for param, data in result:
       ToStdout(" - %-5s -> %s", param, data)
-    ToStdout("Please don't forget that most parameters take effect"
-             " only at the next (re)start of the instance initiated by"
-             " ganeti; restarting from within the instance will"
-             " not be enough.")
+    if not opts.hotplug:
+      ToStdout("Please don't forget that most parameters take effect"
+               " only at the next (re)start of the instance initiated by"
+               " ganeti; restarting from within the instance will"
+               " not be enough.")
   return 0
 
 
@@ -1545,7 +1555,7 @@ commands = {
     [DISK_TEMPLATE_OPT, SINGLE_NODE_OPT, OS_OPT, FORCE_VARIANT_OPT,
      OSPARAMS_OPT, DRY_RUN_OPT, PRIORITY_OPT, NWSYNC_OPT, OFFLINE_INST_OPT,
      ONLINE_INST_OPT, IGNORE_IPOLICY_OPT, RUNTIME_MEM_OPT,
-     NOCONFLICTSCHECK_OPT, NEW_PRIMARY_OPT],
+     NOCONFLICTSCHECK_OPT, NEW_PRIMARY_OPT, HOTPLUG_OPT],
     "<instance>", "Alters the parameters of an instance"),
   "shutdown": (
     GenericManyOps("shutdown", _ShutdownInstance), [ArgInstance()],
diff --git a/lib/opcodes.py b/lib/opcodes.py
index 26e0b06..7b04b36 100644
--- a/lib/opcodes.py
+++ b/lib/opcodes.py
@@ -1374,6 +1374,7 @@ class OpInstanceCreate(OpCode):
     ("src_path", None, ht.TMaybeString, "Source directory for import"),
     ("start", True, ht.TBool, "Whether to start instance after creation"),
     ("tags", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), "Instance tags"),
+    ("hotplug", None, ht.TMaybeBool, "Whether to hotplug devices"),
     ]
   OP_RESULT = ht.Comment("instance nodes")(ht.TListOf(ht.TNonEmptyString))
 
@@ -1767,6 +1768,7 @@ class OpInstanceSetParams(OpCode):
      "Whether to wait for the disk to synchronize, when changing template"),
     ("offline", None, ht.TMaybeBool, "Whether to mark instance as offline"),
     ("conflicts_check", True, ht.TBool, "Check for conflicting IPs"),
+    ("hotplug", None, ht.TMaybeBool, "Whether to hotplug devices"),
     ]
   OP_RESULT = _TSetParamsResult
 
diff --git a/tools/cfgupgrade b/tools/cfgupgrade
index 554877f..6157d6c 100755
--- a/tools/cfgupgrade
+++ b/tools/cfgupgrade
@@ -189,6 +189,11 @@ def UpgradeInstances(config_data):
           print("NIC with network name %s found."
                 " Substituting with uuid %s." % (name, uuid))
           nic["network"] = uuid
+      try:
+        del nic["idx"]
+        print("Deleting deprecated idx")
+      except KeyError:
+        pass
 
     if "disks" not in iobj:
       raise Error("Instance '%s' doesn't have a disks entry?!" % instance)
@@ -203,6 +208,18 @@ def UpgradeInstances(config_data):
         dobj["iv_name"] = expected
       if not "spindles" in dobj:
         missing_spindles = True
+      try:
+        del dobj["idx"]
+        print("Deleting deprecated idx")
+      except KeyError:
+        pass
+
+    for attr in ("dev_idxs", "hotplug_info", "hotplugs", "pci_reservations"):
+      try:
+        del iobj[attr]
+        print("Deleting deprecated %s" % attr)
+      except KeyError:
+        pass
 
   if GetExclusiveStorageValue(config_data) and missing_spindles:
     # We cannot be sure that the instances that are missing spindles have
