Add support to the Startup command for specifying the reason for the last status change.
Signed-off-by: Michele Tartara <[email protected]> --- lib/backend.py | 5 +++-- lib/client/gnt_instance.py | 6 ++++-- lib/cmdlib.py | 3 ++- lib/constants.py | 1 + lib/opcodes.py | 1 + lib/rapi/client.py | 6 +++++- lib/rapi/rlib2.py | 4 ++++ lib/rpc_defs.py | 1 + lib/server/noded.py | 7 +++++-- src/Ganeti/OpCodes.hs | 1 + test/hs/Test/Ganeti/OpCodes.hs | 2 +- test/py/ganeti.rapi.client_unittest.py | 12 ++++++++++++ test/py/ganeti.rapi.rlib2_unittest.py | 3 +++ 13 files changed, 43 insertions(+), 9 deletions(-) diff --git a/lib/backend.py b/lib/backend.py index 17d746b..359d6ed 100644 --- a/lib/backend.py +++ b/lib/backend.py @@ -1355,7 +1355,7 @@ def _GatherAndLinkBlockDevs(instance): return block_devices -def StartInstance(instance, startup_paused): +def StartInstance(instance, startup_paused, reason): """Start an instance. @type instance: L{objects.Instance} @@ -1375,6 +1375,7 @@ def StartInstance(instance, startup_paused): block_devices = _GatherAndLinkBlockDevs(instance) hyper = hypervisor.GetHypervisor(instance.hypervisor) hyper.StartInstance(instance, block_devices, startup_paused) + reason.Store(instance.name) except errors.BlockDeviceError, err: _Fail("Block device error: %s", err, exc=True) except errors.HypervisorError, err: @@ -1487,7 +1488,7 @@ def InstanceReboot(instance, reboot_type, shutdown_timeout, reason): elif reboot_type == constants.INSTANCE_REBOOT_HARD: try: InstanceShutdown(instance, shutdown_timeout) - result = StartInstance(instance, False) + result = StartInstance(instance, False, reason) reason.Store(instance.name) return result except errors.HypervisorError, err: diff --git a/lib/client/gnt_instance.py b/lib/client/gnt_instance.py index 4f1538d..e44fb19 100644 --- a/lib/client/gnt_instance.py +++ b/lib/client/gnt_instance.py @@ -608,7 +608,9 @@ def _StartupInstance(name, opts): force=opts.force, ignore_offline_nodes=opts.ignore_offline, no_remember=opts.no_remember, - startup_paused=opts.startup_paused) + startup_paused=opts.startup_paused, + reason=(constants.INSTANCE_REASON_SOURCE_CLI, + opts.reason)) # do not add these parameters to the opcode unless they're defined if opts.hvparams: op.hvparams = opts.hvparams @@ -1548,7 +1550,7 @@ commands = { m_node_tags_opt, m_pri_node_tags_opt, m_sec_node_tags_opt, m_inst_tags_opt, m_clust_opt, m_inst_opt, SUBMIT_OPT, HVOPTS_OPT, BACKEND_OPT, DRY_RUN_OPT, PRIORITY_OPT, IGNORE_OFFLINE_OPT, - NO_REMEMBER_OPT, STARTUP_PAUSED_OPT], + NO_REMEMBER_OPT, STARTUP_PAUSED_OPT, REASON_OPT], "<instance>", "Starts an instance"), "reboot": ( GenericManyOps("reboot", _RebootInstance), [ArgInstance()], diff --git a/lib/cmdlib.py b/lib/cmdlib.py index b8c2318..0d871fc 100644 --- a/lib/cmdlib.py +++ b/lib/cmdlib.py @@ -7295,6 +7295,7 @@ class LUInstanceStartup(LogicalUnit): """ instance = self.instance force = self.op.force + reason = self.op.reason if not self.op.no_remember: self.cfg.MarkInstanceUp(instance.name) @@ -7311,7 +7312,7 @@ class LUInstanceStartup(LogicalUnit): self.rpc.call_instance_start(node_current, (instance, self.op.hvparams, self.op.beparams), - self.op.startup_paused) + self.op.startup_paused, reason) msg = result.fail_msg if msg: _ShutdownInstanceDisks(self, instance) diff --git a/lib/constants.py b/lib/constants.py index 31bd688..0fbaac8 100644 --- a/lib/constants.py +++ b/lib/constants.py @@ -2349,6 +2349,7 @@ INSTANCE_REASON_SOURCES = compat.UniqueFrozenset([ # The default reasons for the change of state of an instance INSTANCE_REASON_REBOOT = "reboot" +INSTANCE_REASON_STARTUP = "startup" # Do not re-export imported modules del re, _vcsversion, _autoconf, socket, pathutils, compat diff --git a/lib/opcodes.py b/lib/opcodes.py index faffbac..ba789da 100644 --- a/lib/opcodes.py +++ b/lib/opcodes.py @@ -1433,6 +1433,7 @@ class OpInstanceStartup(OpCode): ("beparams", ht.EmptyDict, ht.TDict, "Temporary backend parameters"), _PNoRemember, _PStartupPaused, + _PReason, ] OP_RESULT = ht.TNone diff --git a/lib/rapi/client.py b/lib/rapi/client.py index d02faff..884b540 100644 --- a/lib/rapi/client.py +++ b/lib/rapi/client.py @@ -1054,7 +1054,8 @@ class GanetiRapiClient(object): # pylint: disable=R0904 ("/%s/instances/%s/shutdown" % (GANETI_RAPI_VERSION, instance)), query, body) - def StartupInstance(self, instance, dry_run=False, no_remember=False): + def StartupInstance(self, instance, dry_run=False, no_remember=False, + reason_text=None): """Starts up an instance. @type instance: str @@ -1063,6 +1064,8 @@ class GanetiRapiClient(object): # pylint: disable=R0904 @param dry_run: whether to perform a dry run @type no_remember: bool @param no_remember: if true, will not record the state change + @type reason_text: string + @param reason_text: the reason for the startup @rtype: string @return: job id @@ -1070,6 +1073,7 @@ class GanetiRapiClient(object): # pylint: disable=R0904 query = [] _AppendDryRunIf(query, dry_run) _AppendIf(query, no_remember, ("no-remember", 1)) + _AppendIf(query, reason_text, ("reason_text", reason_text)) return self._SendRequest(HTTP_PUT, ("/%s/instances/%s/startup" % diff --git a/lib/rapi/rlib2.py b/lib/rapi/rlib2.py index a475151..1fabbbb 100644 --- a/lib/rapi/rlib2.py +++ b/lib/rapi/rlib2.py @@ -1067,6 +1067,10 @@ class R_2_instances_name_startup(baserlib.OpcodeResource): "force": self.useForce(), "dry_run": self.dryRun(), "no_remember": bool(self._checkIntVariable("no_remember")), + "reason": + (constants.INSTANCE_REASON_SOURCE_RAPI, + self._checkStringVariable("reason_text", + default=constants.INSTANCE_REASON_REBOOT)), }) diff --git a/lib/rpc_defs.py b/lib/rpc_defs.py index a9ed9da..67fe3f3 100644 --- a/lib/rpc_defs.py +++ b/lib/rpc_defs.py @@ -278,6 +278,7 @@ _INSTANCE_CALLS = [ ("instance_start", SINGLE, None, constants.RPC_TMO_NORMAL, [ ("instance_hvp_bep", ED_INST_DICT_HVP_BEP_DP, None), ("startup_paused", None, None), + ("reason_text", None, "Reason for the startup"), ], None, None, "Starts an instance"), ("instance_os_add", SINGLE, None, constants.RPC_TMO_1DAY, [ ("instance_osp", ED_INST_DICT_OSP_DP, None), diff --git a/lib/server/noded.py b/lib/server/noded.py index 96ad1ac..946396b 100644 --- a/lib/server/noded.py +++ b/lib/server/noded.py @@ -586,9 +586,12 @@ class NodeRequestHandler(http.server.HttpServerHandler): """Start an instance. """ - (instance_name, startup_paused) = params + (instance_name, startup_paused, (reason_source, reason_text)) = params + reason_text = _DefaultAlternative(reason_text, + constants.INSTANCE_REASON_STARTUP) + reason = backend.InstReason(reason_source, reason_text) instance = objects.Instance.FromDict(instance_name) - return backend.StartInstance(instance, startup_paused) + return backend.StartInstance(instance, startup_paused, reason) @staticmethod def perspective_migration_info(params): diff --git a/src/Ganeti/OpCodes.hs b/src/Ganeti/OpCodes.hs index f720577..f09e95d 100644 --- a/src/Ganeti/OpCodes.hs +++ b/src/Ganeti/OpCodes.hs @@ -331,6 +331,7 @@ $(genOpCode "OpCode" , pTempBeParams , pNoRemember , pStartupPaused + , pReason ]) , ("OpInstanceShutdown", [ pInstanceName diff --git a/test/hs/Test/Ganeti/OpCodes.hs b/test/hs/Test/Ganeti/OpCodes.hs index 4c06ee2..c3c0aa8 100644 --- a/test/hs/Test/Ganeti/OpCodes.hs +++ b/test/hs/Test/Ganeti/OpCodes.hs @@ -235,7 +235,7 @@ instance Arbitrary OpCodes.OpCode where "OP_INSTANCE_STARTUP" -> OpCodes.OpInstanceStartup <$> genFQDN <*> arbitrary <*> arbitrary <*> pure emptyJSObject <*> pure emptyJSObject <*> - arbitrary <*> arbitrary + arbitrary <*> arbitrary <*> ((,) <$> arbitrary <*> genStringNE) "OP_INSTANCE_SHUTDOWN" -> OpCodes.OpInstanceShutdown <$> genFQDN <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary diff --git a/test/py/ganeti.rapi.client_unittest.py b/test/py/ganeti.rapi.client_unittest.py index e78a1d9..11e0969 100755 --- a/test/py/ganeti.rapi.client_unittest.py +++ b/test/py/ganeti.rapi.client_unittest.py @@ -626,11 +626,23 @@ class GanetiRapiClientTests(testutils.GanetiTestCase): def testStartupInstance(self): self.rapi.AddResponse("27149") + self.assertEqual(27149, + self.client.StartupInstance("bar-instance", + dry_run=True, + reason_text="NewInstance")) + self.assertHandler(rlib2.R_2_instances_name_startup) + self.assertItems(["bar-instance"]) + self.assertDryRun() + self.assertQuery("reason_text", ["NewInstance"]) + + def testStartupInstanceDefaultReason(self): + self.rapi.AddResponse("27149") self.assertEqual(27149, self.client.StartupInstance("bar-instance", dry_run=True)) self.assertHandler(rlib2.R_2_instances_name_startup) self.assertItems(["bar-instance"]) self.assertDryRun() + self.assertQuery("reason_text", None) def testReinstallInstance(self): self.rapi.AddResponse(serializer.DumpJson([])) diff --git a/test/py/ganeti.rapi.rlib2_unittest.py b/test/py/ganeti.rapi.rlib2_unittest.py index e19e800..531544e 100755 --- a/test/py/ganeti.rapi.rlib2_unittest.py +++ b/test/py/ganeti.rapi.rlib2_unittest.py @@ -396,6 +396,7 @@ class TestInstanceStartup(unittest.TestCase): handler = _CreateHandler(rlib2.R_2_instances_name_startup, ["inst31083"], { "force": ["1"], "no_remember": ["1"], + "reason_text": ["New Instance"], }, {}, clfactory) job_id = handler.PUT() @@ -409,6 +410,8 @@ class TestInstanceStartup(unittest.TestCase): self.assertTrue(op.no_remember) self.assertTrue(op.force) self.assertFalse(op.dry_run) + self.assertEqual(op.reason, + (constants.INSTANCE_REASON_SOURCE_RAPI, "New Instance")) self.assertRaises(IndexError, cl.GetNextSubmittedJob) -- 1.7.10.4
