From ac194f985f02821bea38ccc38d1256a2250ed471 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Ribaud?= Date: Mon, 22 Aug 2022 12:24:57 +0200 Subject: [PATCH] Microversion 2.91: Support specifying destination host to unshelve This patch adds ``host`` to openstacksdk unshelve api. This can help administrators to specify a ``host`` to unshelve a shelve offloaded server starting from 2.91 microversion. Implements: blueprint unshelve-to-host Change-Id: I34c5989be4710c863cce24d6f55bd1faae86cd52 --- openstack/compute/v2/server.py | 31 ++++++++++-- .../tests/unit/compute/v2/test_server.py | 50 +++++++++++++++++++ ...lve-to-specific-host-84666d440dce4a73.yaml | 7 +++ 3 files changed, 83 insertions(+), 5 deletions(-) create mode 100644 releasenotes/notes/unshelve-to-specific-host-84666d440dce4a73.yaml diff --git a/openstack/compute/v2/server.py b/openstack/compute/v2/server.py index fef30fd1d..e2bd75ba6 100644 --- a/openstack/compute/v2/server.py +++ b/openstack/compute/v2/server.py @@ -40,6 +40,11 @@ class Server(resource.Resource, metadata.MetadataMixin, tag.TagMixin): allow_delete = True allow_list = True + # Sentinel used to differentiate API called without parameter or None + # Ex unshelve API can be called without an availability_zone or with + # availability_zone = None to unpin the az. + _sentinel = object() + _query_mapping = resource.QueryParameters( "auto_disk_config", "availability_zone", "created_at", "description", "flavor", @@ -65,7 +70,7 @@ class Server(resource.Resource, metadata.MetadataMixin, tag.TagMixin): **tag.TagMixin._tag_query_parameters ) - _max_microversion = '2.73' + _max_microversion = '2.91' #: A list of dictionaries holding links relevant to this server. links = resource.Body('links') @@ -497,10 +502,26 @@ class Server(resource.Resource, metadata.MetadataMixin, tag.TagMixin): body = {"shelve": None} self._action(session, body) - def unshelve(self, session, availability_zone=None): - body = {"unshelve": None} - if availability_zone: - body["unshelve"] = {"availability_zone": availability_zone} + def unshelve(self, session, availability_zone=_sentinel, host=None): + """ + Unshelve -- Unshelve the server. + + :param availability_zone: If specified the instance will be unshelved + to the availability_zone. + If None is passed the instance defined availability_zone is unpin + and the instance will be scheduled to any availability_zone (free + scheduling). + If not specified the instance will be unshelved to either its + defined availability_zone or any availability_zone + (free scheduling). + :param host: If specified the host to unshelve the instance. + """ + data = {} + if host: + data["host"] = host + if availability_zone is None or isinstance(availability_zone, str): + data["availability_zone"] = availability_zone + body = {'unshelve': data or None} self._action(session, body) def migrate(self, session): diff --git a/openstack/tests/unit/compute/v2/test_server.py b/openstack/tests/unit/compute/v2/test_server.py index 0676e4515..0a6861dc0 100644 --- a/openstack/tests/unit/compute/v2/test_server.py +++ b/openstack/tests/unit/compute/v2/test_server.py @@ -893,6 +893,56 @@ class TestServer(base.TestCase): microversion=self.sess.default_microversion, ) + def test_unshelve_unpin_az(self): + sot = server.Server(**EXAMPLE) + + res = sot.unshelve(self.sess, availability_zone=None) + + self.assertIsNone(res) + url = 'servers/IDENTIFIER/action' + body = {"unshelve": { + "availability_zone": None + }} + headers = {'Accept': ''} + self.sess.post.assert_called_with( + url, json=body, headers=headers, + microversion=self.sess.default_microversion) + + def test_unshelve_host(self): + sot = server.Server(**EXAMPLE) + + res = sot.unshelve(self.sess, host=sot.hypervisor_hostname) + + self.assertIsNone(res) + url = 'servers/IDENTIFIER/action' + body = {"unshelve": { + "host": sot.hypervisor_hostname + }} + headers = {'Accept': ''} + self.sess.post.assert_called_with( + url, json=body, headers=headers, + microversion=self.sess.default_microversion) + + def test_unshelve_host_and_availability_zone(self): + sot = server.Server(**EXAMPLE) + + res = sot.unshelve( + self.sess, + availability_zone=sot.availability_zone, + host=sot.hypervisor_hostname + ) + + self.assertIsNone(res) + url = 'servers/IDENTIFIER/action' + body = {"unshelve": { + "availability_zone": sot.availability_zone, + "host": sot.hypervisor_hostname + }} + headers = {'Accept': ''} + self.sess.post.assert_called_with( + url, json=body, headers=headers, + microversion=self.sess.default_microversion) + def test_migrate(self): sot = server.Server(**EXAMPLE) diff --git a/releasenotes/notes/unshelve-to-specific-host-84666d440dce4a73.yaml b/releasenotes/notes/unshelve-to-specific-host-84666d440dce4a73.yaml new file mode 100644 index 000000000..5a73890a2 --- /dev/null +++ b/releasenotes/notes/unshelve-to-specific-host-84666d440dce4a73.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + Add SDK support for Nova microversion 2.91. This microversion + allows specifying a destination host to unshelve a shelve + offloaded server. And availability zone can be set to None to unpin + the availability zone of a server.