diff --git a/lib/backend.py b/lib/backend.py index abe6b3944b7358e9d74e3a1fc7283e9911428e85..7d8315d6bbf4675563adc914455fe51d91e95ddd 100644 --- a/lib/backend.py +++ b/lib/backend.py @@ -1424,7 +1424,7 @@ def InstanceShutdown(instance, timeout): _RemoveBlockDevLinks(iname, instance.disks) -def InstanceReboot(instance, reboot_type, shutdown_timeout): +def InstanceReboot(instance, reboot_type, shutdown_timeout, reason): """Reboot an instance. @type instance: L{objects.Instance} @@ -1442,6 +1442,8 @@ def InstanceReboot(instance, reboot_type, shutdown_timeout): instance (instead of a call_instance_reboot RPC) @type shutdown_timeout: integer @param shutdown_timeout: maximum timeout for soft shutdown + @type reason: list of reasons + @param reason: the reason trail for this reboot @rtype: None """ @@ -1460,6 +1462,7 @@ def InstanceReboot(instance, reboot_type, shutdown_timeout): try: InstanceShutdown(instance, shutdown_timeout) result = StartInstance(instance, False) + _StoreInstReasonTrail(instance.name, reason) return result except errors.HypervisorError, err: _Fail("Failed to hard reboot instance %s: %s", instance.name, err) diff --git a/lib/cmdlib.py b/lib/cmdlib.py index 2c6d216a37eed83df42e81cb05d9055ba8f34c3f..d6f16d07df5cd74351b24208de3af8c5782fdb95 100644 --- a/lib/cmdlib.py +++ b/lib/cmdlib.py @@ -7440,6 +7440,7 @@ class LUInstanceReboot(LogicalUnit): instance = self.instance ignore_secondaries = self.op.ignore_secondaries reboot_type = self.op.reboot_type + reason = self.op.reason remote_info = self.rpc.call_instance_info(instance.primary_node, instance.name, @@ -7455,7 +7456,7 @@ class LUInstanceReboot(LogicalUnit): self.cfg.SetDiskID(disk, node_current) result = self.rpc.call_instance_reboot(node_current, instance, reboot_type, - self.op.shutdown_timeout) + self.op.shutdown_timeout, reason) result.Raise("Could not reboot instance") else: if instance_running: diff --git a/lib/rapi/client.py b/lib/rapi/client.py index 5fc576f8869dda39e7f1058c93698d084990a58c..ace4d1e0cd143e20627352439c76a035d5743797 100644 --- a/lib/rapi/client.py +++ b/lib/rapi/client.py @@ -1001,7 +1001,7 @@ class GanetiRapiClient(object): # pylint: disable=R0904 (GANETI_RAPI_VERSION, instance)), query, None) def RebootInstance(self, instance, reboot_type=None, ignore_secondaries=None, - dry_run=False): + dry_run=False, reason=None): """Reboots an instance. @type instance: str @@ -1013,6 +1013,8 @@ class GanetiRapiClient(object): # pylint: disable=R0904 while re-assembling disks (in hard-reboot mode only) @type dry_run: bool @param dry_run: whether to perform a dry run + @type reason: string + @param reason: the reason for the reboot @rtype: string @return: job id @@ -1022,6 +1024,7 @@ class GanetiRapiClient(object): # pylint: disable=R0904 _AppendIf(query, reboot_type, ("type", reboot_type)) _AppendIf(query, ignore_secondaries is not None, ("ignore_secondaries", ignore_secondaries)) + _AppendIf(query, reason, ("reason", reason)) return self._SendRequest(HTTP_POST, ("/%s/instances/%s/reboot" % diff --git a/lib/rpc_defs.py b/lib/rpc_defs.py index f5f1c34b23f50452bca6f6a31934f778ca55ceb1..bf3cbe8bc345110f5b70921bbfb8c9648a6dc9b6 100644 --- a/lib/rpc_defs.py +++ b/lib/rpc_defs.py @@ -230,6 +230,7 @@ _INSTANCE_CALLS = [ ("inst", ED_INST_DICT, "Instance object"), ("reboot_type", None, None), ("shutdown_timeout", None, None), + ("reason", None, "The reason for the reboot"), ], None, None, "Returns the list of running instances on the given nodes"), ("instance_shutdown", SINGLE, None, constants.RPC_TMO_NORMAL, [ ("instance", ED_INST_DICT, "Instance object"), diff --git a/lib/server/noded.py b/lib/server/noded.py index 2e1ceaade81ef93eaf8da72141f70a4b2a7a2d3c..c81352b4475bc39db7bd542341a299ab54456fcc 100644 --- a/lib/server/noded.py +++ b/lib/server/noded.py @@ -660,7 +660,10 @@ class NodeRequestHandler(http.server.HttpServerHandler): instance = objects.Instance.FromDict(params[0]) reboot_type = params[1] shutdown_timeout = params[2] - return backend.InstanceReboot(instance, reboot_type, shutdown_timeout) + trail = params[3] + _extendReasonTrail(trail, "reboot") + return backend.InstanceReboot(instance, reboot_type, shutdown_timeout, + trail) @staticmethod def perspective_instance_balloon_memory(params): diff --git a/test/py/ganeti.rapi.client_unittest.py b/test/py/ganeti.rapi.client_unittest.py index ab518e5fe15605ba86fa611cac1d3282f6da0deb..3174c690d9ca60a772d99d6939e927c813e79ad1 100755 --- a/test/py/ganeti.rapi.client_unittest.py +++ b/test/py/ganeti.rapi.client_unittest.py @@ -594,13 +594,15 @@ class GanetiRapiClientTests(testutils.GanetiTestCase): def testRebootInstance(self): self.rapi.AddResponse("6146") job_id = self.client.RebootInstance("i-bar", reboot_type="hard", - ignore_secondaries=True, dry_run=True) + ignore_secondaries=True, dry_run=True, + reason="Updates") self.assertEqual(6146, job_id) self.assertHandler(rlib2.R_2_instances_name_reboot) self.assertItems(["i-bar"]) self.assertDryRun() self.assertQuery("type", ["hard"]) self.assertQuery("ignore_secondaries", ["1"]) + self.assertQuery("reason", ["Updates"]) def testRebootInstanceDefaultReason(self): self.rapi.AddResponse("6146") @@ -612,6 +614,7 @@ class GanetiRapiClientTests(testutils.GanetiTestCase): self.assertDryRun() self.assertQuery("type", ["hard"]) self.assertQuery("ignore_secondaries", ["1"]) + self.assertQuery("reason", None) def testShutdownInstance(self): self.rapi.AddResponse("1487") diff --git a/test/py/ganeti.rapi.rlib2_unittest.py b/test/py/ganeti.rapi.rlib2_unittest.py index d4d36027c4b8771e2a381e8bec06a3d7320e84c6..e19c925c94d12738e10c8afa1a93107990a6ffe1 100755 --- a/test/py/ganeti.rapi.rlib2_unittest.py +++ b/test/py/ganeti.rapi.rlib2_unittest.py @@ -370,6 +370,7 @@ class TestInstanceReboot(unittest.TestCase): handler = _CreateHandler(rlib2.R_2_instances_name_reboot, ["inst847"], { "dry-run": ["1"], "ignore_secondaries": ["1"], + "reason": ["System update"], }, {}, clfactory) job_id = handler.POST() @@ -383,6 +384,12 @@ class TestInstanceReboot(unittest.TestCase): self.assertEqual(op.reboot_type, constants.INSTANCE_REBOOT_HARD) self.assertTrue(op.ignore_secondaries) self.assertTrue(op.dry_run) + self.assertEqual(op.reason[0][0], constants.OPCODE_REASON_SRC_USER) + self.assertEqual(op.reason[0][1], "System update") + self.assertEqual(op.reason[1][0], + "%s:%s" % (constants.OPCODE_REASON_SRC_RLIB2, + "instances_name_reboot")) + self.assertEqual(op.reason[1][1], "") self.assertRaises(IndexError, cl.GetNextSubmittedJob)