Commit c0a146a1 authored by Michael Hanselmann's avatar Michael Hanselmann

Implement instance failover via RAPI

No idea why this was missed before.
Signed-off-by: default avatarMichael Hanselmann <hansmi@google.com>
Reviewed-by: default avatarIustin Pop <iustin@google.com>
parent fcb21ad7
......@@ -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``
++++++++++++++++++++++++++++++++++++++++
......
......@@ -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.
......
......@@ -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):
......
......@@ -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.
......
......@@ -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"],
......
......@@ -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))
......
......@@ -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")
......
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