Microversion 2.53 - services and hypervisors using UUIDs
Adds support for the 2.53 microversion which changes the os-services and os-hypervisors APIs to use a UUID for the ID value on the resource. Also, the PUT and GET API methods have changed a bit for both resources in this microversion, so the pythong API bindings and command lines have been adjusted accordingly. Part of blueprint service-hyper-uuid-in-api Change-Id: Ic721143cc154d91e74a8a9dd2c1e991045c94305
This commit is contained in:
parent
ed058c46a1
commit
5745beae5c
@ -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.52")
|
||||
API_MAX_VERSION = api_versions.APIVersion("2.53")
|
||||
|
@ -13,18 +13,32 @@
|
||||
import six
|
||||
|
||||
from novaclient.tests.functional import base
|
||||
from novaclient import utils
|
||||
|
||||
|
||||
class TestHypervisors(base.ClientTestBase):
|
||||
|
||||
COMPUTE_API_VERSION = "2.1"
|
||||
|
||||
def _test_list(self, cpu_info_type):
|
||||
def _test_list(self, cpu_info_type, uuid_as_id=False):
|
||||
hypervisors = self.client.hypervisors.list()
|
||||
if not len(hypervisors):
|
||||
self.fail("No hypervisors detected.")
|
||||
for hypervisor in hypervisors:
|
||||
self.assertIsInstance(hypervisor.cpu_info, cpu_info_type)
|
||||
if uuid_as_id:
|
||||
# microversion >= 2.53 returns a uuid for the id
|
||||
self.assertFalse(utils.is_integer_like(hypervisor.id),
|
||||
'Expected hypervisor.id to be a UUID.')
|
||||
self.assertFalse(
|
||||
utils.is_integer_like(hypervisor.service['id']),
|
||||
'Expected hypervisor.service.id to be a UUID.')
|
||||
else:
|
||||
self.assertTrue(utils.is_integer_like(hypervisor.id),
|
||||
'Expected hypervisor.id to be an integer.')
|
||||
self.assertTrue(
|
||||
utils.is_integer_like(hypervisor.service['id']),
|
||||
'Expected hypervisor.service.id to be an integer.')
|
||||
|
||||
def test_list(self):
|
||||
self._test_list(six.text_type)
|
||||
|
@ -19,3 +19,10 @@ class TestHypervisorsV28(test_hypervisors.TestHypervisors):
|
||||
|
||||
def test_list(self):
|
||||
self._test_list(dict)
|
||||
|
||||
|
||||
class TestHypervisorsV2_53(TestHypervisorsV28):
|
||||
COMPUTE_API_VERSION = "2.53"
|
||||
|
||||
def test_list(self):
|
||||
self._test_list(cpu_info_type=dict, uuid_as_id=True)
|
||||
|
@ -10,7 +10,9 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.functional import base
|
||||
from novaclient.tests.functional.v2.legacy import test_os_services
|
||||
from novaclient import utils
|
||||
|
||||
|
||||
class TestOsServicesNovaClientV211(test_os_services.TestOsServicesNovaClient):
|
||||
@ -42,3 +44,100 @@ class TestOsServicesNovaClientV211(test_os_services.TestOsServicesNovaClient):
|
||||
status = self._get_column_value_from_single_row_table(
|
||||
service, 'Forced down')
|
||||
self.assertEqual('False', status)
|
||||
|
||||
|
||||
class TestOsServicesNovaClientV2_53(base.ClientTestBase):
|
||||
"""Tests the nova service-* commands using the 2.53 microversion.
|
||||
|
||||
The main difference with the 2.53 microversion in these commands is
|
||||
the host/binary combination is replaced with the service.id as the
|
||||
unique identifier for a service.
|
||||
"""
|
||||
COMPUTE_API_VERSION = "2.53"
|
||||
|
||||
def test_os_services_list(self):
|
||||
table = self.nova('service-list')
|
||||
for serv in self.client.services.list():
|
||||
self.assertIn(serv.binary, table)
|
||||
# the id should not be an integer and should be in the table
|
||||
self.assertFalse(utils.is_integer_like(serv.id))
|
||||
self.assertIn(serv.id, table)
|
||||
|
||||
def test_os_service_disable_enable(self):
|
||||
# Disable and enable Nova services in accordance with list of nova
|
||||
# services returned by client
|
||||
# NOTE(sdague): service disable has the chance in racing
|
||||
# with other tests. Now functional tests for novaclient are launched
|
||||
# in serial way (https://review.openstack.org/#/c/217768/), but
|
||||
# it's a potential issue for making these tests parallel in the future
|
||||
for serv in self.client.services.list():
|
||||
# In Pike the os-services API was made multi-cell aware and it
|
||||
# looks up services by host, which uses the host mapping record
|
||||
# in the API DB which is only populated for nova-compute services,
|
||||
# effectively making it impossible to perform actions like enable
|
||||
# or disable non-nova-compute services since the API won't be able
|
||||
# to find them. So filter out anything that's not nova-compute.
|
||||
if serv.binary != 'nova-compute':
|
||||
continue
|
||||
service = self.nova('service-disable %s' % serv.id)
|
||||
self.addCleanup(self.nova, 'service-enable', params="%s" % serv.id)
|
||||
service_id = self._get_column_value_from_single_row_table(
|
||||
service, 'ID')
|
||||
self.assertEqual(serv.id, service_id)
|
||||
status = self._get_column_value_from_single_row_table(
|
||||
service, 'Status')
|
||||
self.assertEqual('disabled', status)
|
||||
service = self.nova('service-enable %s' % serv.id)
|
||||
service_id = self._get_column_value_from_single_row_table(
|
||||
service, 'ID')
|
||||
self.assertEqual(serv.id, service_id)
|
||||
status = self._get_column_value_from_single_row_table(
|
||||
service, 'Status')
|
||||
self.assertEqual('enabled', status)
|
||||
|
||||
def test_os_service_disable_log_reason(self):
|
||||
for serv in self.client.services.list():
|
||||
# In Pike the os-services API was made multi-cell aware and it
|
||||
# looks up services by host, which uses the host mapping record
|
||||
# in the API DB which is only populated for nova-compute services,
|
||||
# effectively making it impossible to perform actions like enable
|
||||
# or disable non-nova-compute services since the API won't be able
|
||||
# to find them. So filter out anything that's not nova-compute.
|
||||
if serv.binary != 'nova-compute':
|
||||
continue
|
||||
service = self.nova('service-disable --reason test_disable %s'
|
||||
% serv.id)
|
||||
self.addCleanup(self.nova, 'service-enable', params="%s" % serv.id)
|
||||
service_id = self._get_column_value_from_single_row_table(
|
||||
service, 'ID')
|
||||
self.assertEqual(serv.id, service_id)
|
||||
status = self._get_column_value_from_single_row_table(
|
||||
service, 'Status')
|
||||
log_reason = self._get_column_value_from_single_row_table(
|
||||
service, 'Disabled Reason')
|
||||
self.assertEqual('disabled', status)
|
||||
self.assertEqual('test_disable', log_reason)
|
||||
|
||||
def test_os_services_force_down_force_up(self):
|
||||
for serv in self.client.services.list():
|
||||
# In Pike the os-services API was made multi-cell aware and it
|
||||
# looks up services by host, which uses the host mapping record
|
||||
# in the API DB which is only populated for nova-compute services,
|
||||
# effectively making it impossible to perform actions like enable
|
||||
# or disable non-nova-compute services since the API won't be able
|
||||
# to find them. So filter out anything that's not nova-compute.
|
||||
if serv.binary != 'nova-compute':
|
||||
continue
|
||||
service = self.nova('service-force-down %s' % serv.id)
|
||||
self.addCleanup(self.nova, 'service-force-down --unset',
|
||||
params="%s" % serv.id)
|
||||
service_id = self._get_column_value_from_single_row_table(
|
||||
service, 'ID')
|
||||
self.assertEqual(serv.id, service_id)
|
||||
forced_down = self._get_column_value_from_single_row_table(
|
||||
service, 'Forced down')
|
||||
self.assertEqual('True', forced_down)
|
||||
service = self.nova('service-force-down --unset %s' % serv.id)
|
||||
forced_down = self._get_column_value_from_single_row_table(
|
||||
service, 'Forced down')
|
||||
self.assertEqual('False', forced_down)
|
||||
|
@ -10,20 +10,28 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient import api_versions
|
||||
from novaclient.tests.unit.fixture_data import base
|
||||
|
||||
|
||||
class V1(base.Fixture):
|
||||
|
||||
base_url = 'os-hypervisors'
|
||||
api_version = '2.1'
|
||||
hyper_id_1 = 1234
|
||||
hyper_id_2 = 5678
|
||||
service_id_1 = 1
|
||||
service_id_2 = 2
|
||||
|
||||
def setUp(self):
|
||||
super(V1, self).setUp()
|
||||
uuid_as_id = (api_versions.APIVersion(self.api_version) >=
|
||||
api_versions.APIVersion('2.53'))
|
||||
|
||||
get_os_hypervisors = {
|
||||
'hypervisors': [
|
||||
{'id': 1234, 'hypervisor_hostname': 'hyper1'},
|
||||
{'id': 5678, 'hypervisor_hostname': 'hyper2'},
|
||||
{'id': self.hyper_id_1, 'hypervisor_hostname': 'hyper1'},
|
||||
{'id': self.hyper_id_2, 'hypervisor_hostname': 'hyper2'},
|
||||
]
|
||||
}
|
||||
|
||||
@ -36,9 +44,9 @@ class V1(base.Fixture):
|
||||
get_os_hypervisors_detail = {
|
||||
'hypervisors': [
|
||||
{
|
||||
'id': 1234,
|
||||
'id': self.hyper_id_1,
|
||||
'service': {
|
||||
'id': 1,
|
||||
'id': self.service_id_1,
|
||||
'host': 'compute1',
|
||||
},
|
||||
'vcpus': 4,
|
||||
@ -58,9 +66,9 @@ class V1(base.Fixture):
|
||||
'disk_available_least': 100
|
||||
},
|
||||
{
|
||||
'id': 2,
|
||||
'id': self.hyper_id_2,
|
||||
'service': {
|
||||
'id': 2,
|
||||
'id': self.service_id_2,
|
||||
'host': 'compute2',
|
||||
},
|
||||
'vcpus': 4,
|
||||
@ -109,19 +117,23 @@ class V1(base.Fixture):
|
||||
|
||||
get_os_hypervisors_search = {
|
||||
'hypervisors': [
|
||||
{'id': 1234, 'hypervisor_hostname': 'hyper1'},
|
||||
{'id': 5678, 'hypervisor_hostname': 'hyper2'}
|
||||
{'id': self.hyper_id_1, 'hypervisor_hostname': 'hyper1'},
|
||||
{'id': self.hyper_id_2, 'hypervisor_hostname': 'hyper2'}
|
||||
]
|
||||
}
|
||||
|
||||
self.requests_mock.get(self.url('hyper', 'search'),
|
||||
if uuid_as_id:
|
||||
url = self.url(hypervisor_hostname_pattern='hyper')
|
||||
else:
|
||||
url = self.url('hyper', 'search')
|
||||
self.requests_mock.get(url,
|
||||
json=get_os_hypervisors_search,
|
||||
headers=self.headers)
|
||||
|
||||
get_hyper_server = {
|
||||
'hypervisors': [
|
||||
{
|
||||
'id': 1234,
|
||||
'id': self.hyper_id_1,
|
||||
'hypervisor_hostname': 'hyper1',
|
||||
'servers': [
|
||||
{'name': 'inst1', 'uuid': 'uuid1'},
|
||||
@ -129,7 +141,7 @@ class V1(base.Fixture):
|
||||
]
|
||||
},
|
||||
{
|
||||
'id': 5678,
|
||||
'id': self.hyper_id_2,
|
||||
'hypervisor_hostname': 'hyper2',
|
||||
'servers': [
|
||||
{'name': 'inst3', 'uuid': 'uuid3'},
|
||||
@ -139,14 +151,19 @@ class V1(base.Fixture):
|
||||
]
|
||||
}
|
||||
|
||||
self.requests_mock.get(self.url('hyper', 'servers'),
|
||||
if uuid_as_id:
|
||||
url = self.url(hypervisor_hostname_pattern='hyper',
|
||||
with_servers=True)
|
||||
else:
|
||||
url = self.url('hyper', 'servers')
|
||||
self.requests_mock.get(url,
|
||||
json=get_hyper_server,
|
||||
headers=self.headers)
|
||||
|
||||
get_os_hypervisors_1234 = {
|
||||
get_os_hypervisors_hyper1 = {
|
||||
'hypervisor': {
|
||||
'id': 1234,
|
||||
'service': {'id': 1, 'host': 'compute1'},
|
||||
'id': self.hyper_id_1,
|
||||
'service': {'id': self.service_id_1, 'host': 'compute1'},
|
||||
'vcpus': 4,
|
||||
'memory_mb': 10 * 1024,
|
||||
'local_gb': 250,
|
||||
@ -165,18 +182,27 @@ class V1(base.Fixture):
|
||||
}
|
||||
}
|
||||
|
||||
self.requests_mock.get(self.url(1234),
|
||||
json=get_os_hypervisors_1234,
|
||||
self.requests_mock.get(self.url(self.hyper_id_1),
|
||||
json=get_os_hypervisors_hyper1,
|
||||
headers=self.headers)
|
||||
|
||||
get_os_hypervisors_uptime = {
|
||||
'hypervisor': {
|
||||
'id': 1234,
|
||||
'id': self.hyper_id_1,
|
||||
'hypervisor_hostname': 'hyper1',
|
||||
'uptime': 'fake uptime'
|
||||
}
|
||||
}
|
||||
|
||||
self.requests_mock.get(self.url(1234, 'uptime'),
|
||||
self.requests_mock.get(self.url(self.hyper_id_1, 'uptime'),
|
||||
json=get_os_hypervisors_uptime,
|
||||
headers=self.headers)
|
||||
|
||||
|
||||
class V2_53(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'
|
||||
|
@ -54,6 +54,9 @@ FAKE_REQUEST_ID = fakes.FAKE_REQUEST_ID
|
||||
FAKE_REQUEST_ID_LIST = fakes.FAKE_REQUEST_ID_LIST
|
||||
FAKE_RESPONSE_HEADERS = {'x-openstack-request-id': FAKE_REQUEST_ID}
|
||||
|
||||
FAKE_SERVICE_UUID_1 = '75e9eabc-ed3b-4f11-8bba-add1e7e7e2de'
|
||||
FAKE_SERVICE_UUID_2 = '1f140183-c914-4ddf-8757-6df73028aa86'
|
||||
|
||||
|
||||
class FakeClient(fakes.FakeClient, client.Client):
|
||||
|
||||
@ -1582,6 +1585,12 @@ class FakeSessionClient(base_client.SessionClient):
|
||||
def get_os_services(self, **kw):
|
||||
host = kw.get('host', 'host1')
|
||||
binary = kw.get('binary', 'nova-compute')
|
||||
if self.api_version >= api_versions.APIVersion('2.53'):
|
||||
service_id_1 = FAKE_SERVICE_UUID_1
|
||||
service_id_2 = FAKE_SERVICE_UUID_2
|
||||
else:
|
||||
service_id_1 = 1
|
||||
service_id_2 = 2
|
||||
return (200, FAKE_RESPONSE_HEADERS,
|
||||
{'services': [{'binary': binary,
|
||||
'host': host,
|
||||
@ -1589,14 +1598,16 @@ class FakeSessionClient(base_client.SessionClient):
|
||||
'status': 'enabled',
|
||||
'state': 'up',
|
||||
'updated_at': datetime.datetime(
|
||||
2012, 10, 29, 13, 42, 2)},
|
||||
2012, 10, 29, 13, 42, 2),
|
||||
'id': service_id_1},
|
||||
{'binary': binary,
|
||||
'host': host,
|
||||
'zone': 'nova',
|
||||
'status': 'disabled',
|
||||
'state': 'down',
|
||||
'updated_at': datetime.datetime(
|
||||
2012, 9, 18, 8, 3, 38)},
|
||||
2012, 9, 18, 8, 3, 38),
|
||||
'id': service_id_2},
|
||||
]})
|
||||
|
||||
def put_os_services_enable(self, body, **kw):
|
||||
@ -1618,9 +1629,22 @@ class FakeSessionClient(base_client.SessionClient):
|
||||
'status': 'disabled',
|
||||
'disabled_reason': body['disabled_reason']}})
|
||||
|
||||
def put_os_services_75e9eabc_ed3b_4f11_8bba_add1e7e7e2de(
|
||||
self, body, **kw):
|
||||
"""This should only be called with microversion >= 2.53."""
|
||||
return (200, FAKE_RESPONSE_HEADERS, {'service': {
|
||||
'host': 'host1',
|
||||
'binary': 'nova-compute',
|
||||
'status': body.get('status', 'enabled'),
|
||||
'disabled_reason': body.get('disabled_reason'),
|
||||
'forced_down': body.get('forced_down', False)}})
|
||||
|
||||
def delete_os_services_1(self, **kw):
|
||||
return (204, FAKE_RESPONSE_HEADERS, None)
|
||||
|
||||
def delete_os_services_75e9eabc_ed3b_4f11_8bba_add1e7e7e2de(self, **kwarg):
|
||||
return (204, FAKE_RESPONSE_HEADERS, None)
|
||||
|
||||
def put_os_services_force_down(self, body, **kw):
|
||||
return (200, FAKE_RESPONSE_HEADERS, {'service': {
|
||||
'host': body['host'],
|
||||
|
@ -31,8 +31,10 @@ class HypervisorsTest(utils.FixturedTestCase):
|
||||
|
||||
def test_hypervisor_index(self):
|
||||
expected = [
|
||||
dict(id=1234, hypervisor_hostname='hyper1'),
|
||||
dict(id=5678, hypervisor_hostname='hyper2')]
|
||||
dict(id=self.data_fixture.hyper_id_1,
|
||||
hypervisor_hostname='hyper1'),
|
||||
dict(id=self.data_fixture.hyper_id_2,
|
||||
hypervisor_hostname='hyper2')]
|
||||
|
||||
result = self.cs.hypervisors.list(False)
|
||||
self.assert_request_id(result, fakes.FAKE_REQUEST_ID_LIST)
|
||||
@ -43,8 +45,9 @@ class HypervisorsTest(utils.FixturedTestCase):
|
||||
|
||||
def test_hypervisor_detail(self):
|
||||
expected = [
|
||||
dict(id=1234,
|
||||
service=dict(id=1, host='compute1'),
|
||||
dict(id=self.data_fixture.hyper_id_1,
|
||||
service=dict(id=self.data_fixture.service_id_1,
|
||||
host='compute1'),
|
||||
vcpus=4,
|
||||
memory_mb=10 * 1024,
|
||||
local_gb=250,
|
||||
@ -60,8 +63,9 @@ class HypervisorsTest(utils.FixturedTestCase):
|
||||
running_vms=2,
|
||||
cpu_info='cpu_info',
|
||||
disk_available_least=100),
|
||||
dict(id=2,
|
||||
service=dict(id=2, host="compute2"),
|
||||
dict(id=self.data_fixture.hyper_id_2,
|
||||
service=dict(id=self.data_fixture.service_id_2,
|
||||
host="compute2"),
|
||||
vcpus=4,
|
||||
memory_mb=10 * 1024,
|
||||
local_gb=250,
|
||||
@ -87,24 +91,30 @@ class HypervisorsTest(utils.FixturedTestCase):
|
||||
|
||||
def test_hypervisor_search(self):
|
||||
expected = [
|
||||
dict(id=1234, hypervisor_hostname='hyper1'),
|
||||
dict(id=5678, hypervisor_hostname='hyper2')]
|
||||
dict(id=self.data_fixture.hyper_id_1,
|
||||
hypervisor_hostname='hyper1'),
|
||||
dict(id=self.data_fixture.hyper_id_2,
|
||||
hypervisor_hostname='hyper2')]
|
||||
|
||||
result = self.cs.hypervisors.search('hyper')
|
||||
self.assert_request_id(result, fakes.FAKE_REQUEST_ID_LIST)
|
||||
self.assert_called('GET', '/os-hypervisors/hyper/search')
|
||||
if self.cs.api_version >= api_versions.APIVersion('2.53'):
|
||||
self.assert_called(
|
||||
'GET', '/os-hypervisors?hypervisor_hostname_pattern=hyper')
|
||||
else:
|
||||
self.assert_called('GET', '/os-hypervisors/hyper/search')
|
||||
|
||||
for idx, hyper in enumerate(result):
|
||||
self.compare_to_expected(expected[idx], hyper)
|
||||
|
||||
def test_hypervisor_servers(self):
|
||||
expected = [
|
||||
dict(id=1234,
|
||||
dict(id=self.data_fixture.hyper_id_1,
|
||||
hypervisor_hostname='hyper1',
|
||||
servers=[
|
||||
dict(name='inst1', uuid='uuid1'),
|
||||
dict(name='inst2', uuid='uuid2')]),
|
||||
dict(id=5678,
|
||||
dict(id=self.data_fixture.hyper_id_2,
|
||||
hypervisor_hostname='hyper2',
|
||||
servers=[
|
||||
dict(name='inst3', uuid='uuid3'),
|
||||
@ -113,15 +123,20 @@ class HypervisorsTest(utils.FixturedTestCase):
|
||||
|
||||
result = self.cs.hypervisors.search('hyper', True)
|
||||
self.assert_request_id(result, fakes.FAKE_REQUEST_ID_LIST)
|
||||
self.assert_called('GET', '/os-hypervisors/hyper/servers')
|
||||
if self.cs.api_version >= api_versions.APIVersion('2.53'):
|
||||
self.assert_called(
|
||||
'GET', '/os-hypervisors?hypervisor_hostname_pattern=hyper&'
|
||||
'with_servers=True')
|
||||
else:
|
||||
self.assert_called('GET', '/os-hypervisors/hyper/servers')
|
||||
|
||||
for idx, hyper in enumerate(result):
|
||||
self.compare_to_expected(expected[idx], hyper)
|
||||
|
||||
def test_hypervisor_get(self):
|
||||
expected = dict(
|
||||
id=1234,
|
||||
service=dict(id=1, host='compute1'),
|
||||
id=self.data_fixture.hyper_id_1,
|
||||
service=dict(id=self.data_fixture.service_id_1, host='compute1'),
|
||||
vcpus=4,
|
||||
memory_mb=10 * 1024,
|
||||
local_gb=250,
|
||||
@ -138,21 +153,23 @@ class HypervisorsTest(utils.FixturedTestCase):
|
||||
cpu_info='cpu_info',
|
||||
disk_available_least=100)
|
||||
|
||||
result = self.cs.hypervisors.get(1234)
|
||||
result = self.cs.hypervisors.get(self.data_fixture.hyper_id_1)
|
||||
self.assert_request_id(result, fakes.FAKE_REQUEST_ID_LIST)
|
||||
self.assert_called('GET', '/os-hypervisors/1234')
|
||||
self.assert_called(
|
||||
'GET', '/os-hypervisors/%s' % self.data_fixture.hyper_id_1)
|
||||
|
||||
self.compare_to_expected(expected, result)
|
||||
|
||||
def test_hypervisor_uptime(self):
|
||||
expected = dict(
|
||||
id=1234,
|
||||
id=self.data_fixture.hyper_id_1,
|
||||
hypervisor_hostname="hyper1",
|
||||
uptime="fake uptime")
|
||||
|
||||
result = self.cs.hypervisors.uptime(1234)
|
||||
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/1234/uptime')
|
||||
self.assert_called(
|
||||
'GET', '/os-hypervisors/%s/uptime' % self.data_fixture.hyper_id_1)
|
||||
|
||||
self.compare_to_expected(expected, result)
|
||||
|
||||
@ -198,3 +215,12 @@ class HypervisorsV233Test(HypervisorsTest):
|
||||
self.cs.hypervisors.list(**params)
|
||||
for k, v in params.items():
|
||||
self.assertEqual([v], self.requests_mock.last_request.qs[k])
|
||||
|
||||
|
||||
class HypervisorsV2_53Test(HypervisorsV233Test):
|
||||
"""Tests the os-hypervisors 2.53 API bindings."""
|
||||
data_fixture_class = data.V2_53
|
||||
|
||||
def setUp(self):
|
||||
super(HypervisorsV2_53Test, self).setUp()
|
||||
self.cs.api_version = api_versions.APIVersion("2.53")
|
||||
|
@ -34,11 +34,18 @@ class ServicesTest(utils.TestCase):
|
||||
svs = self.cs.services.list()
|
||||
self.assert_request_id(svs, fakes.FAKE_REQUEST_ID_LIST)
|
||||
self.cs.assert_called('GET', '/os-services')
|
||||
expect_uuid_id = (
|
||||
api_versions.APIVersion(self.api_version) >=
|
||||
api_versions.APIVersion('2.53'))
|
||||
for s in svs:
|
||||
self.assertIsInstance(s, self._get_service_type())
|
||||
self.assertEqual('nova-compute', s.binary)
|
||||
self.assertEqual('host1', s.host)
|
||||
self.assertTrue(str(s).startswith('<Service: '))
|
||||
if expect_uuid_id:
|
||||
stringified = '<Service: %s>' % s.id
|
||||
else:
|
||||
stringified = '<Service: %s>' % s.binary
|
||||
self.assertEqual(stringified, str(s))
|
||||
|
||||
def test_list_services_with_hostname(self):
|
||||
svs = self.cs.services.list(host='host2')
|
||||
@ -129,3 +136,63 @@ class ServicesV211TestCase(ServicesTest):
|
||||
self.cs.assert_called('PUT', '/os-services/force-down', values)
|
||||
self.assertIsInstance(service, self._get_service_type())
|
||||
self.assertFalse(service.forced_down)
|
||||
|
||||
|
||||
class ServicesV2_53TestCase(ServicesV211TestCase):
|
||||
api_version = "2.53"
|
||||
|
||||
def _update_body(self, status=None, disabled_reason=None, force_down=None):
|
||||
body = {}
|
||||
if status is not None:
|
||||
body['status'] = status
|
||||
if disabled_reason is not None:
|
||||
body['disabled_reason'] = disabled_reason
|
||||
if force_down is not None:
|
||||
body['forced_down'] = force_down
|
||||
return body
|
||||
|
||||
def test_services_enable(self):
|
||||
service = self.cs.services.enable(fakes.FAKE_SERVICE_UUID_1)
|
||||
self.assert_request_id(service, fakes.FAKE_REQUEST_ID_LIST)
|
||||
values = self._update_body(status='enabled')
|
||||
self.cs.assert_called(
|
||||
'PUT', '/os-services/%s' % fakes.FAKE_SERVICE_UUID_1, values)
|
||||
self.assertIsInstance(service, self._get_service_type())
|
||||
self.assertEqual('enabled', service.status)
|
||||
|
||||
def test_services_delete(self):
|
||||
ret = self.cs.services.delete(fakes.FAKE_SERVICE_UUID_1)
|
||||
self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST)
|
||||
self.cs.assert_called('DELETE',
|
||||
'/os-services/%s' % fakes.FAKE_SERVICE_UUID_1)
|
||||
|
||||
def test_services_disable(self):
|
||||
service = self.cs.services.disable(fakes.FAKE_SERVICE_UUID_1)
|
||||
self.assert_request_id(service, fakes.FAKE_REQUEST_ID_LIST)
|
||||
values = self._update_body(status='disabled')
|
||||
self.cs.assert_called(
|
||||
'PUT', '/os-services/%s' % fakes.FAKE_SERVICE_UUID_1, values)
|
||||
self.assertIsInstance(service, self._get_service_type())
|
||||
self.assertEqual('disabled', service.status)
|
||||
|
||||
def test_services_disable_log_reason(self):
|
||||
service = self.cs.services.disable_log_reason(
|
||||
fakes.FAKE_SERVICE_UUID_1, 'disable bad host')
|
||||
self.assert_request_id(service, fakes.FAKE_REQUEST_ID_LIST)
|
||||
values = self._update_body(status='disabled',
|
||||
disabled_reason='disable bad host')
|
||||
self.cs.assert_called(
|
||||
'PUT', '/os-services/%s' % fakes.FAKE_SERVICE_UUID_1, values)
|
||||
self.assertIsInstance(service, self._get_service_type())
|
||||
self.assertEqual('disabled', service.status)
|
||||
self.assertEqual('disable bad host', service.disabled_reason)
|
||||
|
||||
def test_services_force_down(self):
|
||||
service = self.cs.services.force_down(
|
||||
fakes.FAKE_SERVICE_UUID_1, False)
|
||||
self.assert_request_id(service, fakes.FAKE_REQUEST_ID_LIST)
|
||||
values = self._update_body(force_down=False)
|
||||
self.cs.assert_called(
|
||||
'PUT', '/os-services/%s' % fakes.FAKE_SERVICE_UUID_1, values)
|
||||
self.assertIsInstance(service, self._get_service_type())
|
||||
self.assertFalse(service.forced_down)
|
||||
|
@ -2223,6 +2223,11 @@ class ShellTest(utils.TestCase):
|
||||
self.run_command('service-list')
|
||||
self.assert_called('GET', '/os-services')
|
||||
|
||||
def test_services_list_v2_53(self):
|
||||
"""Tests nova service-list at the 2.53 microversion."""
|
||||
self.run_command('service-list', api_version='2.53')
|
||||
self.assert_called('GET', '/os-services')
|
||||
|
||||
def test_services_list_with_host(self):
|
||||
self.run_command('service-list --host host1')
|
||||
self.assert_called('GET', '/os-services?host=host1')
|
||||
@ -2240,6 +2245,14 @@ class ShellTest(utils.TestCase):
|
||||
body = {'host': 'host1', 'binary': 'nova-cert'}
|
||||
self.assert_called('PUT', '/os-services/enable', body)
|
||||
|
||||
def test_services_enable_v2_53(self):
|
||||
"""Tests nova service-enable at the 2.53 microversion."""
|
||||
self.run_command('service-enable %s' % fakes.FAKE_SERVICE_UUID_1,
|
||||
api_version='2.53')
|
||||
body = {'status': 'enabled'}
|
||||
self.assert_called(
|
||||
'PUT', '/os-services/%s' % fakes.FAKE_SERVICE_UUID_1, body)
|
||||
|
||||
def test_services_enable_default_binary(self):
|
||||
"""Tests that the default binary is nova-compute if not specified."""
|
||||
self.run_command('service-enable host1')
|
||||
@ -2251,6 +2264,14 @@ class ShellTest(utils.TestCase):
|
||||
body = {'host': 'host1', 'binary': 'nova-cert'}
|
||||
self.assert_called('PUT', '/os-services/disable', body)
|
||||
|
||||
def test_services_disable_v2_53(self):
|
||||
"""Tests nova service-disable at the 2.53 microversion."""
|
||||
self.run_command('service-disable %s' % fakes.FAKE_SERVICE_UUID_1,
|
||||
api_version='2.53')
|
||||
body = {'status': 'disabled'}
|
||||
self.assert_called(
|
||||
'PUT', '/os-services/%s' % fakes.FAKE_SERVICE_UUID_1, body)
|
||||
|
||||
def test_services_disable_default_binary(self):
|
||||
"""Tests that the default binary is nova-compute if not specified."""
|
||||
self.run_command('service-disable host1')
|
||||
@ -2263,10 +2284,32 @@ class ShellTest(utils.TestCase):
|
||||
'disabled_reason': 'no_reason'}
|
||||
self.assert_called('PUT', '/os-services/disable-log-reason', body)
|
||||
|
||||
def test_services_disable_with_reason_v2_53(self):
|
||||
"""Tests nova service-disable --reason at microversion 2.53."""
|
||||
self.run_command('service-disable %s --reason no_reason' %
|
||||
fakes.FAKE_SERVICE_UUID_1, api_version='2.53')
|
||||
body = {'status': 'disabled', 'disabled_reason': 'no_reason'}
|
||||
self.assert_called(
|
||||
'PUT', '/os-services/%s' % fakes.FAKE_SERVICE_UUID_1, body)
|
||||
|
||||
def test_service_force_down_v2_53(self):
|
||||
"""Tests nova service-force-down at the 2.53 microversion."""
|
||||
self.run_command('service-force-down %s' %
|
||||
fakes.FAKE_SERVICE_UUID_1, api_version='2.53')
|
||||
body = {'forced_down': True}
|
||||
self.assert_called(
|
||||
'PUT', '/os-services/%s' % fakes.FAKE_SERVICE_UUID_1, body)
|
||||
|
||||
def test_services_delete(self):
|
||||
self.run_command('service-delete 1')
|
||||
self.assert_called('DELETE', '/os-services/1')
|
||||
|
||||
def test_services_delete_v2_53(self):
|
||||
"""Tests nova service-delete at the 2.53 microversion."""
|
||||
self.run_command('service-delete %s' % fakes.FAKE_SERVICE_UUID_1)
|
||||
self.assert_called(
|
||||
'DELETE', '/os-services/%s' % fakes.FAKE_SERVICE_UUID_1)
|
||||
|
||||
def test_host_list(self):
|
||||
_, err = self.run_command('host-list')
|
||||
# make sure we said it's deprecated
|
||||
|
@ -51,6 +51,8 @@ class HypervisorManager(base.ManagerWithFind):
|
||||
def list(self, detailed=True):
|
||||
"""
|
||||
Get a list of hypervisors.
|
||||
|
||||
:param detailed: Include a detailed response.
|
||||
"""
|
||||
return self._list_base(detailed=detailed)
|
||||
|
||||
@ -59,10 +61,13 @@ class HypervisorManager(base.ManagerWithFind):
|
||||
"""
|
||||
Get a list of hypervisors.
|
||||
|
||||
:param marker: Begin returning hypervisor that appear later in the
|
||||
keypair list than that represented by this keypair name
|
||||
:param detailed: Include a detailed response.
|
||||
:param marker: Begin returning hypervisors that appear later in the
|
||||
hypervisors list than that represented by this
|
||||
hypervisor ID. Starting with microversion 2.53 the
|
||||
marker must be a UUID hypervisor ID.
|
||||
(optional).
|
||||
:param limit: maximum number of keypairs to return (optional).
|
||||
:param limit: maximum number of hypervisors to return (optional).
|
||||
"""
|
||||
return self._list_base(detailed=detailed, marker=marker, limit=limit)
|
||||
|
||||
@ -70,16 +75,31 @@ class HypervisorManager(base.ManagerWithFind):
|
||||
"""
|
||||
Get a list of matching hypervisors.
|
||||
|
||||
:param hypervisor_match: The hypervisor host name or a portion of it.
|
||||
The hypervisor hosts are selected with the host name matching
|
||||
this pattern.
|
||||
:param servers: If True, server information is also retrieved.
|
||||
"""
|
||||
target = 'servers' if servers else 'search'
|
||||
url = ('/os-hypervisors/%s/%s' %
|
||||
(parse.quote(hypervisor_match, safe=''), target))
|
||||
# Starting with microversion 2.53, the /servers and /search routes are
|
||||
# deprecated and we get the same results using GET /os-hypervisors
|
||||
# using query parameters for the hostname pattern and servers.
|
||||
if self.api_version >= api_versions.APIVersion('2.53'):
|
||||
url = ('/os-hypervisors?hypervisor_hostname_pattern=%s' %
|
||||
parse.quote(hypervisor_match, safe=''))
|
||||
if servers:
|
||||
url += '&with_servers=True'
|
||||
else:
|
||||
target = 'servers' if servers else 'search'
|
||||
url = ('/os-hypervisors/%s/%s' %
|
||||
(parse.quote(hypervisor_match, safe=''), target))
|
||||
return self._list(url, 'hypervisors')
|
||||
|
||||
def get(self, hypervisor):
|
||||
"""
|
||||
Get a specific hypervisor.
|
||||
|
||||
: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" % base.getid(hypervisor),
|
||||
"hypervisor")
|
||||
@ -87,6 +107,9 @@ class HypervisorManager(base.ManagerWithFind):
|
||||
def uptime(self, hypervisor):
|
||||
"""
|
||||
Get the uptime for a specific hypervisor.
|
||||
|
||||
: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")
|
||||
|
@ -20,11 +20,16 @@ from six.moves import urllib
|
||||
|
||||
from novaclient import api_versions
|
||||
from novaclient import base
|
||||
from novaclient import utils
|
||||
|
||||
|
||||
class Service(base.Resource):
|
||||
def __repr__(self):
|
||||
return "<Service: %s>" % self.binary
|
||||
# If the id is int-like, then represent the service using it's binary
|
||||
# name, otherwise use the UUID ID.
|
||||
if utils.is_integer_like(self.id):
|
||||
return "<Service: %s>" % self.binary
|
||||
return "<Service: %s>" % self.id
|
||||
|
||||
def _add_details(self, info):
|
||||
dico = 'resource' in info and info['resource'] or info
|
||||
@ -70,27 +75,80 @@ class ServiceManager(base.ManagerWithFind):
|
||||
body["forced_down"] = force_down
|
||||
return body
|
||||
|
||||
@api_versions.wraps('2.0', '2.52')
|
||||
def enable(self, host, binary):
|
||||
"""Enable the service specified by hostname and binary."""
|
||||
body = self._update_body(host, binary)
|
||||
return self._update("/os-services/enable", body, "service")
|
||||
|
||||
@api_versions.wraps('2.53')
|
||||
def enable(self, service_uuid):
|
||||
"""Enable the service specified by the service UUID ID.
|
||||
|
||||
:param service_uuid: The UUID ID of the service to enable.
|
||||
"""
|
||||
return self._update(
|
||||
"/os-services/%s" % service_uuid, {'status': 'enabled'}, "service")
|
||||
|
||||
@api_versions.wraps('2.0', '2.52')
|
||||
def disable(self, host, binary):
|
||||
"""Disable the service specified by hostname and binary."""
|
||||
body = self._update_body(host, binary)
|
||||
return self._update("/os-services/disable", body, "service")
|
||||
|
||||
@api_versions.wraps('2.53')
|
||||
def disable(self, service_uuid):
|
||||
"""Disable the service specified by the service UUID ID.
|
||||
|
||||
:param service_uuid: The UUID ID of the service to disable.
|
||||
"""
|
||||
return self._update("/os-services/%s" % service_uuid,
|
||||
{'status': 'disabled'}, "service")
|
||||
|
||||
@api_versions.wraps('2.0', '2.52')
|
||||
def disable_log_reason(self, host, binary, reason):
|
||||
"""Disable the service with reason."""
|
||||
body = self._update_body(host, binary, reason)
|
||||
return self._update("/os-services/disable-log-reason", body, "service")
|
||||
|
||||
@api_versions.wraps('2.53')
|
||||
def disable_log_reason(self, service_uuid, reason):
|
||||
"""Disable the service with a reason.
|
||||
|
||||
:param service_uuid: The UUID ID of the service to disable.
|
||||
:param reason: The reason for disabling a service. The minimum length
|
||||
is 1 and the maximum length is 255.
|
||||
"""
|
||||
body = {
|
||||
'status': 'disabled',
|
||||
'disabled_reason': reason
|
||||
}
|
||||
return self._update("/os-services/%s" % service_uuid, body, "service")
|
||||
|
||||
def delete(self, service_id):
|
||||
"""Delete a service."""
|
||||
"""Delete a service.
|
||||
|
||||
:param service_id: Before microversion 2.53, this must be an integer id
|
||||
and may not uniquely the service in a multi-cell deployment.
|
||||
Starting with microversion 2.53 this must be a UUID.
|
||||
"""
|
||||
return self._delete("/os-services/%s" % service_id)
|
||||
|
||||
@api_versions.wraps("2.11")
|
||||
@api_versions.wraps("2.11", "2.52")
|
||||
def force_down(self, host, binary, force_down=None):
|
||||
"""Force service state to down specified by hostname and binary."""
|
||||
body = self._update_body(host, binary, force_down=force_down)
|
||||
return self._update("/os-services/force-down", body, "service")
|
||||
|
||||
@api_versions.wraps("2.53")
|
||||
def force_down(self, service_uuid, force_down):
|
||||
"""Update the service's ``forced_down`` field specified by the
|
||||
service UUID ID.
|
||||
|
||||
:param service_uuid: The UUID ID of the service.
|
||||
:param force_down: Whether or not this service was forced down manually
|
||||
by an administrator. This value is useful to know that some 3rd
|
||||
party has verified the service should be marked down.
|
||||
"""
|
||||
return self._update("/os-services/%s" % service_uuid,
|
||||
{'forced_down': force_down}, "service")
|
||||
|
@ -3474,6 +3474,9 @@ def do_service_list(cs, args):
|
||||
utils.print_list(result, columns)
|
||||
|
||||
|
||||
# Before microversion 2.53, the service was identified using it's host/binary
|
||||
# values.
|
||||
@api_versions.wraps('2.0', '2.52')
|
||||
@utils.arg('host', metavar='<hostname>', help=_('Name of host.'))
|
||||
# TODO(mriedem): Eventually just hard-code the binary to "nova-compute".
|
||||
@utils.arg('binary', metavar='<binary>', help=_('Service binary. The only '
|
||||
@ -3485,6 +3488,18 @@ def do_service_enable(cs, args):
|
||||
utils.print_list([result], ['Host', 'Binary', 'Status'])
|
||||
|
||||
|
||||
# Starting in microversion 2.53, the service is identified by UUID ID.
|
||||
@api_versions.wraps('2.53')
|
||||
@utils.arg('id', metavar='<id>', help=_('ID of the service as a UUID.'))
|
||||
def do_service_enable(cs, args):
|
||||
"""Enable the service."""
|
||||
result = cs.services.enable(args.id)
|
||||
utils.print_list([result], ['ID', 'Host', 'Binary', 'Status'])
|
||||
|
||||
|
||||
# Before microversion 2.53, the service was identified using it's host/binary
|
||||
# values.
|
||||
@api_versions.wraps('2.0', '2.52')
|
||||
@utils.arg('host', metavar='<hostname>', help=_('Name of host.'))
|
||||
# TODO(mriedem): Eventually just hard-code the binary to "nova-compute".
|
||||
@utils.arg('binary', metavar='<binary>', help=_('Service binary. The only '
|
||||
@ -3506,7 +3521,27 @@ def do_service_disable(cs, args):
|
||||
utils.print_list([result], ['Host', 'Binary', 'Status'])
|
||||
|
||||
|
||||
@api_versions.wraps("2.11")
|
||||
# Starting in microversion 2.53, the service is identified by UUID ID.
|
||||
@api_versions.wraps('2.53')
|
||||
@utils.arg('id', metavar='<id>', help=_('ID of the service as a UUID.'))
|
||||
@utils.arg(
|
||||
'--reason',
|
||||
metavar='<reason>',
|
||||
help=_('Reason for disabling the service.'))
|
||||
def do_service_disable(cs, args):
|
||||
"""Disable the service."""
|
||||
if args.reason:
|
||||
result = cs.services.disable_log_reason(args.id, args.reason)
|
||||
utils.print_list(
|
||||
[result], ['ID', 'Host', 'Binary', 'Status', 'Disabled Reason'])
|
||||
else:
|
||||
result = cs.services.disable(args.id)
|
||||
utils.print_list([result], ['ID', 'Host', 'Binary', 'Status'])
|
||||
|
||||
|
||||
# Before microversion 2.53, the service was identified using it's host/binary
|
||||
# values.
|
||||
@api_versions.wraps("2.11", "2.52")
|
||||
@utils.arg('host', metavar='<hostname>', help=_('Name of host.'))
|
||||
# TODO(mriedem): Eventually just hard-code the binary to "nova-compute".
|
||||
@utils.arg('binary', metavar='<binary>', help=_('Service binary. The only '
|
||||
@ -3524,9 +3559,37 @@ def do_service_force_down(cs, args):
|
||||
utils.print_list([result], ['Host', 'Binary', 'Forced down'])
|
||||
|
||||
|
||||
@utils.arg('id', metavar='<id>', help=_('ID of service.'))
|
||||
# Starting in microversion 2.53, the service is identified by UUID ID.
|
||||
@api_versions.wraps('2.53')
|
||||
@utils.arg('id', metavar='<id>', help=_('ID of the service as a UUID.'))
|
||||
@utils.arg(
|
||||
'--unset',
|
||||
dest='force_down',
|
||||
help=_("Unset the forced_down state of the service."),
|
||||
action='store_false',
|
||||
default=True)
|
||||
def do_service_force_down(cs, args):
|
||||
"""Force service to down."""
|
||||
result = cs.services.force_down(args.id, args.force_down)
|
||||
utils.print_list([result], ['ID', 'Host', 'Binary', 'Forced down'])
|
||||
|
||||
|
||||
# Before microversion 2.53, the service was identified using it's host/binary
|
||||
# values.
|
||||
@api_versions.wraps('2.0', '2.52')
|
||||
@utils.arg('id', metavar='<id>',
|
||||
help=_('ID of service as an integer. Note that this may not '
|
||||
'uniquely identify a service in a multi-cell deployment.'))
|
||||
def do_service_delete(cs, args):
|
||||
"""Delete the service."""
|
||||
"""Delete the service by integer ID."""
|
||||
cs.services.delete(args.id)
|
||||
|
||||
|
||||
# Starting in microversion 2.53, the service is identified by UUID ID.
|
||||
@api_versions.wraps('2.53')
|
||||
@utils.arg('id', metavar='<id>', help=_('ID of service as a UUID.'))
|
||||
def do_service_delete(cs, args):
|
||||
"""Delete the service by UUID ID."""
|
||||
cs.services.delete(args.id)
|
||||
|
||||
|
||||
@ -3691,7 +3754,8 @@ def do_hypervisor_servers(cs, args):
|
||||
@utils.arg(
|
||||
'hypervisor',
|
||||
metavar='<hypervisor>',
|
||||
help=_('Name or ID of the hypervisor to show the details of.'))
|
||||
help=_('Name or ID of the hypervisor. Starting with microversion 2.53 '
|
||||
'the ID must be a UUID.'))
|
||||
@utils.arg(
|
||||
'--wrap', dest='wrap', metavar='<integer>', default=40,
|
||||
help=_('Wrap the output to a specified length. '
|
||||
@ -3705,7 +3769,8 @@ def do_hypervisor_show(cs, args):
|
||||
@utils.arg(
|
||||
'hypervisor',
|
||||
metavar='<hypervisor>',
|
||||
help=_('Name or ID of the hypervisor to show the uptime of.'))
|
||||
help=_('Name or ID of the hypervisor. Starting with microversion 2.53 '
|
||||
'the ID must be a UUID.'))
|
||||
def do_hypervisor_uptime(cs, args):
|
||||
"""Display the uptime of the specified hypervisor."""
|
||||
hyper = _find_hypervisor(cs, args.hypervisor)
|
||||
|
35
releasenotes/notes/microversion-v2_53-3463b546a38c5f84.yaml
Normal file
35
releasenotes/notes/microversion-v2_53-3463b546a38c5f84.yaml
Normal file
@ -0,0 +1,35 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Added support for `microversion 2.53`_. The following changes were made
|
||||
for the ``services`` commands and python API bindings:
|
||||
|
||||
- The ``nova service-list`` command and API will have a UUID value for the
|
||||
``id`` field in the output and response, respectively.
|
||||
- The ``nova service-enable`` command and API will require a UUID service
|
||||
id value to uniquely identify the service rather than a ``host`` and
|
||||
``binary`` value. The UUID ``id`` field will also be in the command
|
||||
output.
|
||||
- The ``nova service-disable`` command and API will require a UUID service
|
||||
id value to uniquely identify the service rather than a ``host`` and
|
||||
``binary`` value. The UUID ``id`` field will also be in the command
|
||||
output.
|
||||
- The ``nova service-force-down`` command and API will require a UUID
|
||||
service id value to uniquely identify the service rather than a ``host``
|
||||
and ``binary`` value. The UUID ``id`` field will also be in the command
|
||||
output.
|
||||
- The ``nova service-delete`` command and API will require a UUID
|
||||
service id value to uniquely identify the service rather than an integer
|
||||
service id value.
|
||||
|
||||
The following changes were made for the ``hypervisors`` commands and python
|
||||
API bindings:
|
||||
|
||||
- The ID field in the various ``nova hypervisor-*`` commands and
|
||||
``Hypervisor.id`` attribute in the API binding will now be a UUID value.
|
||||
- If paging over hypervisors using ``nova hypervisor-list``, the
|
||||
``--marker`` must be a UUID value.
|
||||
- The ``nova hypervisor-show`` and ``nova hypervisor-uptime`` commands and
|
||||
APIs now take a UUID value for the hypervisor ID.
|
||||
|
||||
.. _microversion 2.53: https://docs.openstack.org/nova/latest/api_microversion_history.html#id48
|
Loading…
Reference in New Issue
Block a user