Commit 96ed3a3e authored by Dimitris Aragiorgis's avatar Dimitris Aragiorgis Committed by Thomas Thrainer
Browse files

Introduce --hotplug-if-possible option



This will be useful for an external entity using RAPI that
wants to modify devices of instances.

The common use case for that is:
"I want to add a NIC/disk to an instance. If it is running
then try to hotplug the device. If not, then just add it to config."

Another use case is that if we are unaware of the instance's
hypervisor: "I want do add a NIC/disk to an instance and if
its hypervisor is KVM try to hotplug it."

For all those cases just print a warning in CheckPrereq() and
disable any further hotplug related actions (and let
modifications take place after reboot).

Add NoHoplugSupport and HotplugIfPossible unittests an modify
existing by explicitly creating successful RPC results for
call_hotplug_supported.
Signed-off-by: default avatarDimitris Aragiorgis <dimara@grnet.gr>
Signed-off-by: default avatarThomas Thrainer <thomasth@google.com>
Reviewed-by: default avatarThomas Thrainer <thomasth@google.com>
parent 1ef82b60
......@@ -96,6 +96,7 @@ __all__ = [
"HID_OS_OPT",
"GLOBAL_SHARED_FILEDIR_OPT",
"HOTPLUG_OPT",
"HOTPLUG_IF_POSSIBLE_OPT",
"HVLIST_OPT",
"HVOPTS_OPT",
"HYPERVISOR_OPT",
......@@ -1646,6 +1647,12 @@ HOTPLUG_OPT = cli_option("--hotplug", dest="hotplug",
action="store_true", default=False,
help="Hotplug supported devices (NICs and Disks)")
HOTPLUG_IF_POSSIBLE_OPT = cli_option("--hotplug-if-possible",
dest="hotplug_if_possible",
action="store_true", default=False,
help="Hotplug devices in case"
" hotplug is supported")
#: Options provided by all commands
COMMON_OPTS = [DEBUG_OPT, REASON_OPT]
......
......@@ -1342,6 +1342,7 @@ def SetInstanceParams(opts, args):
nics=nics,
disks=disks,
hotplug=opts.hotplug,
hotplug_if_possible=opts.hotplug_if_possible,
disk_template=opts.disk_template,
remote_node=opts.node,
pnode=opts.new_primary_node,
......@@ -1549,7 +1550,8 @@ 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, HOTPLUG_OPT],
NOCONFLICTSCHECK_OPT, NEW_PRIMARY_OPT, HOTPLUG_OPT,
HOTPLUG_IF_POSSIBLE_OPT],
"<instance>", "Alters the parameters of an instance"),
"shutdown": (
GenericManyOps("shutdown", _ShutdownInstance), [ArgInstance()],
......
......@@ -2841,10 +2841,19 @@ class LUInstanceSetParams(LogicalUnit):
# dictionary with instance information after the modification
ispec = {}
if self.op.hotplug:
if self.op.hotplug or self.op.hotplug_if_possible:
result = self.rpc.call_hotplug_supported(self.instance.primary_node,
self.instance)
result.Raise("Hotplug is not supported.")
if result.fail_msg:
if self.op.hotplug:
result.Raise("Hotplug is not possible: %s" % result.fail_msg,
prereq=True)
else:
self.LogWarning(result.fail_msg)
self.op.hotplug = False
self.LogInfo("Modification will take place without hotplugging.")
else:
self.op.hotplug = True
# Prepare NIC modifications
self.nicmod = _PrepareContainerMods(self.op.nics, _InstNicModPrivate)
......
......@@ -2035,11 +2035,16 @@ class KVMHypervisor(hv_base.BaseHypervisor):
@raise errors.HypervisorError: in one of the previous cases
"""
output = self._CallMonitorCommand(instance.name, self._INFO_VERSION_CMD)
try:
output = self._CallMonitorCommand(instance.name, self._INFO_VERSION_CMD)
except errors.HypervisorError:
raise errors.HotplugError("Instance is probably down")
# TODO: search for netdev_add, drive_add, device_add.....
match = self._INFO_VERSION_RE.search(output.stdout)
if not match:
raise errors.HotplugError("Try hotplug only in running instances.")
raise errors.HotplugError("Cannot parse qemu version via monitor")
v_major, v_min, _, _ = match.groups()
if (int(v_major), int(v_min)) < (1, 0):
raise errors.HotplugError("Hotplug not supported for qemu versions < 1.0")
......
......@@ -1140,6 +1140,7 @@ MODIFY
| [\--submit] [\--print-job-id]
| [\--ignore-ipolicy]
| [\--hotplug]
| [\--hotplug-if-possible]
| {*instance*}
Modifies the memory size, number of vcpus, ip address, MAC address
......@@ -1227,6 +1228,11 @@ userspace access mode can not be hotplugged (yet) d) if hotplug fails
for existing NIC modification interactive verification is needed unless
``--force`` option is passed.
If ``--hotplug-if-possible`` is given then ganeti won't abort in case
hotplug is not supported. It will continue execution and modification
will take place after reboot. This covers use cases where instances are
not running or hypervisor is not KVM.
See **ganeti**\(7) for a description of ``--submit`` and other common
options.
......
......@@ -658,6 +658,7 @@ $(genOpCode "OpCode"
, withDoc "Whether to mark the instance as offline" pOffline
, pIpConflictsCheck
, pHotplug
, pHotplugIfPossible
],
"instance_name")
, ("OpInstanceGrowDisk",
......
......@@ -95,6 +95,7 @@ module Ganeti.OpParams
, pDiskState
, pIgnoreIpolicy
, pHotplug
, pHotplugIfPossible
, pAllowRuntimeChgs
, pInstDisks
, pDiskTemplate
......@@ -516,6 +517,9 @@ pGroupName =
pHotplug :: Field
pHotplug = defaultFalse "hotplug"
pHotplugIfPossible :: Field
pHotplugIfPossible = defaultFalse "hotplug_if_possible"
pInstances :: Field
pInstances =
withDoc "List of instances" .
......
......@@ -290,7 +290,7 @@ instance Arbitrary OpCodes.OpCode where
pure emptyJSObject <*> arbitrary <*> genMaybe genNodeNameNE <*>
return Nothing <*> genMaybe genNodeNameNE <*> return Nothing <*>
genMaybe genNameNE <*> pure emptyJSObject <*> arbitrary <*>
arbitrary <*> arbitrary <*> arbitrary
arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
"OP_INSTANCE_GROW_DISK" ->
OpCodes.OpInstanceGrowDisk <$> genFQDN <*> return Nothing <*>
arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
......
......@@ -1961,10 +1961,34 @@ class TestLUInstanceSetParams(CmdlibTestCase):
nics=[(constants.DDM_ADD, -1, {})])
self.ExecOpCode(op)
def testNoHotplugSupport(self):
op = self.CopyOpCode(self.op,
nics=[(constants.DDM_ADD, -1, {})],
hotplug=True)
self.rpc.call_hotplug_supported.return_value = \
self.RpcResultsBuilder() \
.CreateFailedNodeResult(self.master)
self.ExecOpCodeExpectOpPrereqError(op, "Hotplug is not possible")
self.assertTrue(self.rpc.call_hotplug_supported.called)
def testHotplugIfPossible(self):
op = self.CopyOpCode(self.op,
nics=[(constants.DDM_ADD, -1, {})],
hotplug_if_possible=True)
self.rpc.call_hotplug_supported.return_value = \
self.RpcResultsBuilder() \
.CreateFailedNodeResult(self.master)
self.ExecOpCode(op)
self.assertTrue(self.rpc.call_hotplug_supported.called)
self.assertFalse(self.rpc.call_hotplug_device.called)
def testHotAddNic(self):
op = self.CopyOpCode(self.op,
nics=[(constants.DDM_ADD, -1, {})],
hotplug=True)
self.rpc.call_hotplug_supported.return_value = \
self.RpcResultsBuilder() \
.CreateSuccessfulNodeResult(self.master)
self.ExecOpCode(op)
self.assertTrue(self.rpc.call_hotplug_supported.called)
self.assertTrue(self.rpc.call_hotplug_device.called)
......@@ -2056,6 +2080,9 @@ class TestLUInstanceSetParams(CmdlibTestCase):
op = self.CopyOpCode(self.op,
nics=[(constants.DDM_MODIFY, 0, {})],
hotplug=True)
self.rpc.call_hotplug_supported.return_value = \
self.RpcResultsBuilder() \
.CreateSuccessfulNodeResult(self.master)
self.ExecOpCode(op)
self.assertTrue(self.rpc.call_hotplug_supported.called)
self.assertTrue(self.rpc.call_hotplug_device.called)
......@@ -2081,6 +2108,9 @@ class TestLUInstanceSetParams(CmdlibTestCase):
instance_name=inst.name,
nics=[(constants.DDM_REMOVE, 0, {})],
hotplug=True)
self.rpc.call_hotplug_supported.return_value = \
self.RpcResultsBuilder() \
.CreateSuccessfulNodeResult(self.master)
self.ExecOpCode(op)
self.assertTrue(self.rpc.call_hotplug_supported.called)
self.assertTrue(self.rpc.call_hotplug_device.called)
......@@ -2170,6 +2200,9 @@ class TestLUInstanceSetParams(CmdlibTestCase):
constants.IDISK_SIZE: 1024,
}]],
hotplug=True)
self.rpc.call_hotplug_supported.return_value = \
self.RpcResultsBuilder() \
.CreateSuccessfulNodeResult(self.master)
self.ExecOpCode(op)
self.assertTrue(self.rpc.call_hotplug_supported.called)
self.assertTrue(self.rpc.call_blockdev_create.called)
......@@ -2184,6 +2217,9 @@ class TestLUInstanceSetParams(CmdlibTestCase):
disks=[[constants.DDM_REMOVE, -1,
{}]],
hotplug=True)
self.rpc.call_hotplug_supported.return_value = \
self.RpcResultsBuilder() \
.CreateSuccessfulNodeResult(self.master)
self.ExecOpCode(op)
self.assertTrue(self.rpc.call_hotplug_supported.called)
self.assertTrue(self.rpc.call_hotplug_device.called)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment