Add microversion to allow setting flavor description

This adds the new microversion to allow providing
a description when creating a flavor, returning a
flavor description when showing flavor details, and
updating the description on an existing flavor.

Implements blueprint flavor-description

Change-Id: Ib16b0de82f9f9492f5cacf646dc3165a0849d75e
This commit is contained in:
Matt Riedemann 2017-10-25 16:59:31 -04:00
parent f93f10e7b6
commit 034d7f3795
39 changed files with 1253 additions and 52 deletions

View File

@ -42,14 +42,12 @@ Response
- flavors: flavors
- id: flavor_id_body
- name: flavor_name
- description: flavor_description_resp
- links: links
**Example List Flavors**
**Example List Flavors (v2.55)**
Showing all the default flavors of a Liberty era Nova installation
that was not customized by the site operators.
.. literalinclude:: ../../doc/api_samples/flavors/flavors-list-resp.json
.. literalinclude:: ../../doc/api_samples/flavors/v2.55/flavors-list-resp.json
:language: javascript
Create Flavor
@ -74,6 +72,7 @@ Request
- flavor: flavor
- name: flavor_name
- description: flavor_description
- id: flavor_id_body_create
- ram: flavor_ram
- disk: flavor_disk
@ -83,9 +82,9 @@ Request
- rxtx_factor: flavor_rxtx_factor_in
- os-flavor-access:is_public: flavor_is_public_in
**Example Create Flavor**
**Example Create Flavor (v2.55)**
.. literalinclude:: ../../doc/api_samples/flavor-manage/flavor-create-post-req.json
.. literalinclude:: ../../doc/api_samples/flavor-manage/v2.55/flavor-create-post-req.json
:language: javascript
Response
@ -95,6 +94,7 @@ Response
- flavor: flavor
- name: flavor_name
- description: flavor_description_resp
- id: flavor_id_body
- ram: flavor_ram
- disk: flavor_disk
@ -107,9 +107,9 @@ Response
- os-flavor-access:is_public: flavor_is_public
**Example Create Flavor**
**Example Create Flavor (v2.55)**
.. literalinclude:: ../../doc/api_samples/flavor-manage/flavor-create-post-resp.json
.. literalinclude:: ../../doc/api_samples/flavor-manage/v2.55/flavor-create-post-resp.json
:language: javascript
List Flavors With Details
@ -144,6 +144,7 @@ Response
- flavors: flavors
- name: flavor_name
- description: flavor_description_resp
- id: flavor_id_body
- ram: flavor_ram
- disk: flavor_disk
@ -155,9 +156,9 @@ Response
- rxtx_factor: flavor_rxtx_factor
- os-flavor-access:is_public: flavor_is_public
**Example List Flavors With Details**
**Example List Flavors With Details (v2.55)**
.. literalinclude:: ../../doc/api_samples/flavors/flavors-detail-resp.json
.. literalinclude:: ../../doc/api_samples/flavors/v2.55/flavors-detail-resp.json
:language: javascript
Show Flavor Details
@ -185,6 +186,7 @@ Response
- flavor: flavor
- name: flavor_name
- description: flavor_description_resp
- id: flavor_id_body
- ram: flavor_ram
- disk: flavor_disk
@ -196,9 +198,65 @@ Response
- rxtx_factor: flavor_rxtx_factor
- os-flavor-access:is_public: flavor_is_public
**Example Show Flavor Details**
**Example Show Flavor Details (v2.55)**
.. literalinclude:: ../../doc/api_samples/flavors/flavor-get-resp.json
.. literalinclude:: ../../doc/api_samples/flavors/v2.55/flavor-get-resp.json
:language: javascript
Update Flavor Description
=========================
.. rest_method:: PUT /flavors/{flavor_id}
Updates a flavor description.
This API is available starting with microversion 2.55.
Policy defaults enable only users with the administrative role to
perform this operation. Cloud providers can change these permissions
through the ``policy.json`` file.
Normal response codes: 200
Error response codes: badRequest(400), unauthorized(401), forbidden(403), itemNotFound(404)
Request
-------
.. rest_parameters:: parameters.yaml
- flavor_id: flavor_id
- flavor: flavor
- description: flavor_description_required
**Example Update Flavor Description (v2.55)**
.. literalinclude:: ../../doc/api_samples/flavor-manage/v2.55/flavor-update-req.json
:language: javascript
Response
--------
.. rest_parameters:: parameters.yaml
- flavor: flavor
- name: flavor_name
- description: flavor_description_resp
- id: flavor_id_body
- ram: flavor_ram
- disk: flavor_disk
- vcpus: flavor_cpus
- links: links
- OS-FLV-EXT-DATA:ephemeral: flavor_ephem_disk
- OS-FLV-DISABLED:disabled: flavor_disabled
- swap: flavor_swap
- rxtx_factor: flavor_rxtx_factor
- os-flavor-access:is_public: flavor_is_public
**Example Update Flavor Description (v2.55)**
.. literalinclude:: ../../doc/api_samples/flavor-manage/v2.55/flavor-update-resp.json
:language: javascript
Delete Flavor

View File

@ -2365,6 +2365,29 @@ flavor_cpus_2_47:
type: integer
description: |
The number of virtual CPUs that were allocated to the server.
flavor_description:
type: string
in: body
required: false
min_version: 2.55
description: |
A free form description of the flavor. Limited to 65535 characters
in length. Only printable characters are allowed.
flavor_description_required:
type: string
in: body
required: true
min_version: 2.55
description: |
A free form description of the flavor. Limited to 65535 characters
in length. Only printable characters are allowed.
flavor_description_resp:
description: |
The description of the flavor.
in: body
required: true
type: string
min_version: 2.55
flavor_disabled:
in: body
required: false

View File

@ -0,0 +1,11 @@
{
"flavor": {
"name": "test_flavor",
"ram": 1024,
"vcpus": 2,
"disk": 10,
"id": "10",
"rxtx_factor": 2.0,
"description": "test description"
}
}

View File

@ -0,0 +1,25 @@
{
"flavor": {
"OS-FLV-DISABLED:disabled": false,
"disk": 10,
"OS-FLV-EXT-DATA:ephemeral": 0,
"os-flavor-access:is_public": true,
"id": "10",
"links": [
{
"href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/flavors/10",
"rel": "self"
},
{
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/10",
"rel": "bookmark"
}
],
"name": "test_flavor",
"ram": 1024,
"swap": "",
"rxtx_factor": 2.0,
"vcpus": 2,
"description": "test description"
}
}

View File

@ -0,0 +1,5 @@
{
"flavor": {
"description": "updated description"
}
}

View File

@ -0,0 +1,25 @@
{
"flavor": {
"OS-FLV-DISABLED:disabled": false,
"disk": 1,
"OS-FLV-EXT-DATA:ephemeral": 0,
"os-flavor-access:is_public": true,
"id": "1",
"links": [
{
"href": "http://openstack.example.com/v2.1/6f70656e737461636b20342065766572/flavors/1",
"rel": "self"
},
{
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/1",
"rel": "bookmark"
}
],
"name": "m1.tiny",
"ram": 512,
"swap": "",
"vcpus": 1,
"rxtx_factor": 1.0,
"description": "updated description"
}
}

View File

@ -0,0 +1,25 @@
{
"flavor": {
"OS-FLV-DISABLED:disabled": false,
"disk": 20,
"OS-FLV-EXT-DATA:ephemeral": 0,
"os-flavor-access:is_public": true,
"id": "7",
"links": [
{
"href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/flavors/7",
"rel": "self"
},
{
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/7",
"rel": "bookmark"
}
],
"name": "m1.small.description",
"ram": 2048,
"swap": "",
"vcpus": 1,
"rxtx_factor": 1.0,
"description": "test description"
}
}

View File

@ -0,0 +1,165 @@
{
"flavors": [
{
"OS-FLV-DISABLED:disabled": false,
"disk": 1,
"OS-FLV-EXT-DATA:ephemeral": 0,
"os-flavor-access:is_public": true,
"id": "1",
"links": [
{
"href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/flavors/1",
"rel": "self"
},
{
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/1",
"rel": "bookmark"
}
],
"name": "m1.tiny",
"ram": 512,
"swap": "",
"vcpus": 1,
"rxtx_factor": 1.0,
"description": null
},
{
"OS-FLV-DISABLED:disabled": false,
"disk": 20,
"OS-FLV-EXT-DATA:ephemeral": 0,
"os-flavor-access:is_public": true,
"id": "2",
"links": [
{
"href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/flavors/2",
"rel": "self"
},
{
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/2",
"rel": "bookmark"
}
],
"name": "m1.small",
"ram": 2048,
"swap": "",
"vcpus": 1,
"rxtx_factor": 1.0,
"description": null
},
{
"OS-FLV-DISABLED:disabled": false,
"disk": 40,
"OS-FLV-EXT-DATA:ephemeral": 0,
"os-flavor-access:is_public": true,
"id": "3",
"links": [
{
"href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/flavors/3",
"rel": "self"
},
{
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/3",
"rel": "bookmark"
}
],
"name": "m1.medium",
"ram": 4096,
"swap": "",
"vcpus": 2,
"rxtx_factor": 1.0,
"description": null
},
{
"OS-FLV-DISABLED:disabled": false,
"disk": 80,
"OS-FLV-EXT-DATA:ephemeral": 0,
"os-flavor-access:is_public": true,
"id": "4",
"links": [
{
"href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/flavors/4",
"rel": "self"
},
{
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/4",
"rel": "bookmark"
}
],
"name": "m1.large",
"ram": 8192,
"swap": "",
"vcpus": 4,
"rxtx_factor": 1.0,
"description": null
},
{
"OS-FLV-DISABLED:disabled": false,
"disk": 160,
"OS-FLV-EXT-DATA:ephemeral": 0,
"os-flavor-access:is_public": true,
"id": "5",
"links": [
{
"href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/flavors/5",
"rel": "self"
},
{
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/5",
"rel": "bookmark"
}
],
"name": "m1.xlarge",
"ram": 16384,
"swap": "",
"vcpus": 8,
"rxtx_factor": 1.0,
"description": null
},
{
"OS-FLV-DISABLED:disabled": false,
"disk": 1,
"OS-FLV-EXT-DATA:ephemeral": 0,
"os-flavor-access:is_public": true,
"id": "6",
"links": [
{
"href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/flavors/6",
"rel": "self"
},
{
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/6",
"rel": "bookmark"
}
],
"name": "m1.tiny.specs",
"ram": 512,
"swap": "",
"vcpus": 1,
"rxtx_factor": 1.0,
"description": null
},
{
"OS-FLV-DISABLED:disabled": false,
"disk": 20,
"OS-FLV-EXT-DATA:ephemeral": 0,
"os-flavor-access:is_public": true,
"id": "7",
"links": [
{
"href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/flavors/7",
"rel": "self"
},
{
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/7",
"rel": "bookmark"
}
],
"name": "m1.small.description",
"ram": 2048,
"swap": "",
"vcpus": 1,
"rxtx_factor": 1.0,
"description": "test description"
}
]
}

View File

@ -0,0 +1,109 @@
{
"flavors": [
{
"id": "1",
"links": [
{
"href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/flavors/1",
"rel": "self"
},
{
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/1",
"rel": "bookmark"
}
],
"name": "m1.tiny",
"description": null
},
{
"id": "2",
"links": [
{
"href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/flavors/2",
"rel": "self"
},
{
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/2",
"rel": "bookmark"
}
],
"name": "m1.small",
"description": null
},
{
"id": "3",
"links": [
{
"href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/flavors/3",
"rel": "self"
},
{
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/3",
"rel": "bookmark"
}
],
"name": "m1.medium",
"description": null
},
{
"id": "4",
"links": [
{
"href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/flavors/4",
"rel": "self"
},
{
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/4",
"rel": "bookmark"
}
],
"name": "m1.large",
"description": null
},
{
"id": "5",
"links": [
{
"href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/flavors/5",
"rel": "self"
},
{
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/5",
"rel": "bookmark"
}
],
"name": "m1.xlarge",
"description": null
},
{
"id": "6",
"links": [
{
"href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/flavors/6",
"rel": "self"
},
{
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/6",
"rel": "bookmark"
}
],
"name": "m1.tiny.specs",
"description": null
},
{
"id": "7",
"links": [
{
"href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/flavors/7",
"rel": "self"
},
{
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/7",
"rel": "bookmark"
}
],
"name": "m1.small.description",
"description": "test description"
}
]
}

View File

@ -19,7 +19,7 @@
}
],
"status": "CURRENT",
"version": "2.54",
"version": "2.55",
"min_version": "2.1",
"updated": "2013-07-23T11:33:21Z"
}

View File

@ -22,7 +22,7 @@
}
],
"status": "CURRENT",
"version": "2.54",
"version": "2.55",
"min_version": "2.1",
"updated": "2013-07-23T11:33:21Z"
}

View File

@ -129,6 +129,7 @@ REST_API_VERSION_HISTORY = """REST API Version History:
id field, and takes a uuid in requests. PUT and GET requests
and responses are also changed.
* 2.54 - Enable reset key pair while rebuilding instance.
* 2.55 - Added flavor.description to GET/POST/PUT flavors APIs.
"""
# The minimum and maximum versions of the API supported
@ -137,7 +138,7 @@ REST_API_VERSION_HISTORY = """REST API Version History:
# Note(cyeoh): This only applies for the v2.1 API once microversions
# support is fully merged. It does not affect the V2 API.
_MIN_API_VERSION = "2.1"
_MAX_API_VERSION = "2.54"
_MAX_API_VERSION = "2.55"
DEFAULT_API_VERSION = _MIN_API_VERSION
# Almost all proxy APIs which are related to network, images and baremetal

View File

@ -87,6 +87,13 @@ class FlavorActionController(wsgi.Controller):
self._extend_flavor(resp_obj.obj['flavor'], db_flavor)
@wsgi.extends(action='update')
def update(self, req, id, body, resp_obj):
context = req.environ['nova.context']
if context.can(fa_policies.BASE_POLICY_NAME, fatal=False):
db_flavor = req.get_db_flavor(resp_obj.obj['flavor']['id'])
self._extend_flavor(resp_obj.obj['flavor'], db_flavor)
@extensions.expected_errors((400, 403, 404, 409))
@wsgi.action("addTenantAccess")
@validation.schema(flavor_access.add_tenant_access)

View File

@ -14,6 +14,7 @@ import webob
from oslo_log import log as logging
from nova.api.openstack import api_version_request
from nova.api.openstack.compute.schemas import flavor_manage
from nova.api.openstack.compute.views import flavors as flavors_view
from nova.api.openstack import extensions
@ -67,7 +68,9 @@ class FlavorManageController(wsgi.Controller):
@wsgi.action("create")
@extensions.expected_errors((400, 409))
@validation.schema(flavor_manage.create_v20, '2.0', '2.0')
@validation.schema(flavor_manage.create, '2.1')
@validation.schema(flavor_manage.create, '2.1', '2.54')
@validation.schema(flavor_manage.create_v2_55,
flavors_view.FLAVOR_DESCRIPTION_MICROVERSION)
def _create(self, req, body):
context = req.environ['nova.context']
# TODO(rb560u): remove this check in future release
@ -92,12 +95,18 @@ class FlavorManageController(wsgi.Controller):
rxtx_factor = vals.get('rxtx_factor', 1.0)
is_public = vals.get('os-flavor-access:is_public', True)
# The user can specify a description starting with microversion 2.55.
include_description = api_version_request.is_supported(
req, flavors_view.FLAVOR_DESCRIPTION_MICROVERSION)
description = vals.get('description') if include_description else None
try:
flavor = flavors.create(name, memory, vcpus, root_gb,
ephemeral_gb=ephemeral_gb,
flavorid=flavorid, swap=swap,
rxtx_factor=rxtx_factor,
is_public=is_public)
is_public=is_public,
description=description)
# NOTE(gmann): For backward compatibility, non public flavor
# access is not being added for created tenant. Ref -bug/1209101
req.cache_db_flavor(flavor)
@ -105,4 +114,27 @@ class FlavorManageController(wsgi.Controller):
exception.FlavorIdExists) as err:
raise webob.exc.HTTPConflict(explanation=err.format_message())
return self._view_builder.show(req, flavor)
return self._view_builder.show(req, flavor, include_description)
@wsgi.Controller.api_version(flavors_view.FLAVOR_DESCRIPTION_MICROVERSION)
@wsgi.action('update')
@extensions.expected_errors((400, 404))
@validation.schema(flavor_manage.update_v2_55,
flavors_view.FLAVOR_DESCRIPTION_MICROVERSION)
def _update(self, req, id, body):
# Validate the policy.
context = req.environ['nova.context']
context.can(fm_policies.POLICY_ROOT % 'update')
# Get the flavor and update the description.
try:
flavor = objects.Flavor.get_by_flavor_id(context, id)
flavor.description = body['flavor']['description']
flavor.save()
except exception.FlavorNotFound as e:
raise webob.exc.HTTPNotFound(explanation=e.format_message())
# Cache the flavor so the flavor_access and flavor_rxtx extensions
# can add stuff to the response.
req.cache_db_flavor(flavor)
return self._view_builder.show(req, flavor, include_description=True)

View File

@ -42,6 +42,10 @@ class FlavorRxtxController(wsgi.Controller):
def create(self, req, resp_obj, body):
return self._show(req, resp_obj)
@wsgi.extends(action='update')
def update(self, req, id, body, resp_obj):
return self._show(req, resp_obj)
@wsgi.extends
def detail(self, req, resp_obj):
context = req.environ['nova.context']

View File

@ -16,6 +16,7 @@
from oslo_utils import strutils
import webob
from nova.api.openstack import api_version_request
from nova.api.openstack import common
from nova.api.openstack.compute.views import flavors as flavors_view
from nova.api.openstack import extensions
@ -57,7 +58,9 @@ class FlavorsController(wsgi.Controller):
except exception.FlavorNotFound as e:
raise webob.exc.HTTPNotFound(explanation=e.format_message())
return self._view_builder.show(req, flavor)
include_description = api_version_request.is_supported(
req, flavors_view.FLAVOR_DESCRIPTION_MICROVERSION)
return self._view_builder.show(req, flavor, include_description)
def _parse_is_public(self, is_public):
"""Parse is_public into something usable."""

View File

@ -688,3 +688,16 @@ uniqueness across cells. This microversion brings the following changes:
----
Allow the user to set the server key pair while rebuilding.
2.55
----
Adds a ``description`` field to the flavor resource in the following APIs:
* ``GET /flavors``
* ``GET /flavors/detail``
* ``GET /flavors/{flavor_id}``
* ``POST /flavors``
* ``PUT /flavors/{flavor_id}``
The embedded flavor description will not be included in server representations.

View File

@ -429,6 +429,7 @@ ROUTE_LIST = (
}),
('/flavors/{id}', {
'GET': [flavor_controller, 'show'],
'PUT': [flavor_controller, 'update'],
'DELETE': [flavor_controller, 'delete']
}),
('/flavors/{id}/action', {

View File

@ -65,3 +65,37 @@ create = {
create_v20 = copy.deepcopy(create)
create_v20['properties']['flavor']['properties']['name'] = (parameter_types.
name_with_leading_trailing_spaces)
# 2.55 adds an optional description field with a max length of 65535 since the
# backing database column is a TEXT column which is 64KiB.
flavor_description = {
'type': ['string', 'null'], 'minLength': 0, 'maxLength': 65535,
'pattern': parameter_types.valid_description_regex,
}
create_v2_55 = copy.deepcopy(create)
create_v2_55['properties']['flavor']['properties']['description'] = (
flavor_description)
update_v2_55 = {
'type': 'object',
'properties': {
'flavor': {
'type': 'object',
'properties': {
'description': flavor_description
},
# Since the only property that can be specified on update is the
# description field, it is required. If we allow updating other
# flavor attributes in a later microversion, we should reconsider
# what is required.
'required': ['description'],
'additionalProperties': False,
},
},
'required': ['flavor'],
'additionalProperties': False,
}

View File

@ -13,15 +13,18 @@
# License for the specific language governing permissions and limitations
# under the License.
from nova.api.openstack import api_version_request
from nova.api.openstack import common
FLAVOR_DESCRIPTION_MICROVERSION = '2.55'
class ViewBuilder(common.ViewBuilder):
_collection_name = "flavors"
def basic(self, request, flavor):
return {
def basic(self, request, flavor, include_description=False):
flavor_dict = {
"flavor": {
"id": flavor["flavorid"],
"name": flavor["name"],
@ -31,7 +34,12 @@ class ViewBuilder(common.ViewBuilder):
},
}
def show(self, request, flavor):
if include_description:
flavor_dict['flavor']['description'] = flavor.description
return flavor_dict
def show(self, request, flavor, include_description=False):
flavor_dict = {
"flavor": {
"id": flavor["flavorid"],
@ -48,19 +56,29 @@ class ViewBuilder(common.ViewBuilder):
},
}
if include_description:
flavor_dict['flavor']['description'] = flavor.description
return flavor_dict
def index(self, request, flavors):
"""Return the 'index' view of flavors."""
coll_name = self._collection_name
return self._list_view(self.basic, request, flavors, coll_name)
include_description = api_version_request.is_supported(
request, FLAVOR_DESCRIPTION_MICROVERSION)
return self._list_view(self.basic, request, flavors, coll_name,
include_description=include_description)
def detail(self, request, flavors):
"""Return the 'detail' view of flavors."""
coll_name = self._collection_name + '/detail'
return self._list_view(self.show, request, flavors, coll_name)
include_description = api_version_request.is_supported(
request, FLAVOR_DESCRIPTION_MICROVERSION)
return self._list_view(self.show, request, flavors, coll_name,
include_description=include_description)
def _list_view(self, func, request, flavors, coll_name):
def _list_view(self, func, request, flavors, coll_name,
include_description=False):
"""Provide a view for a list of flavors.
:param func: Function used to format the flavor data
@ -68,10 +86,13 @@ class ViewBuilder(common.ViewBuilder):
:param flavors: List of flavors in dictionary format
:param coll_name: Name of collection, used to generate the next link
for a pagination query
:param include_description: If the flavor.description should be
included in the response dict.
:returns: Flavor reply data in dictionary format
"""
flavor_list = [func(request, flavor)["flavor"] for flavor in flavors]
flavor_list = [func(request, flavor, include_description)["flavor"]
for flavor in flavors]
flavors_links = self._get_collection_links(request,
flavors,
coll_name,

View File

@ -69,7 +69,7 @@ system_metadata_flavor_extra_props = [
def create(name, memory, vcpus, root_gb, ephemeral_gb=0, flavorid=None,
swap=0, rxtx_factor=1.0, is_public=True):
swap=0, rxtx_factor=1.0, is_public=True, description=None):
"""Creates flavors."""
if not flavorid:
flavorid = uuidutils.generate_uuid()
@ -81,6 +81,7 @@ def create(name, memory, vcpus, root_gb, ephemeral_gb=0, flavorid=None,
'ephemeral_gb': ephemeral_gb,
'swap': swap,
'rxtx_factor': rxtx_factor,
'description': description
}
if isinstance(name, six.string_types):

View File

@ -71,6 +71,10 @@ to a flavor via an os-flavor-access API.
'method': 'POST',
'path': '/flavors'
},
{
'method': 'PUT',
'path': '/flavors/{flavor_id}'
},
]),
]

View File

@ -52,6 +52,16 @@ flavor_manage_policies = [
'path': '/flavors'
}
]),
policy.DocumentedRuleDefault(
POLICY_ROOT % 'update',
base.RULE_ADMIN_API,
"Update a flavor",
[
{
'method': 'PUT',
'path': '/flavors/{flavor_id}'
}
]),
policy.DocumentedRuleDefault(
POLICY_ROOT % 'delete',
BASE_POLICY_RULE,

View File

@ -40,6 +40,10 @@ flavor_rxtx_policies = [
'method': 'POST',
'path': '/flavors'
},
{
'method': 'PUT',
'path': '/flavors/{flavor_id}'
},
]),
]

View File

@ -0,0 +1,11 @@
{
"flavor": {
"name": "%(flavor_name)s",
"ram": 1024,
"vcpus": 2,
"disk": 10,
"id": "%(flavor_id)s",
"rxtx_factor": 2.0,
"description": "test description"
}
}

View File

@ -0,0 +1,25 @@
{
"flavor": {
"disk": 10,
"id": "%(flavor_id)s",
"links": [
{
"href": "%(versioned_compute_endpoint)s/flavors/%(flavor_id)s",
"rel": "self"
},
{
"href": "%(compute_endpoint)s/flavors/%(flavor_id)s",
"rel": "bookmark"
}
],
"name": "%(flavor_name)s",
"os-flavor-access:is_public": true,
"ram": 1024,
"vcpus": 2,
"OS-FLV-DISABLED:disabled": false,
"OS-FLV-EXT-DATA:ephemeral": 0,
"swap": "",
"rxtx_factor": 2.0,
"description": "test description"
}
}

View File

@ -0,0 +1,5 @@
{
"flavor": {
"description": "updated description"
}
}

View File

@ -0,0 +1,25 @@
{
"flavor": {
"OS-FLV-DISABLED:disabled": false,
"disk": 1,
"OS-FLV-EXT-DATA:ephemeral": 0,
"os-flavor-access:is_public": true,
"id": "1",
"links": [
{
"href": "http://openstack.example.com/v2.1/6f70656e737461636b20342065766572/flavors/1",
"rel": "self"
},
{
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/1",
"rel": "bookmark"
}
],
"name": "m1.tiny",
"ram": 512,
"swap": "",
"vcpus": 1,
"rxtx_factor": 1.0,
"description": "updated description"
}
}

View File

@ -0,0 +1,25 @@
{
"flavor": {
"OS-FLV-DISABLED:disabled": false,
"disk": 20,
"OS-FLV-EXT-DATA:ephemeral": 0,
"id": "%(flavorid)s",
"links": [
{
"href": "%(versioned_compute_endpoint)s/flavors/%(flavorid)s",
"rel": "self"
},
{
"href": "%(compute_endpoint)s/flavors/%(flavorid)s",
"rel": "bookmark"
}
],
"name": "m1.small.description",
"os-flavor-access:is_public": true,
"ram": 2048,
"swap": "",
"vcpus": 1,
"rxtx_factor": 1.0,
"description": "test description"
}
}

View File

@ -0,0 +1,165 @@
{
"flavors": [
{
"OS-FLV-DISABLED:disabled": false,
"disk": 1,
"OS-FLV-EXT-DATA:ephemeral": 0,
"id": "1",
"links": [
{
"href": "%(versioned_compute_endpoint)s/flavors/1",
"rel": "self"
},
{
"href": "%(compute_endpoint)s/flavors/1",
"rel": "bookmark"
}
],
"name": "m1.tiny",
"os-flavor-access:is_public": true,
"ram": 512,
"swap": "",
"vcpus": 1,
"rxtx_factor": 1.0,
"description": null
},
{
"OS-FLV-DISABLED:disabled": false,
"disk": 20,
"OS-FLV-EXT-DATA:ephemeral": 0,
"id": "2",
"links": [
{
"href": "%(versioned_compute_endpoint)s/flavors/2",
"rel": "self"
},
{
"href": "%(compute_endpoint)s/flavors/2",
"rel": "bookmark"
}
],
"name": "m1.small",
"os-flavor-access:is_public": true,
"ram": 2048,
"swap": "",
"vcpus": 1,
"rxtx_factor": 1.0,
"description": null
},
{
"OS-FLV-DISABLED:disabled": false,
"disk": 40,
"OS-FLV-EXT-DATA:ephemeral": 0,
"id": "3",
"links": [
{
"href": "%(versioned_compute_endpoint)s/flavors/3",
"rel": "self"
},
{
"href": "%(compute_endpoint)s/flavors/3",
"rel": "bookmark"
}
],
"name": "m1.medium",
"os-flavor-access:is_public": true,
"ram": 4096,
"swap": "",
"vcpus": 2,
"rxtx_factor": 1.0,
"description": null
},
{
"OS-FLV-DISABLED:disabled": false,
"disk": 80,
"OS-FLV-EXT-DATA:ephemeral": 0,
"id": "4",
"links": [
{
"href": "%(versioned_compute_endpoint)s/flavors/4",
"rel": "self"
},
{
"href": "%(compute_endpoint)s/flavors/4",
"rel": "bookmark"
}
],
"name": "m1.large",
"os-flavor-access:is_public": true,
"ram": 8192,
"swap": "",
"vcpus": 4,
"rxtx_factor": 1.0,
"description": null
},
{
"OS-FLV-DISABLED:disabled": false,
"disk": 160,
"OS-FLV-EXT-DATA:ephemeral": 0,
"id": "5",
"links": [
{
"href": "%(versioned_compute_endpoint)s/flavors/5",
"rel": "self"
},
{
"href": "%(compute_endpoint)s/flavors/5",
"rel": "bookmark"
}
],
"name": "m1.xlarge",
"os-flavor-access:is_public": true,
"ram": 16384,
"swap": "",
"vcpus": 8,
"rxtx_factor": 1.0,
"description": null
},
{
"OS-FLV-DISABLED:disabled": false,
"disk": 1,
"OS-FLV-EXT-DATA:ephemeral": 0,
"id": "6",
"links": [
{
"href": "%(versioned_compute_endpoint)s/flavors/6",
"rel": "self"
},
{
"href": "%(compute_endpoint)s/flavors/6",
"rel": "bookmark"
}
],
"name": "m1.tiny.specs",
"os-flavor-access:is_public": true,
"ram": 512,
"swap": "",
"vcpus": 1,
"rxtx_factor": 1.0,
"description": null
},
{
"OS-FLV-DISABLED:disabled": false,
"disk": 20,
"OS-FLV-EXT-DATA:ephemeral": 0,
"id": "%(flavorid)s",
"links": [
{
"href": "%(versioned_compute_endpoint)s/flavors/%(flavorid)s",
"rel": "self"
},
{
"href": "%(compute_endpoint)s/flavors/%(flavorid)s",
"rel": "bookmark"
}
],
"name": "m1.small.description",
"os-flavor-access:is_public": true,
"ram": 2048,
"swap": "",
"vcpus": 1,
"rxtx_factor": 1.0,
"description": "test description"
}
]
}

View File

@ -0,0 +1,109 @@
{
"flavors": [
{
"id": "1",
"links": [
{
"href": "%(versioned_compute_endpoint)s/flavors/1",
"rel": "self"
},
{
"href": "%(compute_endpoint)s/flavors/1",
"rel": "bookmark"
}
],
"name": "m1.tiny",
"description": null
},
{
"id": "2",
"links": [
{
"href": "%(versioned_compute_endpoint)s/flavors/2",
"rel": "self"
},
{
"href": "%(compute_endpoint)s/flavors/2",
"rel": "bookmark"
}
],
"name": "m1.small",
"description": null
},
{
"id": "3",
"links": [
{
"href": "%(versioned_compute_endpoint)s/flavors/3",
"rel": "self"
},
{
"href": "%(compute_endpoint)s/flavors/3",
"rel": "bookmark"
}
],
"name": "m1.medium",
"description": null
},
{
"id": "4",
"links": [
{
"href": "%(versioned_compute_endpoint)s/flavors/4",
"rel": "self"
},
{
"href": "%(compute_endpoint)s/flavors/4",
"rel": "bookmark"
}
],
"name": "m1.large",
"description": null
},
{
"id": "5",
"links": [
{
"href": "%(versioned_compute_endpoint)s/flavors/5",
"rel": "self"
},
{
"href": "%(compute_endpoint)s/flavors/5",
"rel": "bookmark"
}
],
"name": "m1.xlarge",
"description": null
},
{
"id": "6",
"links": [
{
"href": "%(versioned_compute_endpoint)s/flavors/6",
"rel": "self"
},
{
"href": "%(compute_endpoint)s/flavors/6",
"rel": "bookmark"
}
],
"name": "m1.tiny.specs",
"description": null
},
{
"id": "%(flavorid)s",
"links": [
{
"href": "%(versioned_compute_endpoint)s/flavors/%(flavorid)s",
"rel": "self"
},
{
"href": "%(compute_endpoint)s/flavors/%(flavorid)s",
"rel": "bookmark"
}
],
"name": "m1.small.description",
"description": "test description"
}
]
}

View File

@ -37,3 +37,12 @@ class FlavorManageSampleJsonTests(api_sample_base.ApiSampleTestBaseV21):
response = self._do_delete("flavors/10")
self.assertEqual(202, response.status_code)
self.assertEqual('', response.text)
class FlavorManageSampleJsonTests2_55(FlavorManageSampleJsonTests):
microversion = '2.55'
scenarios = [('v2_55', {'api_major_version': 'v2.1'})]
def test_update_flavor_description(self):
response = self._do_put("flavors/1", "flavor-update-req", {})
self._verify_response("flavor-update-resp", {}, response, 200)

View File

@ -13,20 +13,47 @@
# License for the specific language governing permissions and limitations
# under the License.
from nova import context as nova_context
from nova import objects
from nova.tests.functional.api_sample_tests import api_sample_base
class FlavorsSampleJsonTest(api_sample_base.ApiSampleTestBaseV21):
sample_dir = 'flavors'
flavor_show_id = '1'
subs = {}
def test_flavors_get(self):
response = self._do_get('flavors/1')
self._verify_response('flavor-get-resp', {}, response, 200)
response = self._do_get('flavors/%s' % self.flavor_show_id)
self._verify_response('flavor-get-resp', self.subs, response, 200)
def test_flavors_list(self):
response = self._do_get('flavors')