From 52c5d28190a1b3ee4d76c42021495b04f4fe5eb0 Mon Sep 17 00:00:00 2001 From: ghanshyam Date: Mon, 23 Apr 2018 08:43:25 +0000 Subject: [PATCH] Add 2.55 schema & update flavor API in flavors_client Compute microversion 2.55 adds 'description' in flavor APIs response and new API to update the same. This commit fill the schema gap for that and also add new updte flavor API in flavors_client lib. Change-Id: I91c049c3aa9dab5a272369edd18f2c5890d1e5b0 --- doc/source/microversion_testing.rst | 4 + ...pi-to-flavors-client-a859542fe54aab7c.yaml | 4 + .../admin/test_flavors_microversions.py | 43 +++++++ .../response/compute/v2_1/flavors.py | 2 +- .../response/compute/v2_55/__init__.py | 0 .../response/compute/v2_55/flavors.py | 112 ++++++++++++++++++ .../lib/services/compute/flavors_client.py | 35 +++++- .../services/compute/test_flavors_client.py | 35 ++++++ 8 files changed, 230 insertions(+), 5 deletions(-) create mode 100644 releasenotes/notes/add-update-flavor--api-to-flavors-client-a859542fe54aab7c.yaml create mode 100644 tempest/api/compute/admin/test_flavors_microversions.py create mode 100644 tempest/lib/api_schema/response/compute/v2_55/__init__.py create mode 100644 tempest/lib/api_schema/response/compute/v2_55/flavors.py diff --git a/doc/source/microversion_testing.rst b/doc/source/microversion_testing.rst index 942f9691d1..535dfc6328 100644 --- a/doc/source/microversion_testing.rst +++ b/doc/source/microversion_testing.rst @@ -342,6 +342,10 @@ Microversion tests implemented in Tempest .. _2.48: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id43 + * `2.55`_ + + .. _2.55: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id49 + * `2.60`_ .. _2.60: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id54 diff --git a/releasenotes/notes/add-update-flavor--api-to-flavors-client-a859542fe54aab7c.yaml b/releasenotes/notes/add-update-flavor--api-to-flavors-client-a859542fe54aab7c.yaml new file mode 100644 index 0000000000..222a99fd85 --- /dev/null +++ b/releasenotes/notes/add-update-flavor--api-to-flavors-client-a859542fe54aab7c.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Add update flavor API to compute flavors_client library. diff --git a/tempest/api/compute/admin/test_flavors_microversions.py b/tempest/api/compute/admin/test_flavors_microversions.py new file mode 100644 index 0000000000..027af258da --- /dev/null +++ b/tempest/api/compute/admin/test_flavors_microversions.py @@ -0,0 +1,43 @@ +# Copyright 2018 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.api.compute import base +from tempest.lib.common.utils import data_utils +from tempest.lib import decorators + + +class FlavorsV255TestJSON(base.BaseV2ComputeAdminTest): + min_microversion = '2.55' + max_microversion = 'latest' + + # NOTE(gmann): This class tests the flavors APIs + # response schema for the 2.55 microversion. + + @decorators.idempotent_id('61976b25-488d-41dc-9dcb-cb9693a7b075') + def test_crud_flavor(self): + flavor_id = data_utils.rand_int_id(start=1000) + # Checking create API response schema + new_flavor_id = self.create_flavor(ram=512, + vcpus=1, + disk=10, + id=flavor_id)['id'] + # Checking show API response schema + self.flavors_client.show_flavor(new_flavor_id)['flavor'] + # Checking update API response schema + self.admin_flavors_client.update_flavor(new_flavor_id, + description='new')['flavor'] + # Checking list details API response schema + self.flavors_client.list_flavors(detail=True)['flavors'] + # Checking list API response schema + self.flavors_client.list_flavors()['flavors'] diff --git a/tempest/lib/api_schema/response/compute/v2_1/flavors.py b/tempest/lib/api_schema/response/compute/v2_1/flavors.py index 547d94d572..af5e67f5c0 100644 --- a/tempest/lib/api_schema/response/compute/v2_1/flavors.py +++ b/tempest/lib/api_schema/response/compute/v2_1/flavors.py @@ -86,7 +86,7 @@ unset_flavor_extra_specs = { 'status_code': [200] } -create_get_flavor_details = { +create_update_get_flavor_details = { 'status_code': [200], 'response_body': { 'type': 'object', diff --git a/tempest/lib/api_schema/response/compute/v2_55/__init__.py b/tempest/lib/api_schema/response/compute/v2_55/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tempest/lib/api_schema/response/compute/v2_55/flavors.py b/tempest/lib/api_schema/response/compute/v2_55/flavors.py new file mode 100644 index 0000000000..823190a23a --- /dev/null +++ b/tempest/lib/api_schema/response/compute/v2_55/flavors.py @@ -0,0 +1,112 @@ +# Copyright 2018 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.api_schema.response.compute.v2_1 import parameter_types + +# Note(gmann): This is schema for microversion 2.55 which includes the +# following changes: +# Add new PUT API +# Adds a ``description`` field to the following APIs response: +# - ``GET /flavors`` +# - ``GET /flavors/detail`` +# - ``GET /flavors/{flavor_id}`` +# - ``POST /flavors`` + +flavor_description = { + 'type': ['string', 'null'], + 'minLength': 0, 'maxLength': 65535 +} + +list_flavors = { + 'status_code': [200], + 'response_body': { + 'type': 'object', + 'properties': { + 'flavors': { + 'type': 'array', + 'items': { + 'type': 'object', + 'properties': { + 'name': {'type': 'string'}, + 'links': parameter_types.links, + 'id': {'type': 'string'}, + 'description': flavor_description + }, + 'additionalProperties': False, + 'required': ['name', 'links', 'id', 'description'] + } + }, + 'flavors_links': parameter_types.links + }, + 'additionalProperties': False, + # NOTE(gmann): flavors_links attribute is not necessary + # to be present always So it is not 'required'. + 'required': ['flavors'] + } +} + +common_flavor_info = { + 'type': 'object', + 'properties': { + 'name': {'type': 'string'}, + 'links': parameter_types.links, + 'ram': {'type': 'integer'}, + 'vcpus': {'type': 'integer'}, + # 'swap' attributes comes as integer value but if it is empty + # it comes as "". So defining type of as string and integer. + 'swap': {'type': ['integer', 'string']}, + 'disk': {'type': 'integer'}, + 'id': {'type': 'string'}, + 'OS-FLV-DISABLED:disabled': {'type': 'boolean'}, + 'os-flavor-access:is_public': {'type': 'boolean'}, + 'rxtx_factor': {'type': 'number'}, + 'OS-FLV-EXT-DATA:ephemeral': {'type': 'integer'}, + 'description': flavor_description + }, + 'additionalProperties': False, + # 'OS-FLV-DISABLED', 'os-flavor-access', 'rxtx_factor' and + # 'OS-FLV-EXT-DATA' are API extensions. So they are not 'required'. + 'required': ['name', 'links', 'ram', 'vcpus', 'swap', 'disk', 'id', + 'description'] +} + +list_flavors_details = { + 'status_code': [200], + 'response_body': { + 'type': 'object', + 'properties': { + 'flavors': { + 'type': 'array', + 'items': common_flavor_info + }, + # NOTE(gmann): flavors_links attribute is not necessary + # to be present always So it is not 'required'. + 'flavors_links': parameter_types.links + }, + 'additionalProperties': False, + 'required': ['flavors'] + } +} + +create_update_get_flavor_details = { + 'status_code': [200], + 'response_body': { + 'type': 'object', + 'properties': { + 'flavor': common_flavor_info + }, + 'additionalProperties': False, + 'required': ['flavor'] + } +} diff --git a/tempest/lib/services/compute/flavors_client.py b/tempest/lib/services/compute/flavors_client.py index 0fb199151a..4923d7ef08 100644 --- a/tempest/lib/services/compute/flavors_client.py +++ b/tempest/lib/services/compute/flavors_client.py @@ -21,12 +21,18 @@ from tempest.lib.api_schema.response.compute.v2_1 import flavors_access \ as schema_access from tempest.lib.api_schema.response.compute.v2_1 import flavors_extra_specs \ as schema_extra_specs +from tempest.lib.api_schema.response.compute.v2_55 import flavors \ + as schemav255 from tempest.lib.common import rest_client from tempest.lib.services.compute import base_compute_client class FlavorsClient(base_compute_client.BaseComputeClient): + schema_versions_info = [ + {'min': None, 'max': '2.54', 'schema': schema}, + {'min': '2.55', 'max': None, 'schema': schemav255}] + def list_flavors(self, detail=False, **params): """Lists flavors. @@ -36,11 +42,12 @@ class FlavorsClient(base_compute_client.BaseComputeClient): https://developer.openstack.org/api-ref/compute/#list-flavors-with-details """ url = 'flavors' - _schema = schema.list_flavors - + schema = self.get_schema(self.schema_versions_info) if detail: url += '/detail' _schema = schema.list_flavors_details + else: + _schema = schema.list_flavors if params: url += '?%s' % urllib.urlencode(params) @@ -58,7 +65,9 @@ class FlavorsClient(base_compute_client.BaseComputeClient): """ resp, body = self.get("flavors/%s" % flavor_id) body = json.loads(body) - self.validate_response(schema.create_get_flavor_details, resp, body) + schema = self.get_schema(self.schema_versions_info) + self.validate_response(schema.create_update_get_flavor_details, + resp, body) return rest_client.ResponseBody(resp, body) def create_flavor(self, **kwargs): @@ -77,7 +86,25 @@ class FlavorsClient(base_compute_client.BaseComputeClient): resp, body = self.post('flavors', post_body) body = json.loads(body) - self.validate_response(schema.create_get_flavor_details, resp, body) + schema = self.get_schema(self.schema_versions_info) + self.validate_response(schema.create_update_get_flavor_details, + resp, body) + return rest_client.ResponseBody(resp, body) + + def update_flavor(self, flavor_id, **kwargs): + """Uodate the flavor or instance type. + + For a full list of available parameters, please refer to the official + API reference: + https://developer.openstack.org/api-ref/compute/#update-flavor-description + """ + put_body = json.dumps({'flavor': kwargs}) + resp, body = self.put("flavors/%s" % flavor_id, put_body) + + body = json.loads(body) + schema = self.get_schema(self.schema_versions_info) + self.validate_response(schema.create_update_get_flavor_details, + resp, body) return rest_client.ResponseBody(resp, body) def delete_flavor(self, flavor_id): diff --git a/tempest/tests/lib/services/compute/test_flavors_client.py b/tempest/tests/lib/services/compute/test_flavors_client.py index cbd17c688a..5325036946 100644 --- a/tempest/tests/lib/services/compute/test_flavors_client.py +++ b/tempest/tests/lib/services/compute/test_flavors_client.py @@ -17,6 +17,7 @@ import copy import fixtures from oslo_serialization import jsonutils as json +from tempest.api.compute import api_microversion_fixture from tempest.lib.services.compute import flavors_client from tempest.tests.lib import fake_auth_provider from tempest.tests.lib import fake_http @@ -39,6 +40,21 @@ class TestFlavorsClient(base.BaseServiceTest): "vcpus": 1 } + FAKE_FLAVOR_UPDATE = { + "disk": 1, + "id": "1", + "links": [{ + "href": "http://openstack.example.com/v2/openstack/flavors/1", + "rel": "self"}, { + "href": "http://openstack.example.com/openstack/flavors/1", + "rel": "bookmark"}], + "name": "m1.tiny", + "ram": 512, + "swap": 1, + "vcpus": 1, + "description": 'new' + } + EXTRA_SPECS = {"extra_specs": { "key1": "value1", "key2": "value2"} @@ -106,6 +122,25 @@ class TestFlavorsClient(base.BaseServiceTest): def test_create_flavor__byte_body(self): self._test_create_flavor(bytes_body=True) + def _test_update_flavor(self, bytes_body=False): + self.useFixture(api_microversion_fixture.APIMicroversionFixture( + '2.55')) + expected = {"flavor": TestFlavorsClient.FAKE_FLAVOR_UPDATE} + request = {"flavor": {"description": "updated description"}} + self.check_service_client_function( + self.client.update_flavor, + 'tempest.lib.common.rest_client.RestClient.put', + expected, + bytes_body, + flavor_id='8c7aae5a-d315-4216-875b-ed9b6a5bcfc6', + **request) + + def test_update_flavor_str_body(self): + self._test_update_flavor(bytes_body=False) + + def test_update_flavor__byte_body(self): + self._test_update_flavor(bytes_body=True) + def test_delete_flavor(self): self.check_service_client_function( self.client.delete_flavor,