From 53d94a82b7b2d6045e9e63c62bcceab7d50bac0b Mon Sep 17 00:00:00 2001 From: zhufl Date: Thu, 20 Sep 2018 17:00:09 +0800 Subject: [PATCH] Add response schema validation for volume qos-specs This is to add response schema validation for volume qos-specs. Besides, there are some inconsistencies in volume qos-specs api ref, Ib5c9b3a15ee2ca40c19e7a530d1ff5351d3dcaf8 will fix them. Change-Id: I433c78821185600a230803cddec54a5d264c2ca5 partially-implements: blueprint volume-response-schema-validation --- tempest/lib/api_schema/response/volume/qos.py | 123 ++++++++++++++++++ tempest/lib/services/volume/v3/qos_client.py | 21 +-- 2 files changed, 134 insertions(+), 10 deletions(-) create mode 100644 tempest/lib/api_schema/response/volume/qos.py diff --git a/tempest/lib/api_schema/response/volume/qos.py b/tempest/lib/api_schema/response/volume/qos.py new file mode 100644 index 0000000000..d1b3910622 --- /dev/null +++ b/tempest/lib/api_schema/response/volume/qos.py @@ -0,0 +1,123 @@ +# Copyright 2018 ZTE 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. + +show_qos = { + 'status_code': [200], + 'response_body': { + 'type': 'object', + 'properties': { + 'qos_specs': { + 'type': 'object', + 'properties': { + 'name': {'type': 'string'}, + 'id': {'type': 'string', 'format': 'uuid'}, + 'consumer': {'type': 'string'}, + 'specs': {'type': ['object', 'null']}, + }, + 'additionalProperties': False, + 'required': ['name', 'id', 'specs'] + }, + 'links': { + 'type': 'array', + 'items': { + 'type': 'object', + 'properties': { + 'href': {'type': 'string', + 'format': 'uri'}, + 'rel': {'type': 'string'}, + }, + 'additionalProperties': False, + 'required': ['href', 'rel'] + } + } + }, + 'additionalProperties': False, + 'required': ['qos_specs', 'links'] + } +} + +delete_qos = {'status_code': [202]} + +list_qos = { + 'status_code': [200], + 'response_body': { + 'type': 'object', + 'properties': { + 'qos_specs': { + 'type': 'array', + 'items': { + 'type': 'object', + 'properties': { + 'specs': { + 'type': 'object', + 'patternProperties': {'^.+$': {'type': 'string'}} + }, + 'consumer': {'type': 'string'}, + 'id': {'type': 'string', 'format': 'uuid'}, + 'name': {'type': 'string'} + }, + 'additionalProperties': False, + 'required': ['specs', 'id', 'name'] + } + } + }, + 'additionalProperties': False, + 'required': ['qos_specs'] + } +} + +set_qos_key = { + 'status_code': [200], + 'response_body': { + 'type': 'object', + 'properties': { + 'qos_specs': { + 'type': 'object', + 'patternProperties': {'^.+$': {'type': 'string'}} + }, + }, + 'additionalProperties': False, + 'required': ['qos_specs'] + } +} + +unset_qos_key = {'status_code': [202]} +associate_qos = {'status_code': [202]} + +show_association_qos = { + 'status_code': [200], + 'response_body': { + 'type': 'object', + 'properties': { + 'qos_associations': { + 'type': 'array', + 'items': { + 'type': 'object', + 'properties': { + 'association_type': {'type': 'string'}, + 'id': {'type': 'string', 'format': 'uuid'}, + 'name': {'type': 'string'} + }, + 'additionalProperties': False, + 'required': ['association_type', 'id', 'name'] + } + }, + }, + 'additionalProperties': False, + 'required': ['qos_associations'] + } +} + +disassociate_qos = {'status_code': [202]} +disassociate_all_qos = {'status_code': [202]} diff --git a/tempest/lib/services/volume/v3/qos_client.py b/tempest/lib/services/volume/v3/qos_client.py index 8f4d37ffc4..520559092c 100644 --- a/tempest/lib/services/volume/v3/qos_client.py +++ b/tempest/lib/services/volume/v3/qos_client.py @@ -14,6 +14,7 @@ from oslo_serialization import jsonutils as json +from tempest.lib.api_schema.response.volume import qos as schema from tempest.lib.common import rest_client from tempest.lib import exceptions as lib_exc @@ -45,15 +46,15 @@ class QosSpecsClient(rest_client.RestClient): """ post_body = json.dumps({'qos_specs': kwargs}) resp, body = self.post('qos-specs', post_body) - self.expected_success(200, resp.status) body = json.loads(body) + self.validate_response(schema.show_qos, resp, body) return rest_client.ResponseBody(resp, body) def delete_qos(self, qos_id, force=False): """Delete the specified QoS specification.""" resp, body = self.delete( "qos-specs/%s?force=%s" % (qos_id, force)) - self.expected_success(202, resp.status) + self.validate_response(schema.delete_qos, resp, body) return rest_client.ResponseBody(resp, body) def list_qos(self): @@ -61,7 +62,7 @@ class QosSpecsClient(rest_client.RestClient): url = 'qos-specs' resp, body = self.get(url) body = json.loads(body) - self.expected_success(200, resp.status) + self.validate_response(schema.list_qos, resp, body) return rest_client.ResponseBody(resp, body) def show_qos(self, qos_id): @@ -69,7 +70,7 @@ class QosSpecsClient(rest_client.RestClient): url = "qos-specs/%s" % qos_id resp, body = self.get(url) body = json.loads(body) - self.expected_success(200, resp.status) + self.validate_response(schema.show_qos, resp, body) return rest_client.ResponseBody(resp, body) def set_qos_key(self, qos_id, **kwargs): @@ -82,7 +83,7 @@ class QosSpecsClient(rest_client.RestClient): put_body = json.dumps({"qos_specs": kwargs}) resp, body = self.put('qos-specs/%s' % qos_id, put_body) body = json.loads(body) - self.expected_success(200, resp.status) + self.validate_response(schema.set_qos_key, resp, body) return rest_client.ResponseBody(resp, body) def unset_qos_key(self, qos_id, keys): @@ -96,7 +97,7 @@ class QosSpecsClient(rest_client.RestClient): """ put_body = json.dumps({'keys': keys}) resp, body = self.put('qos-specs/%s/delete_keys' % qos_id, put_body) - self.expected_success(202, resp.status) + self.validate_response(schema.unset_qos_key, resp, body) return rest_client.ResponseBody(resp, body) def associate_qos(self, qos_id, vol_type_id): @@ -104,7 +105,7 @@ class QosSpecsClient(rest_client.RestClient): url = "qos-specs/%s/associate" % qos_id url += "?vol_type_id=%s" % vol_type_id resp, body = self.get(url) - self.expected_success(202, resp.status) + self.validate_response(schema.associate_qos, resp, body) return rest_client.ResponseBody(resp, body) def show_association_qos(self, qos_id): @@ -112,7 +113,7 @@ class QosSpecsClient(rest_client.RestClient): url = "qos-specs/%s/associations" % qos_id resp, body = self.get(url) body = json.loads(body) - self.expected_success(200, resp.status) + self.validate_response(schema.show_association_qos, resp, body) return rest_client.ResponseBody(resp, body) def disassociate_qos(self, qos_id, vol_type_id): @@ -120,12 +121,12 @@ class QosSpecsClient(rest_client.RestClient): url = "qos-specs/%s/disassociate" % qos_id url += "?vol_type_id=%s" % vol_type_id resp, body = self.get(url) - self.expected_success(202, resp.status) + self.validate_response(schema.disassociate_qos, resp, body) return rest_client.ResponseBody(resp, body) def disassociate_all_qos(self, qos_id): """Disassociate the specified QoS with all associations.""" url = "qos-specs/%s/disassociate_all" % qos_id resp, body = self.get(url) - self.expected_success(202, resp.status) + self.validate_response(schema.disassociate_all_qos, resp, body) return rest_client.ResponseBody(resp, body)