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
|
# when client supported the max version, and bumped sequentially, otherwise
|
||||||
# the client may break due to server side new version may include some
|
# the client may break due to server side new version may include some
|
||||||
# backward incompatible change.
|
# 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
|
import six
|
||||||
|
|
||||||
from novaclient.tests.functional import base
|
from novaclient.tests.functional import base
|
||||||
|
from novaclient import utils
|
||||||
|
|
||||||
|
|
||||||
class TestHypervisors(base.ClientTestBase):
|
class TestHypervisors(base.ClientTestBase):
|
||||||
|
|
||||||
COMPUTE_API_VERSION = "2.1"
|
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()
|
hypervisors = self.client.hypervisors.list()
|
||||||
if not len(hypervisors):
|
if not len(hypervisors):
|
||||||
self.fail("No hypervisors detected.")
|
self.fail("No hypervisors detected.")
|
||||||
for hypervisor in hypervisors:
|
for hypervisor in hypervisors:
|
||||||
self.assertIsInstance(hypervisor.cpu_info, cpu_info_type)
|
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):
|
def test_list(self):
|
||||||
self._test_list(six.text_type)
|
self._test_list(six.text_type)
|
||||||
|
@ -19,3 +19,10 @@ class TestHypervisorsV28(test_hypervisors.TestHypervisors):
|
|||||||
|
|
||||||
def test_list(self):
|
def test_list(self):
|
||||||
self._test_list(dict)
|
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
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from novaclient.tests.functional import base
|
||||||
from novaclient.tests.functional.v2.legacy import test_os_services
|
from novaclient.tests.functional.v2.legacy import test_os_services
|
||||||
|
from novaclient import utils
|
||||||
|
|
||||||
|
|
||||||
class TestOsServicesNovaClientV211(test_os_services.TestOsServicesNovaClient):
|
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(
|
status = self._get_column_value_from_single_row_table(
|
||||||
service, 'Forced down')
|
service, 'Forced down')
|
||||||
self.assertEqual('False', status)
|
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
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from novaclient import api_versions
|
||||||
from novaclient.tests.unit.fixture_data import base
|
from novaclient.tests.unit.fixture_data import base
|
||||||
|
|
||||||
|
|
||||||
class V1(base.Fixture):
|
class V1(base.Fixture):
|
||||||
|
|
||||||
base_url = 'os-hypervisors'
|
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):
|
def setUp(self):
|
||||||
super(V1, self).setUp()
|
super(V1, self).setUp()
|
||||||
|
uuid_as_id = (api_versions.APIVersion(self.api_version) >=
|
||||||
|
api_versions.APIVersion('2.53'))
|
||||||
|
|
||||||
get_os_hypervisors = {
|
get_os_hypervisors = {
|
||||||
'hypervisors': [
|
'hypervisors': [
|
||||||
{'id': 1234, 'hypervisor_hostname': 'hyper1'},
|
{'id': self.hyper_id_1, 'hypervisor_hostname': 'hyper1'},
|
||||||
{'id': 5678, 'hypervisor_hostname': 'hyper2'},
|
{'id': self.hyper_id_2, 'hypervisor_hostname': 'hyper2'},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,9 +44,9 @@ class V1(base.Fixture):
|
|||||||
get_os_hypervisors_detail = {
|
get_os_hypervisors_detail = {
|
||||||
'hypervisors': [
|
'hypervisors': [
|
||||||
{
|
{
|
||||||
'id': 1234,
|
'id': self.hyper_id_1,
|
||||||
'service': {
|
'service': {
|
||||||
'id': 1,
|
'id': self.service_id_1,
|
||||||
'host': 'compute1',
|
'host': 'compute1',
|
||||||
},
|
},
|
||||||
'vcpus': 4,
|
'vcpus': 4,
|
||||||
@ -58,9 +66,9 @@ class V1(base.Fixture):
|
|||||||
'disk_available_least': 100
|
'disk_available_least': 100
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'id': 2,
|
'id': self.hyper_id_2,
|
||||||
'service': {
|
'service': {
|
||||||
'id': 2,
|
'id': self.service_id_2,
|
||||||
'host': 'compute2',
|
'host': 'compute2',
|
||||||
},
|
},
|
||||||
'vcpus': 4,
|
'vcpus': 4,
|
||||||
@ -109,19 +117,23 @@ class V1(base.Fixture):
|
|||||||
|
|
||||||
get_os_hypervisors_search = {
|
get_os_hypervisors_search = {
|
||||||
'hypervisors': [
|
'hypervisors': [
|
||||||
{'id': 1234, 'hypervisor_hostname': 'hyper1'},
|
{'id': self.hyper_id_1, 'hypervisor_hostname': 'hyper1'},
|
||||||
{'id': 5678, 'hypervisor_hostname': 'hyper2'}
|
{'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,
|
json=get_os_hypervisors_search,
|
||||||
headers=self.headers)
|
headers=self.headers)
|
||||||
|
|
||||||
get_hyper_server = {
|
get_hyper_server = {
|
||||||
'hypervisors': [
|
'hypervisors': [
|
||||||
{
|
{
|
||||||
'id': 1234,
|
'id': self.hyper_id_1,
|
||||||
'hypervisor_hostname': 'hyper1',
|
'hypervisor_hostname': 'hyper1',
|
||||||
'servers': [
|
'servers': [
|
||||||
{'name': 'inst1', 'uuid': 'uuid1'},
|
{'name': 'inst1', 'uuid': 'uuid1'},
|
||||||
@ -129,7 +141,7 @@ class V1(base.Fixture):
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'id': 5678,
|
'id': self.hyper_id_2,
|
||||||
'hypervisor_hostname': 'hyper2',
|
'hypervisor_hostname': 'hyper2',
|
||||||
'servers': [
|
'servers': [
|
||||||
{'name': 'inst3', 'uuid': 'uuid3'},
|
{'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,
|
json=get_hyper_server,
|
||||||
headers=self.headers)
|
headers=self.headers)
|
||||||
|
|
||||||
get_os_hypervisors_1234 = {
|
get_os_hypervisors_hyper1 = {
|
||||||
'hypervisor': {
|
'hypervisor': {
|
||||||
'id': 1234,
|
'id': self.hyper_id_1,
|
||||||
'service': {'id': 1, 'host': 'compute1'},
|
'service': {'id': self.service_id_1, 'host': 'compute1'},
|
||||||
'vcpus': 4,
|
'vcpus': 4,
|
||||||
'memory_mb': 10 * 1024,
|
'memory_mb': 10 * 1024,
|
||||||
'local_gb': 250,
|
'local_gb': 250,
|
||||||
@ -165,18 +182,27 @@ class V1(base.Fixture):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.requests_mock.get(self.url(1234),
|
self.requests_mock.get(self.url(self.hyper_id_1),
|
||||||
json=get_os_hypervisors_1234,
|
json=get_os_hypervisors_hyper1,
|
||||||
headers=self.headers)
|
headers=self.headers)
|
||||||
|
|
||||||
get_os_hypervisors_uptime = {
|
get_os_hypervisors_uptime = {
|
||||||
'hypervisor': {
|
'hypervisor': {
|
||||||
'id': 1234,
|
'id': self.hyper_id_1,
|
||||||
'hypervisor_hostname': 'hyper1',
|
'hypervisor_hostname': 'hyper1',
|
||||||
'uptime': 'fake uptime'
|
'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,
|
json=get_os_hypervisors_uptime,
|
||||||
headers=self.headers)
|
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_REQUEST_ID_LIST = fakes.FAKE_REQUEST_ID_LIST
|
||||||
FAKE_RESPONSE_HEADERS = {'x-openstack-request-id': FAKE_REQUEST_ID}
|
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):
|
class FakeClient(fakes.FakeClient, client.Client):
|
||||||
|
|
||||||
@ -1582,6 +1585,12 @@ class FakeSessionClient(base_client.SessionClient):
|
|||||||
def get_os_services(self, **kw):
|
def get_os_services(self, **kw):
|
||||||
host = kw.get('host', 'host1')
|
host = kw.get('host', 'host1')
|
||||||
binary = kw.get('binary', 'nova-compute')
|
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,
|
return (200, FAKE_RESPONSE_HEADERS,
|
||||||
{'services': [{'binary': binary,
|
{'services': [{'binary': binary,
|
||||||
'host': host,
|
'host': host,
|
||||||
@ -1589,14 +1598,16 @@ class FakeSessionClient(base_client.SessionClient):
|
|||||||
'status': 'enabled',
|
'status': 'enabled',
|
||||||
'state': 'up',
|
'state': 'up',
|
||||||
'updated_at': datetime.datetime(
|
'updated_at': datetime.datetime(
|
||||||
2012, 10, 29, 13, 42, 2)},
|
2012, 10, 29, 13, 42, 2),
|
||||||
|
'id': service_id_1},
|
||||||
{'binary': binary,
|
{'binary': binary,
|
||||||
'host': host,
|
'host': host,
|
||||||
'zone': 'nova',
|
'zone': 'nova',
|
||||||
'status': 'disabled',
|
'status': 'disabled',
|
||||||
'state': 'down',
|
'state': 'down',
|
||||||
'updated_at': datetime.datetime(
|
'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):
|
def put_os_services_enable(self, body, **kw):
|
||||||
@ -1618,9 +1629,22 @@ class FakeSessionClient(base_client.SessionClient):
|
|||||||
'status': 'disabled',
|
'status': 'disabled',
|
||||||
'disabled_reason': body['disabled_reason']}})
|
'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):
|
def delete_os_services_1(self, **kw):
|
||||||
return (204, FAKE_RESPONSE_HEADERS, None)
|
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):
|
def put_os_services_force_down(self, body, **kw):
|
||||||
return (200, FAKE_RESPONSE_HEADERS, {'service': {
|
return (200, FAKE_RESPONSE_HEADERS, {'service': {
|
||||||
'host': body['host'],
|
'host': body['host'],
|
||||||
|
@ -31,8 +31,10 @@ class HypervisorsTest(utils.FixturedTestCase):
|
|||||||
|
|
||||||
def test_hypervisor_index(self):
|
def test_hypervisor_index(self):
|
||||||
expected = [
|
expected = [
|
||||||
dict(id=1234, hypervisor_hostname='hyper1'),
|
dict(id=self.data_fixture.hyper_id_1,
|
||||||
dict(id=5678, hypervisor_hostname='hyper2')]
|
hypervisor_hostname='hyper1'),
|
||||||
|
dict(id=self.data_fixture.hyper_id_2,
|
||||||
|
hypervisor_hostname='hyper2')]
|
||||||
|
|
||||||
result = self.cs.hypervisors.list(False)
|
result = self.cs.hypervisors.list(False)
|
||||||
self.assert_request_id(result, fakes.FAKE_REQUEST_ID_LIST)
|
self.assert_request_id(result, fakes.FAKE_REQUEST_ID_LIST)
|
||||||
@ -43,8 +45,9 @@ class HypervisorsTest(utils.FixturedTestCase):
|
|||||||
|
|
||||||
def test_hypervisor_detail(self):
|
def test_hypervisor_detail(self):
|
||||||
expected = [
|
expected = [
|
||||||
dict(id=1234,
|
dict(id=self.data_fixture.hyper_id_1,
|
||||||
service=dict(id=1, host='compute1'),
|
service=dict(id=self.data_fixture.service_id_1,
|
||||||
|
host='compute1'),
|
||||||
vcpus=4,
|
vcpus=4,
|
||||||
memory_mb=10 * 1024,
|
memory_mb=10 * 1024,
|
||||||
local_gb=250,
|
local_gb=250,
|
||||||
@ -60,8 +63,9 @@ class HypervisorsTest(utils.FixturedTestCase):
|
|||||||
running_vms=2,
|
running_vms=2,
|
||||||
cpu_info='cpu_info',
|
cpu_info='cpu_info',
|
||||||
disk_available_least=100),
|
disk_available_least=100),
|
||||||
dict(id=2,
|
dict(id=self.data_fixture.hyper_id_2,
|
||||||
service=dict(id=2, host="compute2"),
|
service=dict(id=self.data_fixture.service_id_2,
|
||||||
|
host="compute2"),
|
||||||
vcpus=4,
|
vcpus=4,
|
||||||
memory_mb=10 * 1024,
|
memory_mb=10 * 1024,
|
||||||
local_gb=250,
|
local_gb=250,
|
||||||
@ -87,11 +91,17 @@ class HypervisorsTest(utils.FixturedTestCase):
|
|||||||
|
|
||||||
def test_hypervisor_search(self):
|
def test_hypervisor_search(self):
|
||||||
expected = [
|
expected = [
|
||||||
dict(id=1234, hypervisor_hostname='hyper1'),
|
dict(id=self.data_fixture.hyper_id_1,
|
||||||
dict(id=5678, hypervisor_hostname='hyper2')]
|
hypervisor_hostname='hyper1'),
|
||||||
|
dict(id=self.data_fixture.hyper_id_2,
|
||||||
|
hypervisor_hostname='hyper2')]
|
||||||
|
|
||||||
result = self.cs.hypervisors.search('hyper')
|
result = self.cs.hypervisors.search('hyper')
|
||||||
self.assert_request_id(result, fakes.FAKE_REQUEST_ID_LIST)
|
self.assert_request_id(result, fakes.FAKE_REQUEST_ID_LIST)
|
||||||
|
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')
|
self.assert_called('GET', '/os-hypervisors/hyper/search')
|
||||||
|
|
||||||
for idx, hyper in enumerate(result):
|
for idx, hyper in enumerate(result):
|
||||||
@ -99,12 +109,12 @@ class HypervisorsTest(utils.FixturedTestCase):
|
|||||||
|
|
||||||
def test_hypervisor_servers(self):
|
def test_hypervisor_servers(self):
|
||||||
expected = [
|
expected = [
|
||||||
dict(id=1234,
|
dict(id=self.data_fixture.hyper_id_1,
|
||||||
hypervisor_hostname='hyper1',
|
hypervisor_hostname='hyper1',
|
||||||
servers=[
|
servers=[
|
||||||
dict(name='inst1', uuid='uuid1'),
|
dict(name='inst1', uuid='uuid1'),
|
||||||
dict(name='inst2', uuid='uuid2')]),
|
dict(name='inst2', uuid='uuid2')]),
|
||||||
dict(id=5678,
|
dict(id=self.data_fixture.hyper_id_2,
|
||||||
hypervisor_hostname='hyper2',
|
hypervisor_hostname='hyper2',
|
||||||
servers=[
|
servers=[
|
||||||
dict(name='inst3', uuid='uuid3'),
|
dict(name='inst3', uuid='uuid3'),
|
||||||
@ -113,6 +123,11 @@ class HypervisorsTest(utils.FixturedTestCase):
|
|||||||
|
|
||||||
result = self.cs.hypervisors.search('hyper', True)
|
result = self.cs.hypervisors.search('hyper', True)
|
||||||
self.assert_request_id(result, fakes.FAKE_REQUEST_ID_LIST)
|
self.assert_request_id(result, fakes.FAKE_REQUEST_ID_LIST)
|
||||||
|
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')
|
self.assert_called('GET', '/os-hypervisors/hyper/servers')
|
||||||
|
|
||||||
for idx, hyper in enumerate(result):
|
for idx, hyper in enumerate(result):
|
||||||
@ -120,8 +135,8 @@ class HypervisorsTest(utils.FixturedTestCase):
|
|||||||
|
|
||||||
def test_hypervisor_get(self):
|
def test_hypervisor_get(self):
|
||||||
expected = dict(
|
expected = dict(
|
||||||
id=1234,
|
id=self.data_fixture.hyper_id_1,
|
||||||
service=dict(id=1, host='compute1'),
|
service=dict(id=self.data_fixture.service_id_1, host='compute1'),
|
||||||
vcpus=4,
|
vcpus=4,
|
||||||
memory_mb=10 * 1024,
|
memory_mb=10 * 1024,
|
||||||
local_gb=250,
|
local_gb=250,
|
||||||
@ -138,21 +153,23 @@ class HypervisorsTest(utils.FixturedTestCase):
|
|||||||
cpu_info='cpu_info',
|
cpu_info='cpu_info',
|
||||||
disk_available_least=100)
|
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_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)
|
self.compare_to_expected(expected, result)
|
||||||
|
|
||||||
def test_hypervisor_uptime(self):
|
def test_hypervisor_uptime(self):
|
||||||
expected = dict(
|
expected = dict(
|
||||||
id=1234,
|
id=self.data_fixture.hyper_id_1,
|
||||||
hypervisor_hostname="hyper1",
|
hypervisor_hostname="hyper1",
|
||||||
uptime="fake uptime")
|
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_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)
|
self.compare_to_expected(expected, result)
|
||||||
|
|
||||||
@ -198,3 +215,12 @@ class HypervisorsV233Test(HypervisorsTest):
|
|||||||
self.cs.hypervisors.list(**params)
|
self.cs.hypervisors.list(**params)
|
||||||
for k, v in params.items():
|
for k, v in params.items():
|
||||||
self.assertEqual([v], self.requests_mock.last_request.qs[k])
|
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()
|
svs = self.cs.services.list()
|
||||||
self.assert_request_id(svs, fakes.FAKE_REQUEST_ID_LIST)
|
self.assert_request_id(svs, fakes.FAKE_REQUEST_ID_LIST)
|
||||||
self.cs.assert_called('GET', '/os-services')
|
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:
|
for s in svs:
|
||||||
self.assertIsInstance(s, self._get_service_type())
|
self.assertIsInstance(s, self._get_service_type())
|
||||||
self.assertEqual('nova-compute', s.binary)
|
self.assertEqual('nova-compute', s.binary)
|
||||||
self.assertEqual('host1', s.host)
|
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):
|
def test_list_services_with_hostname(self):
|
||||||
svs = self.cs.services.list(host='host2')
|
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.cs.assert_called('PUT', '/os-services/force-down', values)
|
||||||
self.assertIsInstance(service, self._get_service_type())
|
self.assertIsInstance(service, self._get_service_type())
|
||||||
self.assertFalse(service.forced_down)
|
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.run_command('service-list')
|
||||||
self.assert_called('GET', '/os-services')
|
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):
|
def test_services_list_with_host(self):
|
||||||
self.run_command('service-list --host host1')
|
self.run_command('service-list --host host1')
|
||||||
self.assert_called('GET', '/os-services?host=host1')
|
self.assert_called('GET', '/os-services?host=host1')
|
||||||
@ -2240,6 +2245,14 @@ class ShellTest(utils.TestCase):
|
|||||||
body = {'host': 'host1', 'binary': 'nova-cert'}
|
body = {'host': 'host1', 'binary': 'nova-cert'}
|
||||||
self.assert_called('PUT', '/os-services/enable', body)
|
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):
|
def test_services_enable_default_binary(self):
|
||||||
"""Tests that the default binary is nova-compute if not specified."""
|
"""Tests that the default binary is nova-compute if not specified."""
|
||||||
self.run_command('service-enable host1')
|
self.run_command('service-enable host1')
|
||||||
@ -2251,6 +2264,14 @@ class ShellTest(utils.TestCase):
|
|||||||
body = {'host': 'host1', 'binary': 'nova-cert'}
|
body = {'host': 'host1', 'binary': 'nova-cert'}
|
||||||
self.assert_called('PUT', '/os-services/disable', body)
|
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):
|
def test_services_disable_default_binary(self):
|
||||||
"""Tests that the default binary is nova-compute if not specified."""
|
"""Tests that the default binary is nova-compute if not specified."""
|
||||||
self.run_command('service-disable host1')
|
self.run_command('service-disable host1')
|
||||||
@ -2263,10 +2284,32 @@ class ShellTest(utils.TestCase):
|
|||||||
'disabled_reason': 'no_reason'}
|
'disabled_reason': 'no_reason'}
|
||||||
self.assert_called('PUT', '/os-services/disable-log-reason', body)
|
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):
|
def test_services_delete(self):
|
||||||
self.run_command('service-delete 1')
|
self.run_command('service-delete 1')
|
||||||
self.assert_called('DELETE', '/os-services/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):
|
def test_host_list(self):
|
||||||
_, err = self.run_command('host-list')
|
_, err = self.run_command('host-list')
|
||||||
# make sure we said it's deprecated
|
# make sure we said it's deprecated
|
||||||
|
@ -51,6 +51,8 @@ class HypervisorManager(base.ManagerWithFind):
|
|||||||
def list(self, detailed=True):
|
def list(self, detailed=True):
|
||||||
"""
|
"""
|
||||||
Get a list of hypervisors.
|
Get a list of hypervisors.
|
||||||
|
|
||||||
|
:param detailed: Include a detailed response.
|
||||||
"""
|
"""
|
||||||
return self._list_base(detailed=detailed)
|
return self._list_base(detailed=detailed)
|
||||||
|
|
||||||
@ -59,10 +61,13 @@ class HypervisorManager(base.ManagerWithFind):
|
|||||||
"""
|
"""
|
||||||
Get a list of hypervisors.
|
Get a list of hypervisors.
|
||||||
|
|
||||||
:param marker: Begin returning hypervisor that appear later in the
|
:param detailed: Include a detailed response.
|
||||||
keypair list than that represented by this keypair name
|
: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).
|
(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)
|
return self._list_base(detailed=detailed, marker=marker, limit=limit)
|
||||||
|
|
||||||
@ -70,8 +75,20 @@ class HypervisorManager(base.ManagerWithFind):
|
|||||||
"""
|
"""
|
||||||
Get a list of matching hypervisors.
|
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.
|
:param servers: If True, server information is also retrieved.
|
||||||
"""
|
"""
|
||||||
|
# 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'
|
target = 'servers' if servers else 'search'
|
||||||
url = ('/os-hypervisors/%s/%s' %
|
url = ('/os-hypervisors/%s/%s' %
|
||||||
(parse.quote(hypervisor_match, safe=''), target))
|
(parse.quote(hypervisor_match, safe=''), target))
|
||||||
@ -80,6 +97,9 @@ class HypervisorManager(base.ManagerWithFind):
|
|||||||
def get(self, hypervisor):
|
def get(self, hypervisor):
|
||||||
"""
|
"""
|
||||||
Get a specific 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),
|
return self._get("/os-hypervisors/%s" % base.getid(hypervisor),
|
||||||
"hypervisor")
|
"hypervisor")
|
||||||
@ -87,6 +107,9 @@ class HypervisorManager(base.ManagerWithFind):
|
|||||||
def uptime(self, hypervisor):
|
def uptime(self, hypervisor):
|
||||||
"""
|
"""
|
||||||
Get the uptime for a specific 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),
|
return self._get("/os-hypervisors/%s/uptime" % base.getid(hypervisor),
|
||||||
"hypervisor")
|
"hypervisor")
|
||||||
|
@ -20,11 +20,16 @@ from six.moves import urllib
|
|||||||
|
|
||||||
from novaclient import api_versions
|
from novaclient import api_versions
|
||||||
from novaclient import base
|
from novaclient import base
|
||||||
|
from novaclient import utils
|
||||||
|
|
||||||
|
|
||||||
class Service(base.Resource):
|
class Service(base.Resource):
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
# 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.binary
|
||||||
|
return "<Service: %s>" % self.id
|
||||||
|
|
||||||
def _add_details(self, info):
|
def _add_details(self, info):
|
||||||
dico = 'resource' in info and info['resource'] or info
|
dico = 'resource' in info and info['resource'] or info
|
||||||
@ -70,27 +75,80 @@ class ServiceManager(base.ManagerWithFind):
|
|||||||
body["forced_down"] = force_down
|
body["forced_down"] = force_down
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
@api_versions.wraps('2.0', '2.52')
|
||||||
def enable(self, host, binary):
|
def enable(self, host, binary):
|
||||||
"""Enable the service specified by hostname and binary."""
|
"""Enable the service specified by hostname and binary."""
|
||||||
body = self._update_body(host, binary)
|
body = self._update_body(host, binary)
|
||||||
return self._update("/os-services/enable", body, "service")
|
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):
|
def disable(self, host, binary):
|
||||||
"""Disable the service specified by hostname and binary."""
|
"""Disable the service specified by hostname and binary."""
|
||||||
body = self._update_body(host, binary)
|
body = self._update_body(host, binary)
|
||||||
return self._update("/os-services/disable", body, "service")
|
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):
|
def disable_log_reason(self, host, binary, reason):
|
||||||
"""Disable the service with reason."""
|
"""Disable the service with reason."""
|
||||||
body = self._update_body(host, binary, reason)
|
body = self._update_body(host, binary, reason)
|
||||||
return self._update("/os-services/disable-log-reason", body, "service")
|
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):
|
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)
|
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):
|
def force_down(self, host, binary, force_down=None):
|
||||||
"""Force service state to down specified by hostname and binary."""
|
"""Force service state to down specified by hostname and binary."""
|
||||||
body = self._update_body(host, binary, force_down=force_down)
|
body = self._update_body(host, binary, force_down=force_down)
|
||||||
return self._update("/os-services/force-down", body, "service")
|
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)
|
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.'))
|
@utils.arg('host', metavar='<hostname>', help=_('Name of host.'))
|
||||||
# TODO(mriedem): Eventually just hard-code the binary to "nova-compute".
|
# TODO(mriedem): Eventually just hard-code the binary to "nova-compute".
|
||||||
@utils.arg('binary', metavar='<binary>', help=_('Service binary. The only '
|
@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'])
|
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.'))
|
@utils.arg('host', metavar='<hostname>', help=_('Name of host.'))
|
||||||
# TODO(mriedem): Eventually just hard-code the binary to "nova-compute".
|
# TODO(mriedem): Eventually just hard-code the binary to "nova-compute".
|
||||||
@utils.arg('binary', metavar='<binary>', help=_('Service binary. The only '
|
@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'])
|
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.'))
|
@utils.arg('host', metavar='<hostname>', help=_('Name of host.'))
|
||||||
# TODO(mriedem): Eventually just hard-code the binary to "nova-compute".
|
# TODO(mriedem): Eventually just hard-code the binary to "nova-compute".
|
||||||
@utils.arg('binary', metavar='<binary>', help=_('Service binary. The only '
|
@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.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):
|
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)
|
cs.services.delete(args.id)
|
||||||
|
|
||||||
|
|
||||||
@ -3691,7 +3754,8 @@ def do_hypervisor_servers(cs, args):
|
|||||||
@utils.arg(
|
@utils.arg(
|
||||||
'hypervisor',
|
'hypervisor',
|
||||||
metavar='<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(
|
@utils.arg(
|
||||||
'--wrap', dest='wrap', metavar='<integer>', default=40,
|
'--wrap', dest='wrap', metavar='<integer>', default=40,
|
||||||
help=_('Wrap the output to a specified length. '
|
help=_('Wrap the output to a specified length. '
|
||||||
@ -3705,7 +3769,8 @@ def do_hypervisor_show(cs, args):
|
|||||||
@utils.arg(
|
@utils.arg(
|
||||||
'hypervisor',
|
'hypervisor',
|
||||||
metavar='<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):
|
def do_hypervisor_uptime(cs, args):
|
||||||
"""Display the uptime of the specified hypervisor."""
|
"""Display the uptime of the specified hypervisor."""
|
||||||
hyper = _find_hypervisor(cs, args.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