From 09bebae231e8d80d29a9d8b9b2eb0edc0439cebe Mon Sep 17 00:00:00 2001 From: John Fischer Date: Fri, 25 Sep 2015 03:55:53 -0400 Subject: [PATCH] Migrated hypervisor_client.py This migrates the above files from tempest. This includes tempest commits: * hypervisor_client.py : Icf782e4469fcecba773b3187c1b36d9d49829c93 * test_hypervisor_client.py: I401057ecdf70d693c285cdaf64e1a5db62fc9b17 * hypervisors.py : I64883306235dc3b90a3a878674532f77d825d5c4 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: I1a1dcab8ecd6e12ec03d14303517c8cb1e68ec60 --- .../response/compute/v2_1/hypervisors.py | 195 ++++++++++++++++++ .../response/compute/v2_1/parameter_types.py | 96 +++++++++ .../services/compute/hypervisor_client.py | 70 +++++++ .../compute/test_hypervisor_client.py | 167 +++++++++++++++ 4 files changed, 528 insertions(+) create mode 100644 tempest_lib/api_schema/response/compute/v2_1/hypervisors.py create mode 100644 tempest_lib/api_schema/response/compute/v2_1/parameter_types.py create mode 100644 tempest_lib/services/compute/hypervisor_client.py create mode 100644 tempest_lib/tests/services/compute/test_hypervisor_client.py diff --git a/tempest_lib/api_schema/response/compute/v2_1/hypervisors.py b/tempest_lib/api_schema/response/compute/v2_1/hypervisors.py new file mode 100644 index 0000000..32697db --- /dev/null +++ b/tempest_lib/api_schema/response/compute/v2_1/hypervisors.py @@ -0,0 +1,195 @@ +# 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 + +from tempest_lib.api_schema.response.compute.v2_1 import parameter_types + +get_hypervisor_statistics = { + 'status_code': [200], + 'response_body': { + 'type': 'object', + 'properties': { + 'hypervisor_statistics': { + 'type': 'object', + 'properties': { + 'count': {'type': 'integer'}, + 'current_workload': {'type': 'integer'}, + 'disk_available_least': {'type': ['integer', 'null']}, + 'free_disk_gb': {'type': 'integer'}, + 'free_ram_mb': {'type': 'integer'}, + 'local_gb': {'type': 'integer'}, + 'local_gb_used': {'type': 'integer'}, + 'memory_mb': {'type': 'integer'}, + 'memory_mb_used': {'type': 'integer'}, + 'running_vms': {'type': 'integer'}, + 'vcpus': {'type': 'integer'}, + 'vcpus_used': {'type': 'integer'} + }, + 'additionalProperties': False, + 'required': ['count', 'current_workload', + 'disk_available_least', 'free_disk_gb', + 'free_ram_mb', 'local_gb', 'local_gb_used', + 'memory_mb', 'memory_mb_used', 'running_vms', + 'vcpus', 'vcpus_used'] + } + }, + 'additionalProperties': False, + 'required': ['hypervisor_statistics'] + } +} + + +hypervisor_detail = { + 'type': 'object', + 'properties': { + 'status': {'type': 'string'}, + 'state': {'type': 'string'}, + 'cpu_info': {'type': 'string'}, + 'current_workload': {'type': 'integer'}, + 'disk_available_least': {'type': ['integer', 'null']}, + 'host_ip': parameter_types.ip_address, + 'free_disk_gb': {'type': 'integer'}, + 'free_ram_mb': {'type': 'integer'}, + 'hypervisor_hostname': {'type': 'string'}, + 'hypervisor_type': {'type': 'string'}, + 'hypervisor_version': {'type': 'integer'}, + 'id': {'type': ['integer', 'string']}, + 'local_gb': {'type': 'integer'}, + 'local_gb_used': {'type': 'integer'}, + 'memory_mb': {'type': 'integer'}, + 'memory_mb_used': {'type': 'integer'}, + 'running_vms': {'type': 'integer'}, + 'service': { + 'type': 'object', + 'properties': { + 'host': {'type': 'string'}, + 'id': {'type': ['integer', 'string']}, + 'disabled_reason': {'type': ['string', 'null']} + }, + 'additionalProperties': False, + 'required': ['host', 'id'] + }, + 'vcpus': {'type': 'integer'}, + 'vcpus_used': {'type': 'integer'} + }, + 'additionalProperties': False, + # NOTE: When loading os-hypervisor-status extension, + # a response contains status and state. So these params + # should not be required. + 'required': ['cpu_info', 'current_workload', + 'disk_available_least', 'host_ip', + 'free_disk_gb', 'free_ram_mb', + 'hypervisor_hostname', 'hypervisor_type', + 'hypervisor_version', 'id', 'local_gb', + 'local_gb_used', 'memory_mb', 'memory_mb_used', + 'running_vms', 'service', 'vcpus', 'vcpus_used'] +} + +list_hypervisors_detail = { + 'status_code': [200], + 'response_body': { + 'type': 'object', + 'properties': { + 'hypervisors': { + 'type': 'array', + 'items': hypervisor_detail + } + }, + 'additionalProperties': False, + 'required': ['hypervisors'] + } +} + +get_hypervisor = { + 'status_code': [200], + 'response_body': { + 'type': 'object', + 'properties': { + 'hypervisor': hypervisor_detail + }, + 'additionalProperties': False, + 'required': ['hypervisor'] + } +} + +list_search_hypervisors = { + 'status_code': [200], + 'response_body': { + 'type': 'object', + 'properties': { + 'hypervisors': { + 'type': 'array', + 'items': { + 'type': 'object', + 'properties': { + 'status': {'type': 'string'}, + 'state': {'type': 'string'}, + 'id': {'type': ['integer', 'string']}, + 'hypervisor_hostname': {'type': 'string'} + }, + 'additionalProperties': False, + # NOTE: When loading os-hypervisor-status extension, + # a response contains status and state. So these params + # should not be required. + 'required': ['id', 'hypervisor_hostname'] + } + } + }, + 'additionalProperties': False, + 'required': ['hypervisors'] + } +} + +get_hypervisor_uptime = { + 'status_code': [200], + 'response_body': { + 'type': 'object', + 'properties': { + 'hypervisor': { + 'type': 'object', + 'properties': { + 'status': {'type': 'string'}, + 'state': {'type': 'string'}, + 'id': {'type': ['integer', 'string']}, + 'hypervisor_hostname': {'type': 'string'}, + 'uptime': {'type': 'string'} + }, + 'additionalProperties': False, + # NOTE: When loading os-hypervisor-status extension, + # a response contains status and state. So these params + # should not be required. + 'required': ['id', 'hypervisor_hostname', 'uptime'] + } + }, + 'additionalProperties': False, + 'required': ['hypervisor'] + } +} + +get_hypervisors_servers = copy.deepcopy(list_search_hypervisors) +get_hypervisors_servers['response_body']['properties']['hypervisors']['items'][ + 'properties']['servers'] = { + 'type': 'array', + 'items': { + 'type': 'object', + 'properties': { + 'uuid': {'type': 'string'}, + 'name': {'type': 'string'} + }, + 'additionalProperties': False, + } + } +# In V2 API, if there is no servers (VM) on the Hypervisor host then 'servers' +# attribute will not be present in response body So it is not 'required'. diff --git a/tempest_lib/api_schema/response/compute/v2_1/parameter_types.py b/tempest_lib/api_schema/response/compute/v2_1/parameter_types.py new file mode 100644 index 0000000..07cc890 --- /dev/null +++ b/tempest_lib/api_schema/response/compute/v2_1/parameter_types.py @@ -0,0 +1,96 @@ +# 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. + +links = { + 'type': 'array', + 'items': { + 'type': 'object', + 'properties': { + 'href': { + 'type': 'string', + 'format': 'uri' + }, + 'rel': {'type': 'string'} + }, + 'additionalProperties': False, + 'required': ['href', 'rel'] + } +} + +mac_address = { + 'type': 'string', + 'pattern': '(?:[a-f0-9]{2}:){5}[a-f0-9]{2}' +} + +ip_address = { + 'oneOf': [ + { + 'type': 'string', + 'oneOf': [ + {'format': 'ipv4'}, + {'format': 'ipv6'} + ] + }, + {'type': 'null'} + ] +} + +access_ip_v4 = { + 'type': 'string', + 'oneOf': [{'format': 'ipv4'}, {'enum': ['']}] +} + +access_ip_v6 = { + 'type': 'string', + 'oneOf': [{'format': 'ipv6'}, {'enum': ['']}] +} + +addresses = { + 'type': 'object', + 'patternProperties': { + # NOTE: Here is for 'private' or something. + '^[a-zA-Z0-9-_.]+$': { + 'type': 'array', + 'items': { + 'type': 'object', + 'properties': { + 'version': {'type': 'integer'}, + 'addr': { + 'type': 'string', + 'oneOf': [ + {'format': 'ipv4'}, + {'format': 'ipv6'} + ] + } + }, + 'additionalProperties': False, + 'required': ['version', 'addr'] + } + } + } +} + +response_header = { + 'connection': {'type': 'string'}, + 'content-length': {'type': 'string'}, + 'content-type': {'type': 'string'}, + 'status': {'type': 'string'}, + 'x-compute-request-id': {'type': 'string'}, + 'vary': {'type': 'string'}, + 'x-openstack-nova-api-version': {'type': 'string'}, + 'date': { + 'type': 'string', + 'format': 'data-time' + } +} diff --git a/tempest_lib/services/compute/hypervisor_client.py b/tempest_lib/services/compute/hypervisor_client.py new file mode 100644 index 0000000..4276a5c --- /dev/null +++ b/tempest_lib/services/compute/hypervisor_client.py @@ -0,0 +1,70 @@ +# Copyright 2013 IBM 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 oslo_serialization import jsonutils as json + +from tempest_lib.api_schema.response.compute.v2_1 import hypervisors as schema +from tempest_lib.common import rest_client + + +class HypervisorClient(rest_client.RestClient): + + def list_hypervisors(self, detail=False): + """List hypervisors information.""" + url = 'os-hypervisors' + _schema = schema.list_search_hypervisors + if detail: + url += '/detail' + _schema = schema.list_hypervisors_detail + + resp, body = self.get(url) + body = json.loads(body) + self.validate_response(_schema, resp, body) + return rest_client.ResponseBody(resp, body) + + def show_hypervisor(self, hypervisor_id): + """Display the details of the specified hypervisor.""" + resp, body = self.get('os-hypervisors/%s' % hypervisor_id) + body = json.loads(body) + self.validate_response(schema.get_hypervisor, resp, body) + return rest_client.ResponseBody(resp, body) + + def list_servers_on_hypervisor(self, hypervisor_name): + """List instances belonging to the specified hypervisor.""" + resp, body = self.get('os-hypervisors/%s/servers' % hypervisor_name) + body = json.loads(body) + self.validate_response(schema.get_hypervisors_servers, resp, body) + return rest_client.ResponseBody(resp, body) + + def show_hypervisor_statistics(self): + """Get hypervisor statistics over all compute nodes.""" + resp, body = self.get('os-hypervisors/statistics') + body = json.loads(body) + self.validate_response(schema.get_hypervisor_statistics, resp, body) + return rest_client.ResponseBody(resp, body) + + def show_hypervisor_uptime(self, hypervisor_id): + """Display the uptime of the specified hypervisor.""" + resp, body = self.get('os-hypervisors/%s/uptime' % hypervisor_id) + body = json.loads(body) + self.validate_response(schema.get_hypervisor_uptime, resp, body) + return rest_client.ResponseBody(resp, body) + + def search_hypervisor(self, hypervisor_name): + """Search specified hypervisor.""" + resp, body = self.get('os-hypervisors/%s/search' % hypervisor_name) + body = json.loads(body) + self.validate_response(schema.list_search_hypervisors, resp, body) + return rest_client.ResponseBody(resp, body) diff --git a/tempest_lib/tests/services/compute/test_hypervisor_client.py b/tempest_lib/tests/services/compute/test_hypervisor_client.py new file mode 100644 index 0000000..1cb85a9 --- /dev/null +++ b/tempest_lib/tests/services/compute/test_hypervisor_client.py @@ -0,0 +1,167 @@ +# Copyright 2015 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 tempest_lib.services.compute import hypervisor_client +from tempest_lib.tests import fake_auth_provider +from tempest_lib.tests.services.compute import base + + +class TestHypervisorClient(base.BaseComputeServiceTest): + + hypervisor_id = "1" + hypervisor_name = "hyper.hostname.com" + + def setUp(self): + super(TestHypervisorClient, self).setUp() + fake_auth = fake_auth_provider.FakeAuthProvider() + self.client = hypervisor_client.HypervisorClient( + fake_auth, 'compute', 'regionOne') + + def test_list_hypervisor_str_body(self): + self._test_list_hypervisor(bytes_body=False) + + def test_list_hypervisor_byte_body(self): + self._test_list_hypervisor(bytes_body=True) + + def _test_list_hypervisor(self, bytes_body=False): + expected = {"hypervisors": [{ + "id": 1, + "hypervisor_hostname": "hypervisor1.hostname.com"}, + { + "id": 2, + "hypervisor_hostname": "hypervisor2.hostname.com"}]} + self.check_service_client_function( + self.client.list_hypervisors, + 'tempest_lib.common.rest_client.RestClient.get', + expected, bytes_body) + + def test_show_hypervisor_str_body(self): + self._test_show_hypervisor(bytes_body=False) + + def test_show_hypervisor_byte_body(self): + self._test_show_hypervisor(bytes_body=True) + + def _test_show_hypervisor(self, bytes_body=False): + expected = {"hypervisor": { + "cpu_info": "?", + "current_workload": 0, + "disk_available_least": 1, + "host_ip": "10.10.10.10", + "free_disk_gb": 1028, + "free_ram_mb": 7680, + "hypervisor_hostname": "fake-mini", + "hypervisor_type": "fake", + "hypervisor_version": 1, + "id": 1, + "local_gb": 1028, + "local_gb_used": 0, + "memory_mb": 8192, + "memory_mb_used": 512, + "running_vms": 0, + "service": { + "host": "fake_host", + "id": 2}, + "vcpus": 1, + "vcpus_used": 0}} + self.check_service_client_function( + self.client.show_hypervisor, + 'tempest_lib.common.rest_client.RestClient.get', + expected, bytes_body, + hypervisor_id=self.hypervisor_id) + + def test_list_servers_on_hypervisor_str_body(self): + self._test_list_servers_on_hypervisor(bytes_body=False) + + def test_list_servers_on_hypervisor_byte_body(self): + self._test_list_servers_on_hypervisor(bytes_body=True) + + def _test_list_servers_on_hypervisor(self, bytes_body=False): + expected = {"hypervisors": [{ + "id": 1, + "hypervisor_hostname": "hyper.hostname.com", + "servers": [{ + "uuid": "e1ae8fc4-b72d-4c2f-a427-30dd420b6277", + "name": "instance-00000001"}, + { + "uuid": "e1ae8fc4-b72d-4c2f-a427-30dd42066666", + "name": "instance-00000002"} + ]} + ]} + self.check_service_client_function( + self.client.list_servers_on_hypervisor, + 'tempest_lib.common.rest_client.RestClient.get', + expected, bytes_body, + hypervisor_name=self.hypervisor_name) + + def test_show_hypervisor_statistics_str_body(self): + self._test_show_hypervisor_statistics(bytes_body=False) + + def test_show_hypervisor_statistics_byte_body(self): + self._test_show_hypervisor_statistics(bytes_body=True) + + def _test_show_hypervisor_statistics(self, bytes_body=False): + expected = { + "hypervisor_statistics": { + "count": 1, + "current_workload": 0, + "disk_available_least": 0, + "free_disk_gb": 1028, + "free_ram_mb": 7680, + "local_gb": 1028, + "local_gb_used": 0, + "memory_mb": 8192, + "memory_mb_used": 512, + "running_vms": 0, + "vcpus": 1, + "vcpus_used": 0}} + self.check_service_client_function( + self.client.show_hypervisor_statistics, + 'tempest_lib.common.rest_client.RestClient.get', + expected, bytes_body) + + def test_show_hypervisor_uptime_str_body(self): + self._test_show_hypervisor_uptime(bytes_body=False) + + def test_show_hypervisor_uptime_byte_body(self): + self._test_show_hypervisor_uptime(bytes_body=True) + + def _test_show_hypervisor_uptime(self, bytes_body=False): + expected = { + "hypervisor": { + "hypervisor_hostname": "fake-mini", + "id": 1, + "uptime": (" 08:32:11 up 93 days, 18:25, 12 users, " + " load average: 0.20, 0.12, 0.14") + }} + self.check_service_client_function( + self.client.show_hypervisor_uptime, + 'tempest_lib.common.rest_client.RestClient.get', + expected, bytes_body, + hypervisor_id=self.hypervisor_id) + + def test_search_hypervisor_str_body(self): + self._test_search_hypervisor(bytes_body=False) + + def test_search_hypervisor_byte_body(self): + self._test_search_hypervisor(bytes_body=True) + + def _test_search_hypervisor(self, bytes_body=False): + expected = {"hypervisors": [{ + "id": 2, + "hypervisor_hostname": "hyper.hostname.com"}]} + self.check_service_client_function( + self.client.search_hypervisor, + 'tempest_lib.common.rest_client.RestClient.get', + expected, bytes_body, + hypervisor_name=self.hypervisor_name)