summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStephen Finucane <stephenfin@redhat.com>2021-01-12 17:18:56 +0000
committerStephen Finucane <stephenfin@redhat.com>2021-03-02 12:14:12 +0000
commit54d4da112a6e84db5bda497364a49b9debfc2904 (patch)
tree5f6624791c0c6fab0dbdf64e0727fbfb937ccd02
parente45953927898b639de9dbcba8edb6d07bcb4cba3 (diff)
downloadpython-novaclient-54d4da112a6e84db5bda497364a49b9debfc2904.tar.gz
Add support for microversion v2.8817.4.0
The key change here is that the 'GET /os-hypervisors/{id}/uptime' API will now returns a HTTP 404 starting in 2.88. The 'GET /os-hypervisors/{id}' will instead now include an 'uptime' value. The 'novaclient.v2.hypervisors.HypervisorManager.uptime' method is updated to handle this. Change-Id: Ib99fbd820a586c14527ff64b319df0b7a44e1b8b Signed-off-by: Stephen Finucane <stephenfin@redhat.com>
-rw-r--r--novaclient/__init__.py2
-rw-r--r--novaclient/tests/unit/fixture_data/hypervisors.py89
-rw-r--r--novaclient/tests/unit/v2/test_hypervisors.py104
-rw-r--r--novaclient/tests/unit/v2/test_shell.py10
-rw-r--r--novaclient/v2/hypervisors.py28
-rw-r--r--novaclient/v2/shell.py10
-rw-r--r--releasenotes/notes/microversion-v2_88-d91136020e3a3621.yaml16
7 files changed, 228 insertions, 31 deletions
diff --git a/novaclient/__init__.py b/novaclient/__init__.py
index 0b29e4f1..50ad89d8 100644
--- a/novaclient/__init__.py
+++ b/novaclient/__init__.py
@@ -25,4 +25,4 @@ API_MIN_VERSION = api_versions.APIVersion("2.1")
# when client supported the max version, and bumped sequentially, otherwise
# the client may break due to server side new version may include some
# backward incompatible change.
-API_MAX_VERSION = api_versions.APIVersion("2.87")
+API_MAX_VERSION = api_versions.APIVersion("2.88")
diff --git a/novaclient/tests/unit/fixture_data/hypervisors.py b/novaclient/tests/unit/fixture_data/hypervisors.py
index eeb5d51e..3c04daa7 100644
--- a/novaclient/tests/unit/fixture_data/hypervisors.py
+++ b/novaclient/tests/unit/fixture_data/hypervisors.py
@@ -27,15 +27,41 @@ class V1(base.Fixture):
service_id_1 = 1
service_id_2 = 2
+ @staticmethod
+ def _transform_hypervisor_details(hypervisor):
+ """Transform a detailed hypervisor view from 2.53 to 2.88."""
+ del hypervisor['current_workload']
+ del hypervisor['disk_available_least']
+ del hypervisor['free_ram_mb']
+ del hypervisor['free_disk_gb']
+ del hypervisor['local_gb']
+ del hypervisor['local_gb_used']
+ del hypervisor['memory_mb']
+ del hypervisor['memory_mb_used']
+ del hypervisor['running_vms']
+ del hypervisor['vcpus']
+ del hypervisor['vcpus_used']
+ hypervisor['uptime'] = 'fake uptime'
+
def setUp(self):
super(V1, self).setUp()
- uuid_as_id = (api_versions.APIVersion(self.api_version) >=
- api_versions.APIVersion('2.53'))
+
+ api_version = api_versions.APIVersion(self.api_version)
get_os_hypervisors = {
'hypervisors': [
- {'id': self.hyper_id_1, 'hypervisor_hostname': 'hyper1'},
- {'id': self.hyper_id_2, 'hypervisor_hostname': 'hyper2'},
+ {
+ 'id': self.hyper_id_1,
+ 'hypervisor_hostname': 'hyper1',
+ 'state': 'up',
+ 'status': 'enabled',
+ },
+ {
+ 'id': self.hyper_id_2,
+ 'hypervisor_hostname': 'hyper2',
+ 'state': 'up',
+ 'status': 'enabled',
+ },
]
}
@@ -67,7 +93,9 @@ class V1(base.Fixture):
'current_workload': 2,
'running_vms': 2,
'cpu_info': 'cpu_info',
- 'disk_available_least': 100
+ 'disk_available_least': 100,
+ 'state': 'up',
+ 'status': 'enabled',
},
{
'id': self.hyper_id_2,
@@ -89,11 +117,17 @@ class V1(base.Fixture):
'current_workload': 2,
'running_vms': 2,
'cpu_info': 'cpu_info',
- 'disk_available_least': 100
+ 'disk_available_least': 100,
+ 'state': 'up',
+ 'status': 'enabled',
}
]
}
+ if api_version >= api_versions.APIVersion('2.88'):
+ for hypervisor in get_os_hypervisors_detail['hypervisors']:
+ self._transform_hypervisor_details(hypervisor)
+
self.requests_mock.get(self.url('detail'),
json=get_os_hypervisors_detail,
headers=self.headers)
@@ -121,12 +155,22 @@ class V1(base.Fixture):
get_os_hypervisors_search = {
'hypervisors': [
- {'id': self.hyper_id_1, 'hypervisor_hostname': 'hyper1'},
- {'id': self.hyper_id_2, 'hypervisor_hostname': 'hyper2'}
+ {
+ 'id': self.hyper_id_1,
+ 'hypervisor_hostname': 'hyper1',
+ 'state': 'up',
+ 'status': 'enabled',
+ },
+ {
+ 'id': self.hyper_id_2,
+ 'hypervisor_hostname': 'hyper2',
+ 'state': 'up',
+ 'status': 'enabled',
+ },
]
}
- if uuid_as_id:
+ if api_version >= api_versions.APIVersion('2.53'):
url = self.url(hypervisor_hostname_pattern='hyper')
else:
url = self.url('hyper', 'search')
@@ -134,7 +178,7 @@ class V1(base.Fixture):
json=get_os_hypervisors_search,
headers=self.headers)
- if uuid_as_id:
+ if api_version >= api_versions.APIVersion('2.53'):
get_os_hypervisors_search_u_v2_53 = {
'error_name': 'BadRequest',
'message': 'Invalid input for query parameters '
@@ -164,6 +208,8 @@ class V1(base.Fixture):
{
'id': self.hyper_id_1,
'hypervisor_hostname': 'hyper1',
+ 'state': 'up',
+ 'status': 'enabled',
'servers': [
{'name': 'inst1', 'uuid': 'uuid1'},
{'name': 'inst2', 'uuid': 'uuid2'}
@@ -172,6 +218,8 @@ class V1(base.Fixture):
{
'id': self.hyper_id_2,
'hypervisor_hostname': 'hyper2',
+ 'state': 'up',
+ 'status': 'enabled',
'servers': [
{'name': 'inst3', 'uuid': 'uuid3'},
{'name': 'inst4', 'uuid': 'uuid4'}
@@ -180,7 +228,7 @@ class V1(base.Fixture):
]
}
- if uuid_as_id:
+ if api_version >= api_versions.APIVersion('2.53'):
url = self.url(hypervisor_hostname_pattern='hyper',
with_servers=True)
else:
@@ -207,10 +255,16 @@ class V1(base.Fixture):
'current_workload': 2,
'running_vms': 2,
'cpu_info': 'cpu_info',
- 'disk_available_least': 100
+ 'disk_available_least': 100,
+ 'state': 'up',
+ 'status': 'enabled',
}
}
+ if api_version >= api_versions.APIVersion('2.88'):
+ self._transform_hypervisor_details(
+ get_os_hypervisors_hyper1['hypervisor'])
+
self.requests_mock.get(self.url(self.hyper_id_1),
json=get_os_hypervisors_hyper1,
headers=self.headers)
@@ -219,7 +273,9 @@ class V1(base.Fixture):
'hypervisor': {
'id': self.hyper_id_1,
'hypervisor_hostname': 'hyper1',
- 'uptime': 'fake uptime'
+ 'uptime': 'fake uptime',
+ 'state': 'up',
+ 'status': 'enabled',
}
}
@@ -228,10 +284,15 @@ class V1(base.Fixture):
headers=self.headers)
-class V2_53(V1):
+class V253(V1):
"""Fixture data for the os-hypervisors 2.53 API."""
api_version = '2.53'
hyper_id_1 = 'd480b1b6-2255-43c2-b2c2-d60d42c2c074'
hyper_id_2 = '43a8214d-f36a-4fc0-a25c-3cf35c17522d'
service_id_1 = 'a87743ff-9c29-42ff-805d-2444659b5fc0'
service_id_2 = '0486ab8b-1cfc-4ccb-9d94-9f22ec8bbd6b'
+
+
+class V288(V253):
+ """Fixture data for the os-hypervisors 2.88 API."""
+ api_version = '2.88'
diff --git a/novaclient/tests/unit/v2/test_hypervisors.py b/novaclient/tests/unit/v2/test_hypervisors.py
index 1c216b64..be48914f 100644
--- a/novaclient/tests/unit/v2/test_hypervisors.py
+++ b/novaclient/tests/unit/v2/test_hypervisors.py
@@ -63,7 +63,9 @@ class HypervisorsTest(utils.FixturedTestCase):
current_workload=2,
running_vms=2,
cpu_info='cpu_info',
- disk_available_least=100),
+ disk_available_least=100,
+ state='up',
+ status='enabled'),
dict(id=self.data_fixture.hyper_id_2,
service=dict(id=self.data_fixture.service_id_2,
host="compute2"),
@@ -81,7 +83,24 @@ class HypervisorsTest(utils.FixturedTestCase):
current_workload=2,
running_vms=2,
cpu_info='cpu_info',
- disk_available_least=100)]
+ disk_available_least=100,
+ state='up',
+ status='enabled')]
+
+ if self.cs.api_version >= api_versions.APIVersion('2.88'):
+ for hypervisor in expected:
+ del hypervisor['current_workload']
+ del hypervisor['disk_available_least']
+ del hypervisor['free_ram_mb']
+ del hypervisor['free_disk_gb']
+ del hypervisor['local_gb']
+ del hypervisor['local_gb_used']
+ del hypervisor['memory_mb']
+ del hypervisor['memory_mb_used']
+ del hypervisor['running_vms']
+ del hypervisor['vcpus']
+ del hypervisor['vcpus_used']
+ hypervisor['uptime'] = 'fake uptime'
result = self.cs.hypervisors.list()
self.assert_request_id(result, fakes.FAKE_REQUEST_ID_LIST)
@@ -93,9 +112,13 @@ class HypervisorsTest(utils.FixturedTestCase):
def test_hypervisor_search(self):
expected = [
dict(id=self.data_fixture.hyper_id_1,
- hypervisor_hostname='hyper1'),
+ hypervisor_hostname='hyper1',
+ state='up',
+ status='enabled'),
dict(id=self.data_fixture.hyper_id_2,
- hypervisor_hostname='hyper2')]
+ hypervisor_hostname='hyper2',
+ state='up',
+ status='enabled')]
result = self.cs.hypervisors.search('hyper')
self.assert_request_id(result, fakes.FAKE_REQUEST_ID_LIST)
@@ -131,11 +154,15 @@ class HypervisorsTest(utils.FixturedTestCase):
expected = [
dict(id=self.data_fixture.hyper_id_1,
hypervisor_hostname='hyper1',
+ state='up',
+ status='enabled',
servers=[
dict(name='inst1', uuid='uuid1'),
dict(name='inst2', uuid='uuid2')]),
dict(id=self.data_fixture.hyper_id_2,
hypervisor_hostname='hyper2',
+ state='up',
+ status='enabled',
servers=[
dict(name='inst3', uuid='uuid3'),
dict(name='inst4', uuid='uuid4')]),
@@ -171,7 +198,23 @@ class HypervisorsTest(utils.FixturedTestCase):
current_workload=2,
running_vms=2,
cpu_info='cpu_info',
- disk_available_least=100)
+ disk_available_least=100,
+ state='up',
+ status='enabled')
+
+ if self.cs.api_version >= api_versions.APIVersion('2.88'):
+ del expected['current_workload']
+ del expected['disk_available_least']
+ del expected['free_ram_mb']
+ del expected['free_disk_gb']
+ del expected['local_gb']
+ del expected['local_gb_used']
+ del expected['memory_mb']
+ del expected['memory_mb_used']
+ del expected['running_vms']
+ del expected['vcpus']
+ del expected['vcpus_used']
+ expected['uptime'] = 'fake uptime'
result = self.cs.hypervisors.get(self.data_fixture.hyper_id_1)
self.assert_request_id(result, fakes.FAKE_REQUEST_ID_LIST)
@@ -184,7 +227,9 @@ class HypervisorsTest(utils.FixturedTestCase):
expected = dict(
id=self.data_fixture.hyper_id_1,
hypervisor_hostname="hyper1",
- uptime="fake uptime")
+ uptime="fake uptime",
+ state='up',
+ status='enabled')
result = self.cs.hypervisors.uptime(self.data_fixture.hyper_id_1)
self.assert_request_id(result, fakes.FAKE_REQUEST_ID_LIST)
@@ -215,11 +260,6 @@ class HypervisorsTest(utils.FixturedTestCase):
self.compare_to_expected(expected, result)
- def test_hypervisor_statistics_data_model(self):
- result = self.cs.hypervisor_stats.statistics()
- self.assert_request_id(result, fakes.FAKE_REQUEST_ID_LIST)
- self.assert_called('GET', '/os-hypervisors/statistics')
-
# Test for Bug #1370415, the line below used to raise AttributeError
self.assertEqual("<HypervisorStats: 2 Hypervisors>",
result.__repr__())
@@ -237,19 +277,23 @@ class HypervisorsV233Test(HypervisorsTest):
self.assertEqual([v], self.requests_mock.last_request.qs[k])
-class HypervisorsV2_53Test(HypervisorsV233Test):
+class HypervisorsV253Test(HypervisorsV233Test):
"""Tests the os-hypervisors 2.53 API bindings."""
- data_fixture_class = data.V2_53
+ data_fixture_class = data.V253
def setUp(self):
- super(HypervisorsV2_53Test, self).setUp()
+ super(HypervisorsV253Test, self).setUp()
self.cs.api_version = api_versions.APIVersion("2.53")
def test_hypervisor_search_detailed(self):
expected = [
dict(id=self.data_fixture.hyper_id_1,
+ state='up',
+ status='enabled',
hypervisor_hostname='hyper1'),
dict(id=self.data_fixture.hyper_id_2,
+ state='up',
+ status='enabled',
hypervisor_hostname='hyper2')]
result = self.cs.hypervisors.search('hyper', detailed=True)
self.assert_request_id(result, fakes.FAKE_REQUEST_ID_LIST)
@@ -257,3 +301,35 @@ class HypervisorsV2_53Test(HypervisorsV233Test):
'GET', '/os-hypervisors/detail?hypervisor_hostname_pattern=hyper')
for idx, hyper in enumerate(result):
self.compare_to_expected(expected[idx], hyper)
+
+
+class HypervisorsV288Test(HypervisorsV253Test):
+ data_fixture_class = data.V288
+
+ def setUp(self):
+ super().setUp()
+ self.cs.api_version = api_versions.APIVersion('2.88')
+
+ def test_hypervisor_uptime(self):
+ expected = {
+ 'id': self.data_fixture.hyper_id_1,
+ 'hypervisor_hostname': 'hyper1',
+ 'uptime': 'fake uptime',
+ 'state': 'up',
+ 'status': 'enabled',
+ }
+
+ result = self.cs.hypervisors.uptime(self.data_fixture.hyper_id_1)
+ self.assert_request_id(result, fakes.FAKE_REQUEST_ID_LIST)
+ self.assert_called(
+ 'GET', '/os-hypervisors/%s' % self.data_fixture.hyper_id_1)
+
+ self.compare_to_expected(expected, result)
+
+ def test_hypervisor_statistics(self):
+ exc = self.assertRaises(
+ exceptions.UnsupportedVersion,
+ self.cs.hypervisor_stats.statistics)
+ self.assertIn(
+ "The 'statistics' API is removed in API version 2.88 or later.",
+ str(exc))
diff --git a/novaclient/tests/unit/v2/test_shell.py b/novaclient/tests/unit/v2/test_shell.py
index fd9cbfac..17c8974d 100644
--- a/novaclient/tests/unit/v2/test_shell.py
+++ b/novaclient/tests/unit/v2/test_shell.py
@@ -3575,6 +3575,16 @@ class ShellTest(utils.TestCase):
self.run_command('hypervisor-stats')
self.assert_called('GET', '/os-hypervisors/statistics')
+ def test_hypervisor_stats_v2_88(self):
+ """Tests nova hypervisor-stats at the 2.88 microversion."""
+ ex = self.assertRaises(
+ exceptions.CommandError, self.run_command,
+ 'hypervisor-stats', api_version='2.88')
+ self.assertIn(
+ 'The hypervisor-stats command is not supported in API version '
+ '2.88 or later.',
+ str(ex))
+
def test_quota_show(self):
self.run_command(
'quota-show --tenant '
diff --git a/novaclient/v2/hypervisors.py b/novaclient/v2/hypervisors.py
index c705dc65..f0cc5863 100644
--- a/novaclient/v2/hypervisors.py
+++ b/novaclient/v2/hypervisors.py
@@ -123,8 +123,25 @@ class HypervisorManager(base.ManagerWithFind):
:param hypervisor: Either a Hypervisor object or an ID. Starting with
microversion 2.53 the ID must be a UUID value.
"""
- return self._get("/os-hypervisors/%s/uptime" % base.getid(hypervisor),
- "hypervisor")
+ # Starting with microversion 2.88, the '/os-hypervisors/{id}/uptime'
+ # route is removed in favour of returning 'uptime' in the response of
+ # the '/os-hypervisors/{id}' route. This behaves slightly differently,
+ # in that it won't error out if a virt driver doesn't support reporting
+ # uptime or if the hypervisor is down, but it's a good enough
+ # approximation
+ if self.api_version < api_versions.APIVersion("2.88"):
+ return self._get(
+ "/os-hypervisors/%s/uptime" % base.getid(hypervisor),
+ "hypervisor")
+
+ resp, body = self.api.client.get(
+ "/os-hypervisors/%s" % base.getid(hypervisor)
+ )
+ content = {
+ k: v for k, v in body['hypervisor'].items()
+ if k in ('id', 'hypervisor_hostname', 'state', 'status', 'uptime')
+ }
+ return self.resource_class(self, content, loaded=True, resp=resp)
def statistics(self):
"""
@@ -145,8 +162,15 @@ class HypervisorStats(base.Resource):
class HypervisorStatsManager(base.Manager):
resource_class = HypervisorStats
+ @api_versions.wraps("2.0", "2.87")
def statistics(self):
"""
Get hypervisor statistics over all compute nodes.
"""
return self._get("/os-hypervisors/statistics", "hypervisor_statistics")
+
+ @api_versions.wraps("2.88")
+ def statistics(self):
+ raise exceptions.UnsupportedVersion(
+ _("The 'statistics' API is removed in API version 2.88 or later.")
+ )
diff --git a/novaclient/v2/shell.py b/novaclient/v2/shell.py
index 23f5486f..eb0315fd 100644
--- a/novaclient/v2/shell.py
+++ b/novaclient/v2/shell.py
@@ -4049,12 +4049,22 @@ def do_hypervisor_uptime(cs, args):
utils.print_dict(hyper.to_dict())
+@api_versions.wraps('2.0', '2.87')
def do_hypervisor_stats(cs, args):
"""Get hypervisor statistics over all compute nodes."""
stats = cs.hypervisor_stats.statistics()
utils.print_dict(stats.to_dict())
+@api_versions.wraps('2.88')
+def do_hypervisor_stats(cs, args):
+ msg = _(
+ "The hypervisor-stats command is not supported in API version 2.88 "
+ "or later."
+ )
+ raise exceptions.CommandError(msg)
+
+
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
@utils.arg(
'--port',
diff --git a/releasenotes/notes/microversion-v2_88-d91136020e3a3621.yaml b/releasenotes/notes/microversion-v2_88-d91136020e3a3621.yaml
new file mode 100644
index 00000000..994c6802
--- /dev/null
+++ b/releasenotes/notes/microversion-v2_88-d91136020e3a3621.yaml
@@ -0,0 +1,16 @@
+---
+features:
+ - |
+ Added support for `microversion 2.88`_. The
+ ``novaclient.v2.hypervisors.HypervisorManager.uptime`` method will now
+ transparently switch between the ``/os-hypervisors/{id}/uptime`` API,
+ which is deprecated in 2.88, and the ``/os-hypervisors/{id}`` API, which
+ now includes uptime information, based on the microversion.
+
+ .. _microversion 2.88: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id80
+deprecations:
+ - |
+ The ``nova hypervisor-stats`` command and underlying
+ ``novaclient.v2.hypervisors.HypervisorStatsManager.statistics`` API are
+ deprecated starting in microversion 2.88 and will return an error starting
+ on this version.