The change is not backwards compatible, see the updated NEWS file.
Signed-off-by: Michael Hanselmann <[email protected]>
---
NEWS | 4 ++++
doc/rapi.rst | 25 ++++++++-----------------
lib/rapi/client.py | 33 +++++++++++++++++++++------------
lib/rapi/rlib2.py | 35 ++++++-----------------------------
test/ganeti.rapi.client_unittest.py | 6 ++++--
5 files changed, 43 insertions(+), 60 deletions(-)
diff --git a/NEWS b/NEWS
index 172277a..56322fb 100644
--- a/NEWS
+++ b/NEWS
@@ -9,6 +9,10 @@ Version 2.5.0 beta1
- The default of the ``/2/instances/[instance_name]/rename`` RAPI
resource's ``ip_check`` parameter changed from ``True`` to ``False``
to match the underlying LUXI interface
+- The ``/2/nodes/[node_name]/evacuate`` RAPI resource was changed to use
+ body parameters, see :doc:`RAPI documentation <rapi>`. Neither server
+ nor RAPI client maintain backwards-compatibility as the underlying
+ operation changed in an incompatible way.
- When creating file-based instances via RAPI, the ``file_driver``
parameter no longer defaults to ``loop`` and must be specified
- The deprecated "bridge" nic parameter is no longer supported. Use
diff --git a/doc/rapi.rst b/doc/rapi.rst
index fbc9970..f5aa245 100644
--- a/doc/rapi.rst
+++ b/doc/rapi.rst
@@ -1161,32 +1161,23 @@ It supports the following commands: ``GET``.
``/2/nodes/[node_name]/evacuate``
+++++++++++++++++++++++++++++++++
-Evacuates all secondary instances off a node.
+Evacuates instances off a node.
It supports the following commands: ``POST``.
``POST``
~~~~~~~~
-To evacuate a node, either one of the ``iallocator`` or ``remote_node``
-parameters must be passed::
+Returns a job ID. The result of the job will contain the IDs of the
+individual jobs submitted to evacuate the node.
- evacuate?iallocator=[iallocator]
- evacuate?remote_node=[nodeX.example.com]
-
-The result value will be a list, each element being a triple of the job
-id (for this specific evacuation), the instance which is being evacuated
-by this job, and the node to which it is being relocated. In case the
-node is already empty, the result will be an empty list (without any
-jobs being submitted).
+Body parameters:
-And additional parameter ``early_release`` signifies whether to try to
-parallelize the evacuations, at the risk of increasing I/O contention
-and increasing the chances of data loss, if the primary node of any of
-the instances being evacuated is not fully healthy.
+.. opcode_params:: OP_NODE_EVACUATE
+ :exclude: nodes
-If the dry-run parameter was specified, then the evacuation jobs were
-not actually submitted, and the job IDs will be null.
+Up to and including Ganeti 2.4 query arguments were used. Those are no
+longer supported.
``/2/nodes/[node_name]/migrate``
diff --git a/lib/rapi/client.py b/lib/rapi/client.py
index d2aa4ac..27dd89d 100644
--- a/lib/rapi/client.py
+++ b/lib/rapi/client.py
@@ -1250,7 +1250,8 @@ class GanetiRapiClient(object): # pylint:
disable-msg=R0904
None, None)
def EvacuateNode(self, node, iallocator=None, remote_node=None,
- dry_run=False, early_release=False):
+ dry_run=False, early_release=None,
+ primary=None, secondary=None):
"""Evacuates instances from a Ganeti node.
@type node: str
@@ -1263,11 +1264,13 @@ class GanetiRapiClient(object): # pylint:
disable-msg=R0904
@param dry_run: whether to perform a dry run
@type early_release: bool
@param early_release: whether to enable parallelization
+ @type primary: bool
+ @param primary: Whether to evacuate primary instances
+ @type secondary: bool
+ @param secondary: Whether to evacuate secondary instances
- @rtype: list
- @return: list of (job ID, instance name, new secondary node); if
- dry_run was specified, then the actual move jobs were not
- submitted and the job IDs will be C{None}
+ @rtype: string
+ @return: job id
@raises GanetiApiError: if an iallocator and remote_node are both
specified
@@ -1277,18 +1280,24 @@ class GanetiRapiClient(object): # pylint:
disable-msg=R0904
raise GanetiApiError("Only one of iallocator or remote_node can be used")
query = []
- if iallocator:
- query.append(("iallocator", iallocator))
- if remote_node:
- query.append(("remote_node", remote_node))
if dry_run:
query.append(("dry-run", 1))
- if early_release:
- query.append(("early_release", 1))
+
+ body = {}
+ if iallocator is not None:
+ body["iallocator"] = iallocator
+ if remote_node is not None:
+ body["remote_node"] = remote_node
+ if early_release is not None:
+ body["early_release"] = early_release
+ if primary is not None:
+ body["primary"] = primary
+ if secondary is not None:
+ body["secondary"] = secondary
return self._SendRequest(HTTP_POST,
("/%s/nodes/%s/evacuate" %
- (GANETI_RAPI_VERSION, node)), query, None)
+ (GANETI_RAPI_VERSION, node)), query, body)
def MigrateNode(self, node, mode=None, dry_run=False, iallocator=None,
target_node=None):
diff --git a/lib/rapi/rlib2.py b/lib/rapi/rlib2.py
index d6bd9ce..b3c5a88 100644
--- a/lib/rapi/rlib2.py
+++ b/lib/rapi/rlib2.py
@@ -414,38 +414,15 @@ class R_2_nodes_name_evacuate(baserlib.R_Generic):
"""
def POST(self):
- """Evacuate all secondary instances off a node.
+ """Evacuate all instances off a node.
"""
- node_name = self.items[0]
- remote_node = self._checkStringVariable("remote_node", default=None)
- iallocator = self._checkStringVariable("iallocator", default=None)
- early_r = bool(self._checkIntVariable("early_release", default=0))
- dry_run = bool(self.dryRun())
-
- cl = baserlib.GetClient()
-
- op = opcodes.OpNodeEvacStrategy(nodes=[node_name],
- iallocator=iallocator,
- remote_node=remote_node)
-
- job_id = baserlib.SubmitJob([op], cl)
- # we use custom feedback function, instead of print we log the status
- result = cli.PollJob(job_id, cl, feedback_fn=baserlib.FeedbackFn)
+ op = baserlib.FillOpcode(opcodes.OpNodeEvacuate, self.request_body, {
+ "nodes": [node_name],
+ "dry_run": self.dryRun(),
+ })
- jobs = []
- for iname, node in result[0]:
- if dry_run:
- jid = None
- else:
- op = opcodes.OpInstanceReplaceDisks(instance_name=iname,
- remote_node=node, disks=[],
- mode=constants.REPLACE_DISK_CHG,
- early_release=early_r)
- jid = baserlib.SubmitJob([op])
- jobs.append((jid, iname, node))
-
- return jobs
+ return baserlib.SubmitJob([op])
class R_2_nodes_name_migrate(baserlib.R_Generic):
diff --git a/test/ganeti.rapi.client_unittest.py
b/test/ganeti.rapi.client_unittest.py
index 23dacf8..844513e 100755
--- a/test/ganeti.rapi.client_unittest.py
+++ b/test/ganeti.rapi.client_unittest.py
@@ -822,13 +822,15 @@ class GanetiRapiClientTests(testutils.GanetiTestCase):
self.assertEqual(9876, job_id)
self.assertHandler(rlib2.R_2_nodes_name_evacuate)
self.assertItems(["node-1"])
- self.assertQuery("remote_node", ["node-2"])
+ self.assertEqual(serializer.LoadJson(self.rapi.GetLastRequestData()),
+ { "remote_node": "node-2", })
self.rapi.AddResponse("8888")
job_id = self.client.EvacuateNode("node-3", iallocator="hail",
dry_run=True)
self.assertEqual(8888, job_id)
self.assertItems(["node-3"])
- self.assertQuery("iallocator", ["hail"])
+ self.assertEqual(serializer.LoadJson(self.rapi.GetLastRequestData()),
+ { "iallocator": "hail", })
self.assertDryRun()
self.assertRaises(client.GanetiApiError,
--
1.7.3.5