commit 99452998d40185befb668645e91e93ebccd8e8dc
Author: Dimitris Aragiorgis <dimara@grnet.gr>
Date:   Wed Oct 9 17:45:35 2013 +0300

    (2.10) Hotplug: cmdlib support
    
    Hotplugging is done by functions invoked by ApplyContainerMods(). In
    order hotplugging to take place the --hotplug option must be passed
    otherwise the modifications will take place after reboot.
    
    NIC hotplug supports add, remove and modify. The modify is done by removing
    the existing NIC and adding a new one in the same pci slot.
    
    Disk hotplug supports add and remove.  Before hotplugging a Disk it
    must be assembled. Use blockdev_assemble RPC, get the device link_name
    and then call hotplug RPC command.
    
    In order to remove a disk (with blockdev_remove) it must be shutdown. So
    after unplugging the disk ShutdownDiskInstanceDisks() must be invoked.
    
    For both device types we use the generic RPC call_hotplug_device.
    
    Also adapt unit tests to follow the change.
    
    Signed-off-by: Dimitris Aragiorgis <dimara@grnet.gr>
    Signed-off-by: Thomas Thrainer <thomasth@google.com>
    Reviewed-by: Thomas Thrainer <thomasth@google.com>
    
    cmdlib unittests do not exist in stable-2.8
    The other were easy to resolve
    
    Conflicts:
    	lib/cmdlib/instance.py
    	lib/cmdlib/instance_storage.py
    	test/py/cmdlib/backup_unittest.py
    
    Signed-off-by: Dimitris Aragiorgis <dimara@grnet.gr>

diff --git a/lib/cmdlib/instance.py b/lib/cmdlib/instance.py
index 007f989..fff06c9 100644
--- a/lib/cmdlib/instance.py
+++ b/lib/cmdlib/instance.py
@@ -1807,7 +1807,7 @@ class LUInstanceMove(LogicalUnit):
                         idx, result.fail_msg)
         errs.append(result.fail_msg)
         break
-      dev_path = result.payload
+      dev_path, _ = result.payload
       result = self.rpc.call_blockdev_export(source_node, (disk, instance),
                                              target_node, dev_path,
                                              cluster_name)
@@ -2977,7 +2977,8 @@ class LUInstanceSetParams(LogicalUnit):
       # Operate on copies as this is still in prereq
       nics = [nic.Copy() for nic in instance.nics]
       _ApplyContainerMods("NIC", nics, self._nic_chgdesc, self.nicmod,
-                          self._CreateNewNic, self._ApplyNicMods, None)
+                          self._CreateNewNic, self._ApplyNicMods,
+                          self._RemoveNic)
       # Verify that NIC names are unique and valid
       utils.ValidateDeviceNames("NIC", nics)
       self._new_nics = nics
@@ -3152,6 +3153,17 @@ class LUInstanceSetParams(LogicalUnit):
         self.LogWarning("Could not remove metadata for disk %d on node %s,"
                         " continuing anyway: %s", idx, pnode, msg)
 
+  def _HotplugDevice(self, action, dev_type, device, extra, seq):
+    self.LogInfo("Trying to hotplug device...")
+    result = self.rpc.call_hotplug_device(self.instance.primary_node,
+                                          self.instance, action, dev_type,
+                                          device, extra, seq)
+    if result.fail_msg:
+      self.LogWarning("Could not hotplug device: %s" % result.fail_msg)
+      self.LogInfo("Continuing execution..")
+    else:
+      self.LogInfo("Hotplug done.")
+
   def _CreateNewDisk(self, idx, params, _):
     """Creates a new disk.
 
@@ -3179,6 +3191,19 @@ class LUInstanceSetParams(LogicalUnit):
                          disks=[(idx, disk, 0)],
                          cleanup=new_disks)
 
+    if self.op.hotplug:
+      result = self.rpc.call_blockdev_assemble(self.instance.primary_node,
+                                               (disk, self.instance),
+                                               self.instance.name, True, idx)
+      if result.fail_msg:
+        self.LogWarning("Can't assemble newly created disk %d: %s",
+                        idx, result.fail_msg)
+      else:
+        _, link_name = result.payload
+        self._HotplugDevice(constants.HOTPLUG_ACTION_ADD,
+                            constants.HOTPLUG_TARGET_DISK,
+                            disk, link_name, idx)
+
     return (disk, [
       ("disk/%d" % idx, "add:size=%s,mode=%s" % (disk.size, disk.mode)),
       ])
@@ -3216,6 +3241,12 @@ class LUInstanceSetParams(LogicalUnit):
     """Removes a disk.
 
     """
+    if self.op.hotplug:
+      self._HotplugDevice(constants.HOTPLUG_ACTION_REMOVE,
+                          constants.HOTPLUG_TARGET_DISK,
+                          root, None, idx)
+      ShutdownInstanceDisks(self, self.instance, [root])
+
     (anno_disk,) = AnnotateDiskParams(self.instance, [root], self.cfg)
     for node, disk in anno_disk.ComputeNodeTree(self.instance.primary_node):
       self.cfg.SetDiskID(disk, node)
@@ -3243,13 +3274,19 @@ class LUInstanceSetParams(LogicalUnit):
                        nicparams=nicparams)
     nobj.uuid = self.cfg.GenerateUniqueID(self.proc.GetECId())
 
-    return (nobj, [
+    if self.op.hotplug:
+      self._HotplugDevice(constants.HOTPLUG_ACTION_ADD,
+                          constants.HOTPLUG_TARGET_NIC,
+                          nobj, None, idx)
+
+    desc = [
       ("nic.%d" % idx,
        "add:mac=%s,ip=%s,mode=%s,link=%s,network=%s" %
        (mac, ip, private.filled[constants.NIC_MODE],
-       private.filled[constants.NIC_LINK],
-       net)),
-      ])
+       private.filled[constants.NIC_LINK], net)),
+      ]
+
+    return (nobj, desc)
 
   def _ApplyNicMods(self, idx, nic, params, private):
     """Modifies a network interface.
@@ -3274,8 +3311,19 @@ class LUInstanceSetParams(LogicalUnit):
       for (key, val) in nic.nicparams.items():
         changes.append(("nic.%s/%d" % (key, idx), val))
 
+    if self.op.hotplug:
+      self._HotplugDevice(constants.HOTPLUG_ACTION_MODIFY,
+                          constants.HOTPLUG_TARGET_NIC,
+                          nic, None, idx)
+
     return changes
 
+  def _RemoveNic(self, idx, nic, _):
+    if self.op.hotplug:
+      self._HotplugDevice(constants.HOTPLUG_ACTION_REMOVE,
+                          constants.HOTPLUG_TARGET_NIC,
+                          nic, None, idx)
+
   def Exec(self, feedback_fn):
     """Modifies an instance.
 
diff --git a/lib/cmdlib/instance_storage.py b/lib/cmdlib/instance_storage.py
index b1fea8b..e3128fe 100644
--- a/lib/cmdlib/instance_storage.py
+++ b/lib/cmdlib/instance_storage.py
@@ -1139,8 +1139,12 @@ def ShutdownInstanceDisks(lu, instance, disks=None, ignore_primary=False):
   ignored.
 
   """
-  lu.cfg.MarkInstanceDisksInactive(instance.name)
   all_result = True
+
+  if disks is None:
+    # only mark instance disks as inactive if all disks are affected
+    lu.cfg.MarkInstanceDisksInactive(instance.name)
+
   disks = ExpandCheckDisks(instance, disks)
 
   for disk in disks:
@@ -1169,7 +1173,7 @@ def _SafeShutdownInstanceDisks(lu, instance, disks=None):
 
 
 def AssembleInstanceDisks(lu, instance, disks=None, ignore_secondaries=False,
-                           ignore_size=False):
+                          ignore_size=False):
   """Prepare the block devices for an instance.
 
   This sets up the block devices on all nodes.
@@ -1195,6 +1199,11 @@ def AssembleInstanceDisks(lu, instance, disks=None, ignore_secondaries=False,
   device_info = []
   disks_ok = True
   iname = instance.name
+
+  if disks is None:
+    # only mark instance disks as active if all disks are affected
+    lu.cfg.MarkInstanceDisksActive(iname)
+
   disks = ExpandCheckDisks(instance, disks)
 
   # With the two passes mechanism we try to reduce the window of
@@ -1206,10 +1215,6 @@ def AssembleInstanceDisks(lu, instance, disks=None, ignore_secondaries=False,
   # into any other network-connected state (Connected, SyncTarget,
   # SyncSource, etc.)
 
-  # mark instance disks as active before doing actual work, so watcher does
-  # not try to shut them down erroneously
-  lu.cfg.MarkInstanceDisksActive(iname)
-
   # 1st pass, assemble on all nodes in secondary mode
   for idx, inst_disk in enumerate(disks):
     for node, node_disk in inst_disk.ComputeNodeTree(instance.primary_node):
@@ -1251,7 +1256,7 @@ def AssembleInstanceDisks(lu, instance, disks=None, ignore_secondaries=False,
                       inst_disk.iv_name, node, msg)
         disks_ok = False
       else:
-        dev_path = result.payload
+        dev_path, _ = result.payload
 
     device_info.append((instance.primary_node, inst_disk.iv_name, dev_path))
 
