From 9caf269c2874499c16b16ab517d0365f2e626321 Mon Sep 17 00:00:00 2001 From: Reedip Banerjee Date: Fri, 2 Oct 2015 08:04:32 +0530 Subject: [PATCH] Migrated hosts_client.py from tempest This migrates the above files from tempest. This includes tempest commits: * hosts_client.py: Ife5bdbbcbdd856ecbce66e8b9c74c70dbb93f077 * test_hosts_client.py: Ifc4aa410d1db84ea8c43c64d1feadd74dbfbc4a4 * hosts.py: Ica929c402a3d042ae751302384e68853eb28b405 to see the commit history for these files refer to the above Change-Ids in the tempest repository. Partially implements blueprint migrate-service-clients-to-tempest-lib Change-Id: I59c82bc896d5036d936e6f9cddb3c7b004648dde --- .../api_schema/response/compute/v2_1/hosts.py | 116 ++++++++++++++ tempest_lib/services/compute/hosts_client.py | 81 ++++++++++ .../services/compute/test_hosts_client.py | 147 ++++++++++++++++++ 3 files changed, 344 insertions(+) create mode 100644 tempest_lib/api_schema/response/compute/v2_1/hosts.py create mode 100644 tempest_lib/services/compute/hosts_client.py create mode 100644 tempest_lib/tests/services/compute/test_hosts_client.py diff --git a/tempest_lib/api_schema/response/compute/v2_1/hosts.py b/tempest_lib/api_schema/response/compute/v2_1/hosts.py new file mode 100644 index 0000000..ae70ff1 --- /dev/null +++ b/tempest_lib/api_schema/response/compute/v2_1/hosts.py @@ -0,0 +1,116 @@ +# 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. + +import copy + + +list_hosts = { + 'status_code': [200], + 'response_body': { + 'type': 'object', + 'properties': { + 'hosts': { + 'type': 'array', + 'items': { + 'type': 'object', + 'properties': { + 'host_name': {'type': 'string'}, + 'service': {'type': 'string'}, + 'zone': {'type': 'string'} + }, + 'additionalProperties': False, + 'required': ['host_name', 'service', 'zone'] + } + } + }, + 'additionalProperties': False, + 'required': ['hosts'] + } +} + +get_host_detail = { + 'status_code': [200], + 'response_body': { + 'type': 'object', + 'properties': { + 'host': { + 'type': 'array', + 'item': { + 'type': 'object', + 'properties': { + 'resource': { + 'type': 'object', + 'properties': { + 'cpu': {'type': 'integer'}, + 'disk_gb': {'type': 'integer'}, + 'host': {'type': 'string'}, + 'memory_mb': {'type': 'integer'}, + 'project': {'type': 'string'} + }, + 'additionalProperties': False, + 'required': ['cpu', 'disk_gb', 'host', + 'memory_mb', 'project'] + } + }, + 'additionalProperties': False, + 'required': ['resource'] + } + } + }, + 'additionalProperties': False, + 'required': ['host'] + } +} + +startup_host = { + 'status_code': [200], + 'response_body': { + 'type': 'object', + 'properties': { + 'host': {'type': 'string'}, + 'power_action': {'enum': ['startup']} + }, + 'additionalProperties': False, + 'required': ['host', 'power_action'] + } +} + +# The 'power_action' attribute of 'shutdown_host' API is 'shutdown' +shutdown_host = copy.deepcopy(startup_host) + +shutdown_host['response_body']['properties']['power_action'] = { + 'enum': ['shutdown'] +} + +# The 'power_action' attribute of 'reboot_host' API is 'reboot' +reboot_host = copy.deepcopy(startup_host) + +reboot_host['response_body']['properties']['power_action'] = { + 'enum': ['reboot'] +} + +update_host = { + 'status_code': [200], + 'response_body': { + 'type': 'object', + 'properties': { + 'host': {'type': 'string'}, + 'maintenance_mode': {'enum': ['on_maintenance', + 'off_maintenance']}, + 'status': {'enum': ['enabled', 'disabled']} + }, + 'additionalProperties': False, + 'required': ['host', 'maintenance_mode', 'status'] + } +} diff --git a/tempest_lib/services/compute/hosts_client.py b/tempest_lib/services/compute/hosts_client.py new file mode 100644 index 0000000..4fd4d8d --- /dev/null +++ b/tempest_lib/services/compute/hosts_client.py @@ -0,0 +1,81 @@ +# Copyright 2013 IBM Corp. +# +# 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 hosts as schema +from tempest_lib.common import rest_client + + +class HostsClient(rest_client.RestClient): + + def list_hosts(self, **params): + """Lists all hosts.""" + + url = 'os-hosts' + if params: + url += '?%s' % urllib.urlencode(params) + + resp, body = self.get(url) + body = json.loads(body) + self.validate_response(schema.list_hosts, resp, body) + return rest_client.ResponseBody(resp, body) + + def show_host(self, hostname): + """Show detail information for the host.""" + + resp, body = self.get("os-hosts/%s" % hostname) + body = json.loads(body) + self.validate_response(schema.get_host_detail, resp, body) + return rest_client.ResponseBody(resp, body) + + def update_host(self, hostname, **kwargs): + """Update a host.""" + + request_body = { + 'status': None, + 'maintenance_mode': None, + } + request_body.update(**kwargs) + request_body = json.dumps(request_body) + + resp, body = self.put("os-hosts/%s" % hostname, request_body) + body = json.loads(body) + self.validate_response(schema.update_host, resp, body) + return rest_client.ResponseBody(resp, body) + + def startup_host(self, hostname): + """Startup a host.""" + + resp, body = self.get("os-hosts/%s/startup" % hostname) + body = json.loads(body) + self.validate_response(schema.startup_host, resp, body) + return rest_client.ResponseBody(resp, body) + + def shutdown_host(self, hostname): + """Shutdown a host.""" + + resp, body = self.get("os-hosts/%s/shutdown" % hostname) + body = json.loads(body) + self.validate_response(schema.shutdown_host, resp, body) + return rest_client.ResponseBody(resp, body) + + def reboot_host(self, hostname): + """reboot a host.""" + + resp, body = self.get("os-hosts/%s/reboot" % hostname) + body = json.loads(body) + self.validate_response(schema.reboot_host, resp, body) + return rest_client.ResponseBody(resp, body) diff --git a/tempest_lib/tests/services/compute/test_hosts_client.py b/tempest_lib/tests/services/compute/test_hosts_client.py new file mode 100644 index 0000000..c1e053a --- /dev/null +++ b/tempest_lib/tests/services/compute/test_hosts_client.py @@ -0,0 +1,147 @@ +# 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. + +from tempest_lib.services.compute import hosts_client +from tempest_lib.tests import fake_auth_provider +from tempest_lib.tests.services.compute import base + + +class TestHostsClient(base.BaseComputeServiceTest): + FAKE_HOST_DATA = { + "host": { + "resource": { + "cpu": 1, + "disk_gb": 1028, + "host": "c1a7de0ac9d94e4baceae031d05caae3", + "memory_mb": 8192, + "project": "(total)" + } + }, + "hosts": { + "host_name": "c1a7de0ac9d94e4baceae031d05caae3", + "service": "conductor", + "zone": "internal" + }, + "enable_hosts": { + "host": "65c5d5b7e3bd44308e67fc50f362aee6", + "maintenance_mode": "off_maintenance", + "status": "enabled" + } + } + + FAKE_CONTROL_DATA = { + "shutdown": { + "host": "c1a7de0ac9d94e4baceae031d05caae3", + "power_action": "shutdown" + }, + "startup": { + "host": "c1a7de0ac9d94e4baceae031d05caae3", + "power_action": "startup" + }, + "reboot": { + "host": "c1a7de0ac9d94e4baceae031d05caae3", + "power_action": "reboot" + }} + + HOST_DATA = {'host': [FAKE_HOST_DATA['host']]} + HOSTS_DATA = {'hosts': [FAKE_HOST_DATA['hosts']]} + ENABLE_HOST_DATA = FAKE_HOST_DATA['enable_hosts'] + HOST_ID = "c1a7de0ac9d94e4baceae031d05caae3" + TEST_HOST_DATA = { + "status": "enable", + "maintenance_mode": "disable" + } + + def setUp(self): + super(TestHostsClient, self).setUp() + fake_auth = fake_auth_provider.FakeAuthProvider() + self.client = hosts_client.HostsClient(fake_auth, 'compute', + 'regionOne') + self.params = {'hostname': self.HOST_ID} + self.func2mock = { + 'get': 'tempest_lib.common.rest_client.RestClient.get', + 'put': 'tempest_lib.common.rest_client.RestClient.put'} + + def _test_host_data(self, test_type='list', bytes_body=False): + expected_resp = self.HOST_DATA + if test_type != 'list': + function_call = self.client.show_host + else: + expected_resp = self.HOSTS_DATA + function_call = self.client.list_hosts + self.params = {'host_name': self.HOST_ID} + + self.check_service_client_function( + function_call, self.func2mock['get'], + expected_resp, bytes_body, + 200, **self.params) + + def _test_update_hosts(self, bytes_body=False): + expected_resp = self.ENABLE_HOST_DATA + self.check_service_client_function( + self.client.update_host, self.func2mock['put'], + expected_resp, bytes_body, + 200, **self.params) + + def _test_control_host(self, control_op='reboot', bytes_body=False): + if control_op == 'start': + expected_resp = self.FAKE_CONTROL_DATA['startup'] + function_call = self.client.startup_host + elif control_op == 'stop': + expected_resp = self.FAKE_CONTROL_DATA['shutdown'] + function_call = self.client.shutdown_host + else: + expected_resp = self.FAKE_CONTROL_DATA['reboot'] + function_call = self.client.reboot_host + + self.check_service_client_function( + function_call, self.func2mock['get'], + expected_resp, bytes_body, + 200, **self.params) + + def test_show_host_with_str_body(self): + self._test_host_data('show') + + def test_show_host_with_bytes_body(self): + self._test_host_data('show', True) + + def test_list_host_with_str_body(self): + self._test_host_data() + + def test_list_host_with_bytes_body(self): + self._test_host_data(bytes_body=True) + + def test_start_host_with_str_body(self): + self._test_control_host('start') + + def test_start_host_with_bytes_body(self): + self._test_control_host('start', True) + + def test_stop_host_with_str_body(self): + self._test_control_host('stop') + + def test_stop_host_with_bytes_body(self): + self._test_control_host('stop', True) + + def test_reboot_host_with_str_body(self): + self._test_control_host('reboot') + + def test_reboot_host_with_bytes_body(self): + self._test_control_host('reboot', True) + + def test_update_host_with_str_body(self): + self._test_update_hosts() + + def test_update_host_with_bytes_body(self): + self._test_update_hosts(True)