From c0a146a129f7081a5f24dcbc0c60028d41c6c004 Mon Sep 17 00:00:00 2001 From: Michael Hanselmann <hansmi@google.com> Date: Thu, 21 Jul 2011 15:20:45 +0200 Subject: [PATCH] Implement instance failover via RAPI No idea why this was missed before. Signed-off-by: Michael Hanselmann <hansmi@google.com> Reviewed-by: Iustin Pop <iustin@google.com> --- doc/rapi.rst | 18 ++++++++++++++++ lib/rapi/client.py | 32 +++++++++++++++++++++++++++++ lib/rapi/connector.py | 2 ++ lib/rapi/rlib2.py | 19 +++++++++++++++++ qa/ganeti-qa.py | 2 ++ qa/qa_rapi.py | 8 ++++++++ test/ganeti.rapi.client_unittest.py | 30 +++++++++++++++++++++++++++ 7 files changed, 111 insertions(+) diff --git a/doc/rapi.rst b/doc/rapi.rst index 49e82342c..f8853cbb9 100644 --- a/doc/rapi.rst +++ b/doc/rapi.rst @@ -876,6 +876,24 @@ Body parameters: :exclude: instance_name, live +``/2/instances/[instance_name]/failover`` ++++++++++++++++++++++++++++++++++++++++++ + +Does a failover of an instance. + +Supports the following commands: ``PUT``. + +``PUT`` +~~~~~~~ + +Returns a job ID. + +Body parameters: + +.. opcode_params:: OP_INSTANCE_FAILOVER + :exclude: instance_name + + ``/2/instances/[instance_name]/rename`` ++++++++++++++++++++++++++++++++++++++++ diff --git a/lib/rapi/client.py b/lib/rapi/client.py index 953c1ddd3..a4be072db 100644 --- a/lib/rapi/client.py +++ b/lib/rapi/client.py @@ -1073,6 +1073,38 @@ class GanetiRapiClient(object): # pylint: disable-msg=R0904 ("/%s/instances/%s/migrate" % (GANETI_RAPI_VERSION, instance)), None, body) + def FailoverInstance(self, instance, iallocator=None, + ignore_consistency=None, target_node=None): + """Does a failover of an instance. + + @type instance: string + @param instance: Instance name + @type iallocator: string + @param iallocator: Iallocator for deciding the target node for + shared-storage instances + @type ignore_consistency: bool + @param ignore_consistency: Whether to ignore disk consistency + @type target_node: string + @param target_node: Target node for shared-storage instances + @rtype: string + @return: job id + + """ + body = {} + + if iallocator is not None: + body["iallocator"] = iallocator + + if ignore_consistency is not None: + body["ignore_consistency"] = ignore_consistency + + if target_node is not None: + body["target_node"] = target_node + + return self._SendRequest(HTTP_PUT, + ("/%s/instances/%s/failover" % + (GANETI_RAPI_VERSION, instance)), None, body) + def RenameInstance(self, instance, new_name, ip_check=None, name_check=None): """Changes the name of an instance. diff --git a/lib/rapi/connector.py b/lib/rapi/connector.py index cb4bb296c..55dfe86f3 100644 --- a/lib/rapi/connector.py +++ b/lib/rapi/connector.py @@ -211,6 +211,8 @@ def GetHandlers(node_name_pattern, instance_name_pattern, rlib2.R_2_instances_name_export, re.compile(r'^/2/instances/(%s)/migrate$' % instance_name_pattern): rlib2.R_2_instances_name_migrate, + re.compile(r'^/2/instances/(%s)/failover$' % instance_name_pattern): + rlib2.R_2_instances_name_failover, re.compile(r'^/2/instances/(%s)/rename$' % instance_name_pattern): rlib2.R_2_instances_name_rename, re.compile(r'^/2/instances/(%s)/modify$' % instance_name_pattern): diff --git a/lib/rapi/rlib2.py b/lib/rapi/rlib2.py index 8d14ae48e..37df839e2 100644 --- a/lib/rapi/rlib2.py +++ b/lib/rapi/rlib2.py @@ -1122,6 +1122,25 @@ class R_2_instances_name_migrate(baserlib.R_Generic): return baserlib.SubmitJob([op]) +class R_2_instances_name_failover(baserlib.R_Generic): + """/2/instances/[instance_name]/failover resource. + + """ + def PUT(self): + """Does a failover of an instance. + + @return: a job id + + """ + baserlib.CheckType(self.request_body, dict, "Body contents") + + op = baserlib.FillOpcode(opcodes.OpInstanceFailover, self.request_body, { + "instance_name": self.items[0], + }) + + return baserlib.SubmitJob([op]) + + def _ParseRenameInstanceRequest(name, data): """Parses a request for renaming an instance. diff --git a/qa/ganeti-qa.py b/qa/ganeti-qa.py index 3da635172..8b3d8f93f 100755 --- a/qa/ganeti-qa.py +++ b/qa/ganeti-qa.py @@ -366,6 +366,8 @@ def RunHardwareFailureTests(instance, pnode, snode): """ RunTestIf("instance-failover", qa_instance.TestInstanceFailover, instance) + RunTestIf(["instance-failover", "rapi"], + qa_rapi.TestRapiInstanceFailover, instance) RunTestIf("instance-migrate", qa_instance.TestInstanceMigrate, instance) RunTestIf(["instance-migrate", "rapi"], diff --git a/qa/qa_rapi.py b/qa/qa_rapi.py index cdf454a44..e06d5e8de 100644 --- a/qa/qa_rapi.py +++ b/qa/qa_rapi.py @@ -590,6 +590,14 @@ def TestRapiInstanceMigrate(instance): _WaitForRapiJob(_rapi_client.MigrateInstance(instance["name"])) +def TestRapiInstanceFailover(instance): + """Test failing over instance via RAPI""" + # Move to secondary node + _WaitForRapiJob(_rapi_client.FailoverInstance(instance["name"])) + # And back to previous primary + _WaitForRapiJob(_rapi_client.FailoverInstance(instance["name"])) + + def TestRapiInstanceRename(rename_source, rename_target): """Test renaming instance via RAPI""" _WaitForRapiJob(_rapi_client.RenameInstance(rename_source, rename_target)) diff --git a/test/ganeti.rapi.client_unittest.py b/test/ganeti.rapi.client_unittest.py index e512be28f..d7af45ad3 100755 --- a/test/ganeti.rapi.client_unittest.py +++ b/test/ganeti.rapi.client_unittest.py @@ -738,6 +738,36 @@ class GanetiRapiClientTests(testutils.GanetiTestCase): self.assertEqual(data["mode"], mode) self.assertEqual(data["cleanup"], cleanup) + def testFailoverInstanceDefaults(self): + self.rapi.AddResponse("7639") + job_id = self.client.FailoverInstance("inst13579") + self.assertEqual(job_id, 7639) + self.assertHandler(rlib2.R_2_instances_name_failover) + self.assertItems(["inst13579"]) + + data = serializer.LoadJson(self.rapi.GetLastRequestData()) + self.assertFalse(data) + + def testFailoverInstance(self): + for iallocator in ["dumb", "hail"]: + for ignore_consistency in [False, True]: + for target_node in ["node-a", "node2"]: + self.rapi.AddResponse("19161") + job_id = \ + self.client.FailoverInstance("inst251", iallocator=iallocator, + ignore_consistency=ignore_consistency, + target_node=target_node) + self.assertEqual(job_id, 19161) + self.assertHandler(rlib2.R_2_instances_name_failover) + self.assertItems(["inst251"]) + + data = serializer.LoadJson(self.rapi.GetLastRequestData()) + self.assertEqual(len(data), 3) + self.assertEqual(data["iallocator"], iallocator) + self.assertEqual(data["ignore_consistency"], ignore_consistency) + self.assertEqual(data["target_node"], target_node) + self.assertEqual(self.rapi.CountPending(), 0) + def testRenameInstanceDefaults(self): new_name = "newnametha7euqu" self.rapi.AddResponse("8791") -- GitLab