Introduce new RPC that eventually invoke hypervisor specific hotplug functions. In order to be generic it has the following arguments: device type, action, device, extra info, seq. Device type can be NIC or DISK, action can be ADD, REMOVE, device is the NIC or Disk object, extra info is used by Disk hotplug to point the device path and seq is the device index (from the master perspective)
If hypervisor does not support hotplug the opcode will fail. Signed-off-by: Dimitris Aragiorgis <[email protected]> --- lib/backend.py | 26 ++++++++++++++++++++++++++ lib/rpc.py | 7 +++++++ lib/rpc_defs.py | 11 ++++++++++- lib/server/noded.py | 13 +++++++++++++ 4 files changed, 56 insertions(+), 1 deletion(-) diff --git a/lib/backend.py b/lib/backend.py index 7063aa5..a3d4ee5 100644 --- a/lib/backend.py +++ b/lib/backend.py @@ -1934,6 +1934,32 @@ def GetMigrationStatus(instance): except Exception, err: # pylint: disable=W0703 _Fail("Failed to get migration status: %s", err, exc=True) +def HotplugDevice(instance, action, dev_type, device, extra, seq): + """Hotplug a device + + Hotplug is currently supported only for KVM Hypervisor. + @type instance: L{objects.Instance} + @param instance: the instance to which we hotplug a device + @type action: string + @param action: the hotplug action to perform + @type dev_type: string + @param dev_type: the device type to hotplug + @type device: either L{objects.NIC} or L{objects.Disk} + @param device: the device object to hotplug + @type extra: string + @param extra: extra info used by hotplug code (e.g. disk link) + @type seq: int + @param seq: the index of the device from master perspective + @raise RPCFail: in case instance does not have KVM hypervisor + + """ + hyper = hypervisor.GetHypervisor(instance.hypervisor) + try: + getattr(hyper, "HotplugDevice") + except NameError: + _Fail("Hotplug is not supported for %s hypervisor", + instance.hypervisor, exc=True ) + return hyper.HotplugDevice(instance, action, dev_type, device, extra, seq) def BlockdevCreate(disk, size, owner, on_primary, info, excl_stor): """Creates a block device for an instance. diff --git a/lib/rpc.py b/lib/rpc.py index 1058dcc..0219554 100644 --- a/lib/rpc.py +++ b/lib/rpc.py @@ -820,6 +820,7 @@ class RpcRunner(_RpcClientBase, rpc_defs.ED_INST_DICT_HVP_BEP_DP: self._InstDictHvpBepDp, rpc_defs.ED_INST_DICT_OSP_DP: self._InstDictOspDp, rpc_defs.ED_NIC_DICT: self._NicDict, + rpc_defs.ED_DEVICE_DICT: self._DeviceDict, # Encoders annotating disk parameters rpc_defs.ED_DISKS_DICT_DP: self._DisksDictDP, @@ -858,6 +859,12 @@ class RpcRunner(_RpcClientBase, n.netinfo = objects.Network.ToDict(nobj) return n.ToDict() + def _DeviceDict(self, device): + if isinstance(device, objects.NIC): + return self._NicDict(device) + elif isinstance(device, objects.Disk): + return _ObjectToDict(device) + def _InstDict(self, instance, hvp=None, bep=None, osp=None): """Convert the given instance to a dict. diff --git a/lib/rpc_defs.py b/lib/rpc_defs.py index ffb5ceb..77152db 100644 --- a/lib/rpc_defs.py +++ b/lib/rpc_defs.py @@ -72,7 +72,8 @@ ACCEPT_OFFLINE_NODE = object() ED_DISKS_DICT_DP, ED_MULTI_DISKS_DICT_DP, ED_SINGLE_DISK_DICT_DP, - ED_NIC_DICT) = range(1, 16) + ED_NIC_DICT, + ED_DEVICE_DICT) = range(1, 17) def _Prepare(calls): @@ -296,6 +297,14 @@ _INSTANCE_CALLS = [ ("reinstall", None, None), ("debug", None, None), ], None, None, "Starts an instance"), + ("hotplug_device", SINGLE, None, constants.RPC_TMO_NORMAL, [ + ("instance", ED_INST_DICT, "Instance object"), + ("action", None, "Hotplug Action"), + ("dev_type", None, "Device type"), + ("device", ED_DEVICE_DICT, "Device dict"), + ("extra", None, "Extra info for device (dev_path for disk)"), + ("seq", None, "Device seq"), + ], None, None, "Hoplug a device to a running instance"), ] _IMPEXP_CALLS = [ diff --git a/lib/server/noded.py b/lib/server/noded.py index ecc7c95..fab5709 100644 --- a/lib/server/noded.py +++ b/lib/server/noded.py @@ -617,6 +617,19 @@ class NodeRequestHandler(http.server.HttpServerHandler): return backend.StartInstance(instance, startup_paused, trail) @staticmethod + def perspective_hotplug_device(params): + """Hotplugs device to a running instance. + + """ + (idict, action, dev_type, ddict, extra, seq) = params + instance = objects.Instance.FromDict(idict) + if dev_type == constants.HOTPLUG_DISK: + device = objects.Disk.FromDict(ddict) + elif dev_type == constants.HOTPLUG_NIC: + device = objects.NIC.FromDict(ddict) + return backend.HotplugDevice(instance, action, dev_type, device, extra, seq) + + @staticmethod def perspective_migration_info(params): """Gather information about an instance to be migrated. -- 1.7.10.4
