diff --git a/tempest_lib/api_schema/response/compute/v2_1/services.py b/tempest_lib/api_schema/response/compute/v2_1/services.py new file mode 100644 index 0000000..ddef7b2 --- /dev/null +++ b/tempest_lib/api_schema/response/compute/v2_1/services.py @@ -0,0 +1,65 @@ +# Copyright 2014 NEC 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. + +list_services = { + 'status_code': [200], + 'response_body': { + 'type': 'object', + 'properties': { + 'services': { + 'type': 'array', + 'items': { + 'type': 'object', + 'properties': { + 'id': {'type': ['integer', 'string'], + 'pattern': '^[a-zA-Z!]*@[0-9]+$'}, + 'zone': {'type': 'string'}, + 'host': {'type': 'string'}, + 'state': {'type': 'string'}, + 'binary': {'type': 'string'}, + 'status': {'type': 'string'}, + 'updated_at': {'type': ['string', 'null']}, + 'disabled_reason': {'type': ['string', 'null']} + }, + 'additionalProperties': False, + 'required': ['id', 'zone', 'host', 'state', 'binary', + 'status', 'updated_at', 'disabled_reason'] + } + } + }, + 'additionalProperties': False, + 'required': ['services'] + } +} + +enable_disable_service = { + 'status_code': [200], + 'response_body': { + 'type': 'object', + 'properties': { + 'service': { + 'type': 'object', + 'properties': { + 'status': {'type': 'string'}, + 'binary': {'type': 'string'}, + 'host': {'type': 'string'} + }, + 'additionalProperties': False, + 'required': ['status', 'binary', 'host'] + } + }, + 'additionalProperties': False, + 'required': ['service'] + } +} diff --git a/tempest_lib/services/compute/services_client.py b/tempest_lib/services/compute/services_client.py new file mode 100644 index 0000000..5cdfea3 --- /dev/null +++ b/tempest_lib/services/compute/services_client.py @@ -0,0 +1,58 @@ +# Copyright 2013 NEC Corporation +# Copyright 2013 IBM Corp. +# 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. + +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.common import rest_client + + +class ServicesClient(rest_client.RestClient): + + def list_services(self, **params): + url = 'os-services' + if params: + url += '?%s' % urllib.urlencode(params) + + resp, body = self.get(url) + body = json.loads(body) + self.validate_response(schema.list_services, resp, body) + return rest_client.ResponseBody(resp, body) + + def enable_service(self, **kwargs): + """Enable service on a host + + host_name: Name of host + binary: Service binary + """ + post_body = json.dumps(kwargs) + resp, body = self.put('os-services/enable', post_body) + body = json.loads(body) + self.validate_response(schema.enable_disable_service, resp, body) + return rest_client.ResponseBody(resp, body) + + def disable_service(self, **kwargs): + """Disable service on a host + + host_name: Name of host + binary: Service binary + """ + post_body = json.dumps(kwargs) + resp, body = self.put('os-services/disable', post_body) + body = json.loads(body) + self.validate_response(schema.enable_disable_service, resp, body) + return rest_client.ResponseBody(resp, body) diff --git a/tempest_lib/tests/services/compute/test_services_client.py b/tempest_lib/tests/services/compute/test_services_client.py new file mode 100644 index 0000000..67d717a --- /dev/null +++ b/tempest_lib/tests/services/compute/test_services_client.py @@ -0,0 +1,94 @@ +# Copyright 2015 NEC 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.services.compute import services_client +from tempest_lib.tests import fake_auth_provider +from tempest_lib.tests.services.compute import base + + +class TestServicesClient(base.BaseComputeServiceTest): + + FAKE_SERVICES = { + "services": + [{ + "status": "enabled", + "binary": "nova-conductor", + "zone": "internal", + "state": "up", + "updated_at": "2015-08-19T06:50:55.000000", + "host": "controller", + "disabled_reason": None, + "id": 1 + }] + } + + FAKE_SERVICE = { + "service": + { + "status": "enabled", + "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') + + def test_list_services_with_str_body(self): + self.check_service_client_function( + self.client.list_services, + 'tempest_lib.common.rest_client.RestClient.get', + self.FAKE_SERVICES) + + def test_list_services_with_bytes_body(self): + self.check_service_client_function( + self.client.list_services, + 'tempest_lib.common.rest_client.RestClient.get', + self.FAKE_SERVICES, to_utf=True) + + def _test_enable_service(self, bytes_body=False): + self.check_service_client_function( + self.client.enable_service, + 'tempest_lib.common.rest_client.RestClient.put', + self.FAKE_SERVICE, + bytes_body, + host_name="nova-conductor", binary="controller") + + def test_enable_service_with_str_body(self): + self._test_enable_service() + + def test_enable_service_with_bytes_body(self): + self._test_enable_service(bytes_body=True) + + def _test_disable_service(self, bytes_body=False): + fake_service = copy.deepcopy(self.FAKE_SERVICE) + fake_service["service"]["status"] = "disable" + + self.check_service_client_function( + self.client.disable_service, + 'tempest_lib.common.rest_client.RestClient.put', + fake_service, + bytes_body, + host_name="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)