diff --git a/releasenotes/notes/extra-compute-services-tests-92b6c0618972e02f.yaml b/releasenotes/notes/extra-compute-services-tests-92b6c0618972e02f.yaml new file mode 100644 index 0000000000..414adf18b0 --- /dev/null +++ b/releasenotes/notes/extra-compute-services-tests-92b6c0618972e02f.yaml @@ -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. diff --git a/tempest/lib/api_schema/response/compute/v2_1/services.py b/tempest/lib/api_schema/response/compute/v2_1/services.py index 6949f86265..3b58ece480 100644 --- a/tempest/lib/api_schema/response/compute/v2_1/services.py +++ b/tempest/lib/api_schema/response/compute/v2_1/services.py @@ -65,3 +65,25 @@ enable_disable_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'] + } +} diff --git a/tempest/lib/api_schema/response/compute/v2_11/__init__.py b/tempest/lib/api_schema/response/compute/v2_11/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tempest/lib/api_schema/response/compute/v2_11/services.py b/tempest/lib/api_schema/response/compute/v2_11/services.py new file mode 100644 index 0000000000..18b833bd27 --- /dev/null +++ b/tempest/lib/api_schema/response/compute/v2_11/services.py @@ -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'] + } +} diff --git a/tempest/lib/services/compute/services_client.py b/tempest/lib/services/compute/services_client.py index 77ac82fee8..857c435a63 100644 --- a/tempest/lib/services/compute/services_client.py +++ b/tempest/lib/services/compute/services_client.py @@ -18,12 +18,18 @@ from oslo_serialization import jsonutils as json 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_11 import services \ + as schemav211 from tempest.lib.common import rest_client from tempest.lib.services.compute import base_compute_client 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): """Lists all running Compute services for a tenant. @@ -37,7 +43,8 @@ class ServicesClient(base_compute_client.BaseComputeClient): resp, body = self.get(url) 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) def enable_service(self, **kwargs): @@ -65,3 +72,31 @@ class ServicesClient(base_compute_client.BaseComputeClient): body = json.loads(body) self.validate_response(schema.enable_disable_service, 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) diff --git a/tempest/tests/lib/services/compute/test_services_client.py b/tempest/tests/lib/services/compute/test_services_client.py index 41da39ce7f..2dd981c007 100644 --- a/tempest/tests/lib/services/compute/test_services_client.py +++ b/tempest/tests/lib/services/compute/test_services_client.py @@ -14,6 +14,9 @@ import copy +import mock + +from tempest.lib.services.compute import base_compute_client from tempest.lib.services.compute import services_client from tempest.tests.lib import fake_auth_provider 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): super(TestServicesClient, self).setUp() fake_auth = fake_auth_provider.FakeAuthProvider() self.client = services_client.ServicesClient( fake_auth, 'compute', 'regionOne') + self.addCleanup(mock.patch.stopall) def test_list_services_with_str_body(self): self.check_service_client_function( @@ -68,7 +81,7 @@ class TestServicesClient(base.BaseServiceTest): 'tempest.lib.common.rest_client.RestClient.put', self.FAKE_SERVICE, bytes_body, - host_name="nova-conductor", binary="controller") + host="nova-conductor", binary="controller") def test_enable_service_with_str_body(self): self._test_enable_service() @@ -85,10 +98,49 @@ class TestServicesClient(base.BaseServiceTest): 'tempest.lib.common.rest_client.RestClient.put', fake_service, bytes_body, - host_name="nova-conductor", binary="controller") + host="nova-conductor", binary="controller") def test_disable_service_with_str_body(self): self._test_disable_service() def test_disable_service_with_bytes_body(self): 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)