diff --git a/lib/backend.py b/lib/backend.py index b2924c042295868d2b320e5e2f31d5572f942deb..68fd39339d505b696030760c8269f1d6c79d7831 100644 --- a/lib/backend.py +++ b/lib/backend.py @@ -1328,13 +1328,17 @@ def _GatherAndLinkBlockDevs(instance): return block_devices -def StartInstance(instance, startup_paused): +def StartInstance(instance, startup_paused, reason, store_reason=True): """Start an instance. @type instance: L{objects.Instance} @param instance: the instance object @type startup_paused: bool @param instance: pause instance at startup? + @type reason: list of reasons + @param reason: the reason trail for this startup + @type store_reason: boolean + @param store_reason: whether to store the shutdown reason trail on file @rtype: None """ @@ -1348,6 +1352,8 @@ def StartInstance(instance, startup_paused): block_devices = _GatherAndLinkBlockDevs(instance) hyper = hypervisor.GetHypervisor(instance.hypervisor) hyper.StartInstance(instance, block_devices, startup_paused) + if store_reason: + _StoreInstReasonTrail(instance.name, reason) except errors.BlockDeviceError, err: _Fail("Block device error: %s", err, exc=True) except errors.HypervisorError, err: @@ -1467,7 +1473,7 @@ def InstanceReboot(instance, reboot_type, shutdown_timeout, reason): elif reboot_type == constants.INSTANCE_REBOOT_HARD: try: InstanceShutdown(instance, shutdown_timeout, reason, store_reason=False) - result = StartInstance(instance, False) + result = StartInstance(instance, False, reason, store_reason=False) _StoreInstReasonTrail(instance.name, reason) return result except errors.HypervisorError, err: diff --git a/lib/cmdlib.py b/lib/cmdlib.py index a67acc63801bc305720f377e29b1287f8360e299..38c21896468f9d453c44a00f857a4514e086ac79 100644 --- a/lib/cmdlib.py +++ b/lib/cmdlib.py @@ -7349,6 +7349,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) @@ -7365,7 +7366,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) @@ -7458,7 +7459,8 @@ class LUInstanceReboot(LogicalUnit): instance.name) _StartInstanceDisks(self, instance, ignore_secondaries) result = self.rpc.call_instance_start(node_current, - (instance, None, None), False) + (instance, None, None), False, + reason) msg = result.fail_msg if msg: _ShutdownInstanceDisks(self, instance) @@ -8557,7 +8559,8 @@ class LUInstanceMove(LogicalUnit): raise errors.OpExecError("Can't activate the instance's disks") result = self.rpc.call_instance_start(target_node, - (instance, None, None), False) + (instance, None, None), False, + self.op.reason) msg = result.fail_msg if msg: _ShutdownInstanceDisks(self, instance) @@ -9297,7 +9300,7 @@ class TLMigrateInstance(Tasklet): self.feedback_fn("* starting the instance on the target node %s" % target_node) result = self.rpc.call_instance_start(target_node, (instance, None, None), - False) + False, self.lu.op.reason) msg = result.fail_msg if msg: _ShutdownInstanceDisks(self.lu, instance) @@ -11229,7 +11232,7 @@ class LUInstanceCreate(LogicalUnit): logging.info("Starting instance %s on node %s", instance, pnode_name) feedback_fn("* starting instance...") result = self.rpc.call_instance_start(pnode_name, (iobj, None, None), - False) + False, self.op.reason) result.Raise("Could not start instance") return list(iobj.all_nodes) @@ -14963,7 +14966,8 @@ class LUBackupExport(LogicalUnit): assert not activate_disks feedback_fn("Starting instance %s" % instance.name) result = self.rpc.call_instance_start(src_node, - (instance, None, None), False) + (instance, None, None), False, + self.op.reason) msg = result.fail_msg if msg: feedback_fn("Failed to start instance: %s" % msg) diff --git a/lib/rapi/client.py b/lib/rapi/client.py index e6e575144a5431395868c3a736140e98b72d833b..eb3c21e144a2fb1e93ce75ee451fe4f2af35ec7c 100644 --- a/lib/rapi/client.py +++ b/lib/rapi/client.py @@ -1057,7 +1057,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=None): """Starts up an instance. @type instance: str @@ -1066,6 +1067,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: string + @param reason: the reason for the startup @rtype: string @return: job id @@ -1073,6 +1076,7 @@ class GanetiRapiClient(object): # pylint: disable=R0904 query = [] _AppendDryRunIf(query, dry_run) _AppendIf(query, no_remember, ("no_remember", 1)) + _AppendIf(query, reason, ("reason", reason)) return self._SendRequest(HTTP_PUT, ("/%s/instances/%s/startup" % diff --git a/lib/rpc_defs.py b/lib/rpc_defs.py index af625a95056a3047a4050305b325695b141302e0..c6f9a09c50a5a186f611f2d36d90a68ad58ec32f 100644 --- a/lib/rpc_defs.py +++ b/lib/rpc_defs.py @@ -279,6 +279,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", None, "The 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 f4d9df2529583a7712155b1585e18ccdd4e6b60d..a7d7a888cefad2ef5f2cc0ffb32f07e17f30654e 100644 --- a/lib/server/noded.py +++ b/lib/server/noded.py @@ -598,9 +598,10 @@ class NodeRequestHandler(http.server.HttpServerHandler): """Start an instance. """ - (instance_name, startup_paused) = params + (instance_name, startup_paused, trail) = params instance = objects.Instance.FromDict(instance_name) - return backend.StartInstance(instance, startup_paused) + _extendReasonTrail(trail, "start") + return backend.StartInstance(instance, startup_paused, trail) @staticmethod def perspective_migration_info(params): diff --git a/test/py/ganeti.rapi.client_unittest.py b/test/py/ganeti.rapi.client_unittest.py index a122d4e71bf6d68f37e8a054fcfa3c52e41db955..555ee21be4ca464a0149bdb80eb6a09e4f275405 100755 --- a/test/py/ganeti.rapi.client_unittest.py +++ b/test/py/ganeti.rapi.client_unittest.py @@ -636,12 +636,23 @@ class GanetiRapiClientTests(testutils.GanetiTestCase): self.assertQuery("reason", None) def testStartupInstance(self): + self.rapi.AddResponse("27149") + self.assertEqual(27149, self.client.StartupInstance("bar-instance", + dry_run=True, + reason="New")) + self.assertHandler(rlib2.R_2_instances_name_startup) + self.assertItems(["bar-instance"]) + self.assertDryRun() + self.assertQuery("reason", ["New"]) + + 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", 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 14b4d75740cf400d46c16b41ada82c6bbb1609cc..cb198114529ef82250883d6ee9b0e3b04d61049e 100755 --- a/test/py/ganeti.rapi.rlib2_unittest.py +++ b/test/py/ganeti.rapi.rlib2_unittest.py @@ -400,6 +400,7 @@ class TestInstanceStartup(unittest.TestCase): handler = _CreateHandler(rlib2.R_2_instances_name_startup, ["inst31083"], { "force": ["1"], "no_remember": ["1"], + "reason": ["Newly created instance"], }, {}, clfactory) job_id = handler.PUT() @@ -413,6 +414,12 @@ class TestInstanceStartup(unittest.TestCase): self.assertTrue(op.no_remember) self.assertTrue(op.force) self.assertFalse(op.dry_run) + self.assertEqual(op.reason[0][0], constants.OPCODE_REASON_SRC_USER) + self.assertEqual(op.reason[0][1], "Newly created instance") + self.assertEqual(op.reason[1][0], + "%s:%s" % (constants.OPCODE_REASON_SRC_RLIB2, + "instances_name_startup")) + self.assertEqual(op.reason[1][1], "") self.assertRaises(IndexError, cl.GetNextSubmittedJob)