Hotpluging is done by functions invoked by ApplyContainerMods(). In order hotpluging 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 support add and remove. Before hotpluging a Disk it must be assembled. During LUInstanceSetParams() newly created disks are not added to the instance so ExpandCheckDisks() in AssembleInstanceDisks() will fail. So we make this check optional only for this case. In order to remove a disk (with blockdev_remove) it must be shutdown. So after unpluging the disk ShutdownDiskInstanceDisks() must be invoked. For both device types we use the generic RPC call_hotplug_device. Signed-off-by: Dimitris Aragiorgis <[email protected]> --- lib/cmdlib/instance.py | 52 ++++++++++++++++++++++++++++++++++++---- lib/cmdlib/instance_storage.py | 5 ++-- 2 files changed, 50 insertions(+), 7 deletions(-) diff --git a/lib/cmdlib/instance.py b/lib/cmdlib/instance.py index 22812ea..a602512 100644 --- a/lib/cmdlib/instance.py +++ b/lib/cmdlib/instance.py @@ -76,6 +76,11 @@ _TApplyContModsCbChanges = \ ]))) +def _DeviceHotplugable(dev): + + return dev.uuid is not None + + def _CheckHostnameSane(lu, name): """Ensures that a given hostname resolves to a 'sane' name. @@ -3028,7 +3033,8 @@ class LUInstanceSetParams(LogicalUnit): # Operate on copies as this is still in prereq nics = [nic.Copy() for nic in self.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 @@ -3203,6 +3209,14 @@ class LUInstanceSetParams(LogicalUnit): " continuing anyway: %s", idx, self.cfg.GetNodeName(pnode_uuid), 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) + result.Raise("Could not hotplug device.") + self.LogInfo("Hotplug done.") + def _CreateNewDisk(self, idx, params, _): """Creates a new disk. @@ -3228,6 +3242,13 @@ class LUInstanceSetParams(LogicalUnit): disks=[(idx, disk, 0)], cleanup=new_disks) + if self.op.hotplug: + _, device_info = AssembleInstanceDisks(self, self.instance, + [disk], check=False) + _, _, dev_path = device_info[0] + self._HotplugDevice(constants.HOTPLUG_ADD, constants.HOTPLUG_DISK, + disk, dev_path, idx) + return (disk, [ ("disk/%d" % idx, "add:size=%s,mode=%s" % (disk.size, disk.mode)), ]) @@ -3253,6 +3274,11 @@ class LUInstanceSetParams(LogicalUnit): """Removes a disk. """ + if self.op.hotplug and _DeviceHotplugable(root): + self._HotplugDevice(constants.HOTPLUG_REMOVE, constants.HOTPLUG_DISK, + root, None, idx) + ShutdownInstanceDisks(self, self.instance, [root]) + (anno_disk,) = AnnotateDiskParams(self.instance, [root], self.cfg) for node_uuid, disk in anno_disk.ComputeNodeTree( self.instance.primary_node): @@ -3282,13 +3308,18 @@ class LUInstanceSetParams(LogicalUnit): nicparams=nicparams) nobj.uuid = self.cfg.GenerateUniqueID(self.proc.GetECId()) - return (nobj, [ + if self.op.hotplug: + self._HotplugDevice(constants.HOTPLUG_ADD, constants.HOTPLUG_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. @@ -3313,8 +3344,19 @@ class LUInstanceSetParams(LogicalUnit): for (key, val) in nic.nicparams.items(): changes.append(("nic.%s/%d" % (key, idx), val)) + if self.op.hotplug and _DeviceHotplugable(nic): + self._HotplugDevice(constants.HOTPLUG_REMOVE, constants.HOTPLUG_NIC, + nic, None, idx) + self._HotplugDevice(constants.HOTPLUG_ADD, constants.HOTPLUG_NIC, + nic, None, idx) + return changes + def _RemoveNic(self, idx, nic, _): + if self.op.hotplug and _DeviceHotplugable(nic): + self._HotplugDevice(constants.HOTPLUG_REMOVE, constants.HOTPLUG_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 793cfa6..1516826 100644 --- a/lib/cmdlib/instance_storage.py +++ b/lib/cmdlib/instance_storage.py @@ -1259,7 +1259,7 @@ def _SafeShutdownInstanceDisks(lu, instance, disks=None): def AssembleInstanceDisks(lu, instance, disks=None, ignore_secondaries=False, - ignore_size=False): + ignore_size=False, check=True): """Prepare the block devices for an instance. This sets up the block devices on all nodes. @@ -1284,7 +1284,8 @@ def AssembleInstanceDisks(lu, instance, disks=None, ignore_secondaries=False, """ device_info = [] disks_ok = True - disks = ExpandCheckDisks(instance, disks) + if check: + disks = ExpandCheckDisks(instance, disks) # With the two passes mechanism we try to reduce the window of # opportunity for the race condition of switching DRBD to primary -- 1.7.10.4
