Extra compute services_client API endpoints
This patch: - adds ``disable_log_reason`` and ``update_forced_down`` API endpoints to the compute ``services_client`` - adds corresponding unit tests and schemas (for 2.11 microversion) - changes ``host_name`` parameter in the unit tests for ``services_client`` to ``host`` because ``host_name`` is wrong; see [0] for example However, this patch does not add API tests for these endpoints because they result in compute services being forced down or disabled, which are dangerous to test in the gates. Valid use cases for these APIs include: - negative testing - RBAC testing (forcing a BadRequest but ensuring policy enforcement happens beforehand) [0] https://developer.openstack.org/api-ref/compute/#update-forced-down Change-Id: I641218e104bba55e3679a7111e7f3d042ad7665b
This commit is contained in:
parent
123eb2aa55
commit
fe399fdfeb
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Add the ``disable_log_reason`` and the ``update_forced_down`` API endpoints
|
||||||
|
to the compute ``services_client``.
|
||||||
|
Add '2.11' compute validation schema for compute services API.
|
@ -65,3 +65,25 @@ enable_disable_service = {
|
|||||||
'required': ['service']
|
'required': ['service']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
disable_log_reason = {
|
||||||
|
'status_code': [200],
|
||||||
|
'response_body': {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'service': {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'disabled_reason': {'type': 'string'},
|
||||||
|
'binary': {'type': 'string'},
|
||||||
|
'host': {'type': 'string'},
|
||||||
|
'status': {'type': 'string'}
|
||||||
|
},
|
||||||
|
'additionalProperties': False,
|
||||||
|
'required': ['disabled_reason', 'binary', 'host', 'status']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'additionalProperties': False,
|
||||||
|
'required': ['service']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
46
tempest/lib/api_schema/response/compute/v2_11/services.py
Normal file
46
tempest/lib/api_schema/response/compute/v2_11/services.py
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# Copyright 2017 AT&T Corporation.
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
import copy
|
||||||
|
|
||||||
|
from tempest.lib.api_schema.response.compute.v2_1 import services
|
||||||
|
|
||||||
|
|
||||||
|
list_services = copy.deepcopy(services.list_services)
|
||||||
|
list_services['response_body']['properties']['services']['items'][
|
||||||
|
'properties']['forced_down'] = {'type': 'boolean'}
|
||||||
|
list_services['response_body']['properties']['services']['items'][
|
||||||
|
'required'].append('forced_down')
|
||||||
|
|
||||||
|
update_forced_down = {
|
||||||
|
'status_code': [200],
|
||||||
|
'response_body': {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'service': {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'binary': {'type': 'string'},
|
||||||
|
'host': {'type': 'string'},
|
||||||
|
'forced_down': {'type': 'boolean'}
|
||||||
|
},
|
||||||
|
'additionalProperties': False,
|
||||||
|
'required': ['binary', 'host', 'forced_down']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'additionalProperties': False,
|
||||||
|
'required': ['service']
|
||||||
|
}
|
||||||
|
}
|
@ -18,12 +18,18 @@ from oslo_serialization import jsonutils as json
|
|||||||
from six.moves.urllib import parse as urllib
|
from six.moves.urllib import parse as urllib
|
||||||
|
|
||||||
from tempest.lib.api_schema.response.compute.v2_1 import services as schema
|
from tempest.lib.api_schema.response.compute.v2_1 import services as schema
|
||||||
|
from tempest.lib.api_schema.response.compute.v2_11 import services \
|
||||||
|
as schemav211
|
||||||
from tempest.lib.common import rest_client
|
from tempest.lib.common import rest_client
|
||||||
from tempest.lib.services.compute import base_compute_client
|
from tempest.lib.services.compute import base_compute_client
|
||||||
|
|
||||||
|
|
||||||
class ServicesClient(base_compute_client.BaseComputeClient):
|
class ServicesClient(base_compute_client.BaseComputeClient):
|
||||||
|
|
||||||
|
schema_versions_info = [
|
||||||
|
{'min': None, 'max': '2.10', 'schema': schema},
|
||||||
|
{'min': '2.11', 'max': None, 'schema': schemav211}]
|
||||||
|
|
||||||
def list_services(self, **params):
|
def list_services(self, **params):
|
||||||
"""Lists all running Compute services for a tenant.
|
"""Lists all running Compute services for a tenant.
|
||||||
|
|
||||||
@ -37,7 +43,8 @@ class ServicesClient(base_compute_client.BaseComputeClient):
|
|||||||
|
|
||||||
resp, body = self.get(url)
|
resp, body = self.get(url)
|
||||||
body = json.loads(body)
|
body = json.loads(body)
|
||||||
self.validate_response(schema.list_services, resp, body)
|
_schema = self.get_schema(self.schema_versions_info)
|
||||||
|
self.validate_response(_schema.list_services, resp, body)
|
||||||
return rest_client.ResponseBody(resp, body)
|
return rest_client.ResponseBody(resp, body)
|
||||||
|
|
||||||
def enable_service(self, **kwargs):
|
def enable_service(self, **kwargs):
|
||||||
@ -65,3 +72,31 @@ class ServicesClient(base_compute_client.BaseComputeClient):
|
|||||||
body = json.loads(body)
|
body = json.loads(body)
|
||||||
self.validate_response(schema.enable_disable_service, resp, body)
|
self.validate_response(schema.enable_disable_service, resp, body)
|
||||||
return rest_client.ResponseBody(resp, body)
|
return rest_client.ResponseBody(resp, body)
|
||||||
|
|
||||||
|
def disable_log_reason(self, **kwargs):
|
||||||
|
"""Disables scheduling for a Compute service and logs reason.
|
||||||
|
|
||||||
|
For a full list of available parameters, please refer to the official
|
||||||
|
API reference:
|
||||||
|
https://developer.openstack.org/api-ref/compute/#log-disabled-compute-service-information
|
||||||
|
"""
|
||||||
|
post_body = json.dumps(kwargs)
|
||||||
|
resp, body = self.put('os-services/disable-log-reason', post_body)
|
||||||
|
body = json.loads(body)
|
||||||
|
self.validate_response(schema.disable_log_reason, resp, body)
|
||||||
|
return rest_client.ResponseBody(resp, body)
|
||||||
|
|
||||||
|
def update_forced_down(self, **kwargs):
|
||||||
|
"""Set or unset ``forced_down`` flag for the service.
|
||||||
|
|
||||||
|
For a full list of available parameters, please refer to the official
|
||||||
|
API reference:
|
||||||
|
https://developer.openstack.org/api-ref/compute/#update-forced-down
|
||||||
|
"""
|
||||||
|
post_body = json.dumps(kwargs)
|
||||||
|
resp, body = self.put('os-services/force-down', post_body)
|
||||||
|
body = json.loads(body)
|
||||||
|
# NOTE: Use schemav211.update_forced_down directly because there is no
|
||||||
|
# update_forced_down schema for <2.11.
|
||||||
|
self.validate_response(schemav211.update_forced_down, resp, body)
|
||||||
|
return rest_client.ResponseBody(resp, body)
|
||||||
|
@ -14,6 +14,9 @@
|
|||||||
|
|
||||||
import copy
|
import copy
|
||||||
|
|
||||||
|
import mock
|
||||||
|
|
||||||
|
from tempest.lib.services.compute import base_compute_client
|
||||||
from tempest.lib.services.compute import services_client
|
from tempest.lib.services.compute import services_client
|
||||||
from tempest.tests.lib import fake_auth_provider
|
from tempest.tests.lib import fake_auth_provider
|
||||||
from tempest.tests.lib.services import base
|
from tempest.tests.lib.services import base
|
||||||
@ -44,11 +47,21 @@ class TestServicesClient(base.BaseServiceTest):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FAKE_UPDATE_FORCED_DOWN = {
|
||||||
|
"service":
|
||||||
|
{
|
||||||
|
"forced_down": True,
|
||||||
|
"binary": "nova-conductor",
|
||||||
|
"host": "controller"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestServicesClient, self).setUp()
|
super(TestServicesClient, self).setUp()
|
||||||
fake_auth = fake_auth_provider.FakeAuthProvider()
|
fake_auth = fake_auth_provider.FakeAuthProvider()
|
||||||
self.client = services_client.ServicesClient(
|
self.client = services_client.ServicesClient(
|
||||||
fake_auth, 'compute', 'regionOne')
|
fake_auth, 'compute', 'regionOne')
|
||||||
|
self.addCleanup(mock.patch.stopall)
|
||||||
|
|
||||||
def test_list_services_with_str_body(self):
|
def test_list_services_with_str_body(self):
|
||||||
self.check_service_client_function(
|
self.check_service_client_function(
|
||||||
@ -68,7 +81,7 @@ class TestServicesClient(base.BaseServiceTest):
|
|||||||
'tempest.lib.common.rest_client.RestClient.put',
|
'tempest.lib.common.rest_client.RestClient.put',
|
||||||
self.FAKE_SERVICE,
|
self.FAKE_SERVICE,
|
||||||
bytes_body,
|
bytes_body,
|
||||||
host_name="nova-conductor", binary="controller")
|
host="nova-conductor", binary="controller")
|
||||||
|
|
||||||
def test_enable_service_with_str_body(self):
|
def test_enable_service_with_str_body(self):
|
||||||
self._test_enable_service()
|
self._test_enable_service()
|
||||||
@ -85,10 +98,49 @@ class TestServicesClient(base.BaseServiceTest):
|
|||||||
'tempest.lib.common.rest_client.RestClient.put',
|
'tempest.lib.common.rest_client.RestClient.put',
|
||||||
fake_service,
|
fake_service,
|
||||||
bytes_body,
|
bytes_body,
|
||||||
host_name="nova-conductor", binary="controller")
|
host="nova-conductor", binary="controller")
|
||||||
|
|
||||||
def test_disable_service_with_str_body(self):
|
def test_disable_service_with_str_body(self):
|
||||||
self._test_disable_service()
|
self._test_disable_service()
|
||||||
|
|
||||||
def test_disable_service_with_bytes_body(self):
|
def test_disable_service_with_bytes_body(self):
|
||||||
self._test_disable_service(bytes_body=True)
|
self._test_disable_service(bytes_body=True)
|
||||||
|
|
||||||
|
def _test_log_reason_disabled_service(self, bytes_body=False):
|
||||||
|
resp_body = copy.deepcopy(self.FAKE_SERVICE)
|
||||||
|
resp_body['service']['disabled_reason'] = 'test reason'
|
||||||
|
|
||||||
|
self.check_service_client_function(
|
||||||
|
self.client.disable_log_reason,
|
||||||
|
'tempest.lib.common.rest_client.RestClient.put',
|
||||||
|
resp_body,
|
||||||
|
bytes_body,
|
||||||
|
host="nova-conductor",
|
||||||
|
binary="controller",
|
||||||
|
disabled_reason='test reason')
|
||||||
|
|
||||||
|
def test_log_reason_disabled_service_with_str_body(self):
|
||||||
|
self._test_log_reason_disabled_service()
|
||||||
|
|
||||||
|
def test_log_reason_disabled_service_with_bytes_body(self):
|
||||||
|
self._test_log_reason_disabled_service(bytes_body=True)
|
||||||
|
|
||||||
|
def _test_update_forced_down(self, bytes_body=False):
|
||||||
|
self.check_service_client_function(
|
||||||
|
self.client.update_forced_down,
|
||||||
|
'tempest.lib.common.rest_client.RestClient.put',
|
||||||
|
self.FAKE_UPDATE_FORCED_DOWN,
|
||||||
|
bytes_body,
|
||||||
|
host="nova-conductor",
|
||||||
|
binary="controller",
|
||||||
|
forced_down=True)
|
||||||
|
|
||||||
|
@mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
|
||||||
|
new_callable=mock.PropertyMock(return_value='2.11'))
|
||||||
|
def test_update_forced_down_with_str_body(self, _):
|
||||||
|
self._test_update_forced_down()
|
||||||
|
|
||||||
|
@mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
|
||||||
|
new_callable=mock.PropertyMock(return_value='2.11'))
|
||||||
|
def test_update_forced_down_with_bytes_body(self, _):
|
||||||
|
self._test_update_forced_down(bytes_body=True)
|
||||||
|
Loading…
Reference in New Issue
Block a user