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
This commit is contained in:
ghanshyam 2018-04-23 08:43:25 +00:00 committed by Ghanshyam Mann
parent 52d0c059ab
commit 52c5d28190
8 changed files with 230 additions and 5 deletions

View File

@ -342,6 +342,10 @@ Microversion tests implemented in Tempest
.. _2.48: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id43 .. _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`_
.. _2.60: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id54 .. _2.60: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id54

View File

@ -0,0 +1,4 @@
---
features:
- |
Add update flavor API to compute flavors_client library.

View File

@ -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']

View File

@ -86,7 +86,7 @@ unset_flavor_extra_specs = {
'status_code': [200] 'status_code': [200]
} }
create_get_flavor_details = { create_update_get_flavor_details = {
'status_code': [200], 'status_code': [200],
'response_body': { 'response_body': {
'type': 'object', 'type': 'object',

View File

@ -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']
}
}

View File

@ -21,12 +21,18 @@ from tempest.lib.api_schema.response.compute.v2_1 import flavors_access \
as schema_access as schema_access
from tempest.lib.api_schema.response.compute.v2_1 import flavors_extra_specs \ from tempest.lib.api_schema.response.compute.v2_1 import flavors_extra_specs \
as schema_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.common import rest_client
from tempest.lib.services.compute import base_compute_client from tempest.lib.services.compute import base_compute_client
class FlavorsClient(base_compute_client.BaseComputeClient): 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): def list_flavors(self, detail=False, **params):
"""Lists flavors. """Lists flavors.
@ -36,11 +42,12 @@ class FlavorsClient(base_compute_client.BaseComputeClient):
https://developer.openstack.org/api-ref/compute/#list-flavors-with-details https://developer.openstack.org/api-ref/compute/#list-flavors-with-details
""" """
url = 'flavors' url = 'flavors'
_schema = schema.list_flavors schema = self.get_schema(self.schema_versions_info)
if detail: if detail:
url += '/detail' url += '/detail'
_schema = schema.list_flavors_details _schema = schema.list_flavors_details
else:
_schema = schema.list_flavors
if params: if params:
url += '?%s' % urllib.urlencode(params) url += '?%s' % urllib.urlencode(params)
@ -58,7 +65,9 @@ class FlavorsClient(base_compute_client.BaseComputeClient):
""" """
resp, body = self.get("flavors/%s" % flavor_id) resp, body = self.get("flavors/%s" % flavor_id)
body = json.loads(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) return rest_client.ResponseBody(resp, body)
def create_flavor(self, **kwargs): def create_flavor(self, **kwargs):
@ -77,7 +86,25 @@ class FlavorsClient(base_compute_client.BaseComputeClient):
resp, body = self.post('flavors', post_body) resp, body = self.post('flavors', post_body)
body = json.loads(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) return rest_client.ResponseBody(resp, body)
def delete_flavor(self, flavor_id): def delete_flavor(self, flavor_id):

View File

@ -17,6 +17,7 @@ import copy
import fixtures import fixtures
from oslo_serialization import jsonutils as json 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.lib.services.compute import flavors_client
from tempest.tests.lib import fake_auth_provider from tempest.tests.lib import fake_auth_provider
from tempest.tests.lib import fake_http from tempest.tests.lib import fake_http
@ -39,6 +40,21 @@ class TestFlavorsClient(base.BaseServiceTest):
"vcpus": 1 "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": { EXTRA_SPECS = {"extra_specs": {
"key1": "value1", "key1": "value1",
"key2": "value2"} "key2": "value2"}
@ -106,6 +122,25 @@ class TestFlavorsClient(base.BaseServiceTest):
def test_create_flavor__byte_body(self): def test_create_flavor__byte_body(self):
self._test_create_flavor(bytes_body=True) 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): def test_delete_flavor(self):
self.check_service_client_function( self.check_service_client_function(
self.client.delete_flavor, self.client.delete_flavor,