From 639d842a22c25b503b8fce5426d4aee15da2b0b6 Mon Sep 17 00:00:00 2001 From: Ghanshyam Date: Wed, 26 Mar 2014 18:23:32 +0900 Subject: [PATCH] Verify flavor extra specs attributes of Nova APIs This patch adds the JSON schema for Nova V2/V3 flavor extra specs APIs response and validate the response with added JSON schema to block the backward incompatibility change in the future. The response body of flavor extra specs APIs are below: Response of set & get extra specs of V2 & V3- { "extra_specs": { "key1": "%(value1)s", "key2": "%(value2)s" } } Response code of set extra specs API differ- 200 - V2 201 - V3 Response of update & get extra specs key of V2 & V3- { "key1": "%(value1)s" } Response code of unset extra specs API differ- 200 - V2 204 - V3 This does not return any response body. Partially implements blueprint nova-api-attribute-test Change-Id: I41e566cb484d0b7fa29197c639f9f231c8c32ea9 --- .../api_schema/compute/flavors_extra_specs.py | 39 +++++++++++++++++++ tempest/api_schema/compute/v2/flavors.py | 4 ++ tempest/api_schema/compute/v3/flavors.py | 8 ++++ .../services/compute/json/flavors_client.py | 16 +++++++- .../compute/v3/json/flavors_client.py | 15 ++++++- 5 files changed, 78 insertions(+), 4 deletions(-) create mode 100644 tempest/api_schema/compute/flavors_extra_specs.py diff --git a/tempest/api_schema/compute/flavors_extra_specs.py b/tempest/api_schema/compute/flavors_extra_specs.py new file mode 100644 index 000000000..4003d367c --- /dev/null +++ b/tempest/api_schema/compute/flavors_extra_specs.py @@ -0,0 +1,39 @@ +# 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. + +flavor_extra_specs = { + 'status_code': [200], + 'response_body': { + 'type': 'object', + 'properties': { + 'extra_specs': { + 'type': 'object', + 'patternProperties': { + '^[a-zA-Z0-9_\-\. :]+$': {'type': 'string'} + } + } + }, + 'required': ['extra_specs'] + } +} + +flavor_extra_specs_key = { + 'status_code': [200], + 'response_body': { + 'type': 'object', + 'patternProperties': { + '^[a-zA-Z0-9_\-\. :]+$': {'type': 'string'} + } + } +} diff --git a/tempest/api_schema/compute/v2/flavors.py b/tempest/api_schema/compute/v2/flavors.py index 999ca19f7..48e6ceb1b 100644 --- a/tempest/api_schema/compute/v2/flavors.py +++ b/tempest/api_schema/compute/v2/flavors.py @@ -31,3 +31,7 @@ list_flavors_details['response_body']['properties']['flavors']['items'][ 'OS-FLV-EXT-DATA:ephemeral': {'type': 'integer'}}) # 'OS-FLV-DISABLED', 'os-flavor-access', 'rxtx_factor' and 'OS-FLV-EXT-DATA' # are API extensions. So they are not 'required'. + +unset_flavor_extra_specs = { + 'status_code': [200] +} diff --git a/tempest/api_schema/compute/v3/flavors.py b/tempest/api_schema/compute/v3/flavors.py index 542d2b1dd..468658c0c 100644 --- a/tempest/api_schema/compute/v3/flavors.py +++ b/tempest/api_schema/compute/v3/flavors.py @@ -15,6 +15,7 @@ import copy from tempest.api_schema.compute import flavors +from tempest.api_schema.compute import flavors_extra_specs list_flavors_details = copy.deepcopy(flavors.common_flavor_list_details) @@ -31,3 +32,10 @@ list_flavors_details['response_body']['properties']['flavors']['items'][ # So they are not 'required'. list_flavors_details['response_body']['properties']['flavors']['items'][ 'required'].extend(['disabled', 'ephemeral']) + +set_flavor_extra_specs = copy.deepcopy(flavors_extra_specs.flavor_extra_specs) +set_flavor_extra_specs['status_code'] = [201] + +unset_flavor_extra_specs = { + 'status_code': [204] +} diff --git a/tempest/services/compute/json/flavors_client.py b/tempest/services/compute/json/flavors_client.py index 0206b82a5..65d2657d5 100644 --- a/tempest/services/compute/json/flavors_client.py +++ b/tempest/services/compute/json/flavors_client.py @@ -18,6 +18,8 @@ import urllib from tempest.api_schema.compute import flavors as common_schema from tempest.api_schema.compute import flavors_access as schema_access +from tempest.api_schema.compute import flavors_extra_specs \ + as schema_extra_specs from tempest.api_schema.compute.v2 import flavors as v2schema from tempest.common import rest_client from tempest import config @@ -99,12 +101,16 @@ class FlavorsClientJSON(rest_client.RestClient): resp, body = self.post('flavors/%s/os-extra_specs' % flavor_id, post_body) body = json.loads(body) + self.validate_response(schema_extra_specs.flavor_extra_specs, + resp, body) return resp, body['extra_specs'] def get_flavor_extra_spec(self, flavor_id): """Gets extra Specs details of the mentioned flavor.""" resp, body = self.get('flavors/%s/os-extra_specs' % flavor_id) body = json.loads(body) + self.validate_response(schema_extra_specs.flavor_extra_specs, + resp, body) return resp, body['extra_specs'] def get_flavor_extra_spec_with_key(self, flavor_id, key): @@ -112,6 +118,8 @@ class FlavorsClientJSON(rest_client.RestClient): resp, body = self.get('flavors/%s/os-extra_specs/%s' % (str(flavor_id), key)) body = json.loads(body) + self.validate_response(schema_extra_specs.flavor_extra_specs_key, + resp, body) return resp, body def update_flavor_extra_spec(self, flavor_id, key, **kwargs): @@ -119,12 +127,16 @@ class FlavorsClientJSON(rest_client.RestClient): resp, body = self.put('flavors/%s/os-extra_specs/%s' % (flavor_id, key), json.dumps(kwargs)) body = json.loads(body) + self.validate_response(schema_extra_specs.flavor_extra_specs_key, + resp, body) return resp, body def unset_flavor_extra_spec(self, flavor_id, key): """Unsets extra Specs from the mentioned flavor.""" - return self.delete('flavors/%s/os-extra_specs/%s' % (str(flavor_id), - key)) + resp, body = self.delete('flavors/%s/os-extra_specs/%s' % + (str(flavor_id), key)) + self.validate_response(v2schema.unset_flavor_extra_specs, resp, body) + return resp, body def list_flavor_access(self, flavor_id): """Gets flavor access information given the flavor id.""" diff --git a/tempest/services/compute/v3/json/flavors_client.py b/tempest/services/compute/v3/json/flavors_client.py index 189fe3f22..602fee231 100644 --- a/tempest/services/compute/v3/json/flavors_client.py +++ b/tempest/services/compute/v3/json/flavors_client.py @@ -18,6 +18,8 @@ import urllib from tempest.api_schema.compute import flavors as common_schema from tempest.api_schema.compute import flavors_access as schema_access +from tempest.api_schema.compute import flavors_extra_specs \ + as schema_extra_specs from tempest.api_schema.compute.v3 import flavors as v3schema from tempest.common import rest_client from tempest import config @@ -99,12 +101,15 @@ class FlavorsV3ClientJSON(rest_client.RestClient): resp, body = self.post('flavors/%s/flavor-extra-specs' % flavor_id, post_body) body = json.loads(body) + self.validate_response(v3schema.set_flavor_extra_specs, resp, body) return resp, body['extra_specs'] def get_flavor_extra_spec(self, flavor_id): """Gets extra Specs details of the mentioned flavor.""" resp, body = self.get('flavors/%s/flavor-extra-specs' % flavor_id) body = json.loads(body) + self.validate_response(schema_extra_specs.flavor_extra_specs, + resp, body) return resp, body['extra_specs'] def get_flavor_extra_spec_with_key(self, flavor_id, key): @@ -112,6 +117,8 @@ class FlavorsV3ClientJSON(rest_client.RestClient): resp, body = self.get('flavors/%s/flavor-extra-specs/%s' % (str(flavor_id), key)) body = json.loads(body) + self.validate_response(schema_extra_specs.flavor_extra_specs_key, + resp, body) return resp, body def update_flavor_extra_spec(self, flavor_id, key, **kwargs): @@ -119,12 +126,16 @@ class FlavorsV3ClientJSON(rest_client.RestClient): resp, body = self.put('flavors/%s/flavor-extra-specs/%s' % (flavor_id, key), json.dumps(kwargs)) body = json.loads(body) + self.validate_response(schema_extra_specs.flavor_extra_specs_key, + resp, body) return resp, body def unset_flavor_extra_spec(self, flavor_id, key): """Unsets extra Specs from the mentioned flavor.""" - return self.delete('flavors/%s/flavor-extra-specs/%s' % - (str(flavor_id), key)) + resp, body = self.delete('flavors/%s/flavor-extra-specs/%s' % + (str(flavor_id), key)) + self.validate_response(v3schema.unset_flavor_extra_specs, resp, body) + return resp, body def list_flavor_access(self, flavor_id): """Gets flavor access information given the flavor id."""