Clean up non-spec output in flavor extensions
Adds option to cache flavors in the request object like instances. Modifies the flavorextradata extension to use the cache and optimizes tests. Adds flavor_disabled extension to control the extra data and includes tests. Fixes api samples to show the new extension. Fixes bug 1043585 Change-Id: Ie89df24a2891e3869d3fb604e07c79e8c913f290
This commit is contained in:
@@ -36,6 +36,7 @@
|
||||
"compute_extension:extended_server_attributes": [["rule:admin_api"]],
|
||||
"compute_extension:extended_status": [],
|
||||
"compute_extension:flavor_access": [],
|
||||
"compute_extension:flavor_disabled": [],
|
||||
"compute_extension:flavorextradata": [],
|
||||
"compute_extension:flavorextraspecs": [],
|
||||
"compute_extension:flavormanage": [["rule:admin_api"]],
|
||||
|
||||
89
nova/api/openstack/compute/contrib/flavor_disabled.py
Normal file
89
nova/api/openstack/compute/contrib/flavor_disabled.py
Normal file
@@ -0,0 +1,89 @@
|
||||
# Copyright 2012 Nebula, Inc.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""The Flavor Disabled API extension."""
|
||||
|
||||
from nova.api.openstack import extensions
|
||||
from nova.api.openstack import wsgi
|
||||
from nova.api.openstack import xmlutil
|
||||
|
||||
|
||||
authorize = extensions.soft_extension_authorizer('compute', 'flavor_disabled')
|
||||
|
||||
|
||||
class FlavorDisabledController(wsgi.Controller):
|
||||
def _extend_flavors(self, req, flavors):
|
||||
for flavor in flavors:
|
||||
db_flavor = req.get_db_flavor(flavor['id'])
|
||||
key = "%s:disabled" % Flavor_disabled.alias
|
||||
flavor[key] = db_flavor['disabled']
|
||||
|
||||
def _show(self, req, resp_obj):
|
||||
if not authorize(req.environ['nova.context']):
|
||||
return
|
||||
if 'flavor' in resp_obj.obj:
|
||||
resp_obj.attach(xml=FlavorDisabledTemplate())
|
||||
self._extend_flavors(req, [resp_obj.obj['flavor']])
|
||||
|
||||
@wsgi.extends
|
||||
def show(self, req, resp_obj, id):
|
||||
return self._show(req, resp_obj)
|
||||
|
||||
@wsgi.extends(action='create')
|
||||
def create(self, req, resp_obj, body):
|
||||
return self._show(req, resp_obj)
|
||||
|
||||
@wsgi.extends
|
||||
def detail(self, req, resp_obj):
|
||||
if not authorize(req.environ['nova.context']):
|
||||
return
|
||||
resp_obj.attach(xml=FlavorsDisabledTemplate())
|
||||
self._extend_flavors(req, list(resp_obj.obj['flavors']))
|
||||
|
||||
|
||||
class Flavor_disabled(extensions.ExtensionDescriptor):
|
||||
"""Support to show the disabled status of a flavor"""
|
||||
|
||||
name = "FlavorDisabled"
|
||||
alias = "OS-FLV-DISABLED"
|
||||
namespace = ("http://docs.openstack.org/compute/ext/"
|
||||
"flavor_disabled/api/v1.1")
|
||||
updated = "2012-08-29T00:00:00+00:00"
|
||||
|
||||
def get_controller_extensions(self):
|
||||
controller = FlavorDisabledController()
|
||||
extension = extensions.ControllerExtension(self, 'flavors', controller)
|
||||
return [extension]
|
||||
|
||||
|
||||
def make_flavor(elem):
|
||||
elem.set('{%s}disabled' % Flavor_disabled.namespace,
|
||||
'%s:disabled' % Flavor_disabled.alias)
|
||||
|
||||
|
||||
class FlavorDisabledTemplate(xmlutil.TemplateBuilder):
|
||||
def construct(self):
|
||||
root = xmlutil.TemplateElement('flavor', selector='flavor')
|
||||
make_flavor(root)
|
||||
return xmlutil.SlaveTemplate(root, 1, nsmap={
|
||||
Flavor_disabled.alias: Flavor_disabled.namespace})
|
||||
|
||||
|
||||
class FlavorsDisabledTemplate(xmlutil.TemplateBuilder):
|
||||
def construct(self):
|
||||
root = xmlutil.TemplateElement('flavors')
|
||||
elem = xmlutil.SubTemplateElement(root, 'flavor', selector='flavors')
|
||||
make_flavor(elem)
|
||||
return xmlutil.SlaveTemplate(root, 1, nsmap={
|
||||
Flavor_disabled.alias: Flavor_disabled.namespace})
|
||||
@@ -27,71 +27,39 @@ attributes. This extension adds to that list:
|
||||
from nova.api.openstack import extensions
|
||||
from nova.api.openstack import wsgi
|
||||
from nova.api.openstack import xmlutil
|
||||
from nova.compute import instance_types
|
||||
from nova import exception
|
||||
|
||||
|
||||
authorize = extensions.soft_extension_authorizer('compute', 'flavorextradata')
|
||||
|
||||
|
||||
class FlavorextradataController(wsgi.Controller):
|
||||
def _get_flavor_refs(self, context):
|
||||
"""Return a dictionary mapping flavorid to flavor_ref."""
|
||||
def _extend_flavors(self, req, flavors):
|
||||
for flavor in flavors:
|
||||
db_flavor = req.get_db_flavor(flavor['id'])
|
||||
key = "%s:ephemeral" % Flavorextradata.alias
|
||||
flavor[key] = db_flavor['ephemeral_gb']
|
||||
|
||||
flavor_refs = instance_types.get_all_types(context)
|
||||
rval = {}
|
||||
for name, obj in flavor_refs.iteritems():
|
||||
rval[obj['flavorid']] = obj
|
||||
return rval
|
||||
|
||||
def _extend_flavor(self, flavor_rval, flavor_ref):
|
||||
key = "%s:ephemeral" % (Flavorextradata.alias)
|
||||
flavor_rval[key] = flavor_ref['ephemeral_gb']
|
||||
def _show(self, req, resp_obj):
|
||||
if not authorize(req.environ['nova.context']):
|
||||
return
|
||||
if 'flavor' in resp_obj.obj:
|
||||
resp_obj.attach(xml=FlavorextradatumTemplate())
|
||||
self._extend_flavors(req, [resp_obj.obj['flavor']])
|
||||
|
||||
@wsgi.extends
|
||||
def show(self, req, resp_obj, id):
|
||||
context = req.environ['nova.context']
|
||||
if authorize(context):
|
||||
# Attach our slave template to the response object
|
||||
resp_obj.attach(xml=FlavorextradatumTemplate())
|
||||
return self._show(req, resp_obj)
|
||||
|
||||
try:
|
||||
flavor_ref = instance_types.get_instance_type_by_flavor_id(id)
|
||||
except exception.FlavorNotFound:
|
||||
explanation = _("Flavor not found.")
|
||||
raise exception.HTTPNotFound(explanation=explanation)
|
||||
|
||||
self._extend_flavor(resp_obj.obj['flavor'], flavor_ref)
|
||||
@wsgi.extends(action='create')
|
||||
def create(self, req, resp_obj, body):
|
||||
return self._show(req, resp_obj)
|
||||
|
||||
@wsgi.extends
|
||||
def detail(self, req, resp_obj):
|
||||
context = req.environ['nova.context']
|
||||
if authorize(context):
|
||||
# Attach our slave template to the response object
|
||||
resp_obj.attach(xml=FlavorextradataTemplate())
|
||||
|
||||
flavors = list(resp_obj.obj['flavors'])
|
||||
flavor_refs = self._get_flavor_refs(context)
|
||||
|
||||
for flavor_rval in flavors:
|
||||
flavor_ref = flavor_refs[flavor_rval['id']]
|
||||
self._extend_flavor(flavor_rval, flavor_ref)
|
||||
|
||||
@wsgi.extends(action='create')
|
||||
def create(self, req, body, resp_obj):
|
||||
context = req.environ['nova.context']
|
||||
if authorize(context):
|
||||
# Attach our slave template to the response object
|
||||
resp_obj.attach(xml=FlavorextradatumTemplate())
|
||||
|
||||
try:
|
||||
fid = resp_obj.obj['flavor']['id']
|
||||
flavor_ref = instance_types.get_instance_type_by_flavor_id(fid)
|
||||
except exception.FlavorNotFound:
|
||||
explanation = _("Flavor not found.")
|
||||
raise exception.HTTPNotFound(explanation=explanation)
|
||||
|
||||
self._extend_flavor(resp_obj.obj['flavor'], flavor_ref)
|
||||
if not authorize(req.environ['nova.context']):
|
||||
return
|
||||
resp_obj.attach(xml=FlavorextradataTemplate())
|
||||
self._extend_flavors(req, list(resp_obj.obj['flavors']))
|
||||
|
||||
|
||||
class Flavorextradata(extensions.ExtensionDescriptor):
|
||||
|
||||
@@ -71,6 +71,7 @@ class FlavorManageController(wsgi.Controller):
|
||||
flavor = instance_types.create(name, memory_mb, vcpus,
|
||||
root_gb, ephemeral_gb, flavorid,
|
||||
swap, rxtx_factor, is_public)
|
||||
req.cache_db_flavor(flavor)
|
||||
except exception.InstanceTypeExists as err:
|
||||
raise webob.exc.HTTPConflict(explanation=str(err))
|
||||
|
||||
|
||||
@@ -79,6 +79,7 @@ class Controller(wsgi.Controller):
|
||||
def detail(self, req):
|
||||
"""Return all flavors in detail."""
|
||||
flavors = self._get_flavors(req)
|
||||
req.cache_db_flavors(flavors)
|
||||
return self._view_builder.detail(req, flavors)
|
||||
|
||||
@wsgi.serializers(xml=FlavorTemplate)
|
||||
@@ -86,6 +87,7 @@ class Controller(wsgi.Controller):
|
||||
"""Return data about the given flavor id."""
|
||||
try:
|
||||
flavor = instance_types.get_instance_type_by_flavor_id(id)
|
||||
req.cache_db_flavor(flavor)
|
||||
except exception.NotFound:
|
||||
raise webob.exc.HTTPNotFound()
|
||||
|
||||
|
||||
@@ -49,12 +49,6 @@ class ViewBuilder(common.ViewBuilder):
|
||||
},
|
||||
}
|
||||
|
||||
# NOTE(sirp): disabled attribute is namespaced for now for
|
||||
# compatability with the OpenStack API. This should ultimately be made
|
||||
# a first class attribute.
|
||||
flavor_dict["flavor"]["OS-FLV-DISABLED:disabled"] =\
|
||||
flavor.get("disabled", "")
|
||||
|
||||
return flavor_dict
|
||||
|
||||
def index(self, request, flavors):
|
||||
|
||||
@@ -66,49 +66,62 @@ class Request(webob.Request):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Request, self).__init__(*args, **kwargs)
|
||||
self._extension_data = {'db_instances': {}}
|
||||
self._extension_data = {'db_items': {}}
|
||||
|
||||
def cache_db_instances(self, instances):
|
||||
def cache_db_items(self, key, items, item_key='id'):
|
||||
"""
|
||||
Allow API methods to store instances from a DB query to be
|
||||
Allow API methods to store objects from a DB query to be
|
||||
used by API extensions within the same API request.
|
||||
|
||||
An instance of this class only lives for the lifetime of a
|
||||
single API request, so there's no need to implement full
|
||||
cache management.
|
||||
"""
|
||||
db_instances = self._extension_data['db_instances']
|
||||
for instance in instances:
|
||||
db_instances[instance['uuid']] = instance
|
||||
db_items = self._extension_data['db_items'].setdefault(key, {})
|
||||
for item in items:
|
||||
db_items[item[item_key]] = item
|
||||
|
||||
def cache_db_instance(self, instance):
|
||||
def get_db_items(self, key):
|
||||
"""
|
||||
Allow API methods to store an instance from a DB query to be
|
||||
used by API extensions within the same API request.
|
||||
|
||||
An instance of this class only lives for the lifetime of a
|
||||
single API request, so there's no need to implement full
|
||||
cache management.
|
||||
"""
|
||||
self.cache_db_instances([instance])
|
||||
|
||||
def get_db_instances(self):
|
||||
"""
|
||||
Allow an API extension to get previously stored instances within
|
||||
Allow an API extension to get previously stored objects within
|
||||
the same API request.
|
||||
|
||||
Note that the instance data will be slightly stale.
|
||||
Note that the object data will be slightly stale.
|
||||
"""
|
||||
return self._extension_data['db_instances']
|
||||
return self._extension_data['db_items'][key]
|
||||
|
||||
def get_db_instance(self, instance_uuid):
|
||||
def get_db_item(self, key, item_key):
|
||||
"""
|
||||
Allow an API extension to get a previously stored instance
|
||||
Allow an API extension to get a previously stored object
|
||||
within the same API request.
|
||||
|
||||
Note that the instance data will be slightly stale.
|
||||
Note that the object data will be slightly stale.
|
||||
"""
|
||||
return self._extension_data['db_instances'].get(instance_uuid)
|
||||
return self.get_db_items(key).get(item_key)
|
||||
|
||||
def cache_db_instances(self, instances):
|
||||
self.cache_db_items('instances', instances, 'uuid')
|
||||
|
||||
def cache_db_instance(self, instance):
|
||||
self.cache_db_items('instances', [instance], 'uuid')
|
||||
|
||||
def get_db_instances(self):
|
||||
return self.get_db_items('instances')
|
||||
|
||||
def get_db_instance(self, instance_uuid):
|
||||
return self.get_db_item('instances', instance_uuid)
|
||||
|
||||
def cache_db_flavors(self, flavors):
|
||||
self.cache_db_items('flavors', flavors, 'flavorid')
|
||||
|
||||
def cache_db_flavor(self, flavor):
|
||||
self.cache_db_items('flavors', [flavor], 'flavorid')
|
||||
|
||||
def get_db_flavors(self):
|
||||
return self.get_db_items('flavors')
|
||||
|
||||
def get_db_flavor(self, flavorid):
|
||||
return self.get_db_item('flavors', flavorid)
|
||||
|
||||
def best_match_content_type(self):
|
||||
"""Determine the requested response content-type."""
|
||||
|
||||
112
nova/tests/api/openstack/compute/contrib/test_flavor_disabled.py
Normal file
112
nova/tests/api/openstack/compute/contrib/test_flavor_disabled.py
Normal file
@@ -0,0 +1,112 @@
|
||||
# Copyright 2011 OpenStack LLC.
|
||||
# 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 lxml import etree
|
||||
import webob
|
||||
|
||||
from nova.api.openstack.compute.contrib import flavor_disabled
|
||||
from nova.compute import instance_types
|
||||
from nova import flags
|
||||
from nova.openstack.common import jsonutils
|
||||
from nova import test
|
||||
from nova.tests.api.openstack import fakes
|
||||
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
|
||||
|
||||
FAKE_FLAVORS = {
|
||||
'flavor 1': {
|
||||
"flavorid": '1',
|
||||
"name": 'flavor 1',
|
||||
"memory_mb": '256',
|
||||
"root_gb": '10',
|
||||
"disabled": False,
|
||||
},
|
||||
'flavor 2': {
|
||||
"flavorid": '2',
|
||||
"name": 'flavor 2',
|
||||
"memory_mb": '512',
|
||||
"root_gb": '20',
|
||||
"disabled": True,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def fake_instance_type_get_by_flavor_id(flavorid):
|
||||
return FAKE_FLAVORS['flavor %s' % flavorid]
|
||||
|
||||
|
||||
def fake_instance_type_get_all(*args, **kwargs):
|
||||
return FAKE_FLAVORS
|
||||
|
||||
|
||||
class FlavorDisabledTest(test.TestCase):
|
||||
content_type = 'application/json'
|
||||
prefix = '%s:' % flavor_disabled.Flavor_disabled.alias
|
||||
|
||||
def setUp(self):
|
||||
super(FlavorDisabledTest, self).setUp()
|
||||
ext = ('nova.api.openstack.compute.contrib'
|
||||
'.flavor_disabled.Flavor_disabled')
|
||||
self.flags(osapi_compute_extension=[ext])
|
||||
fakes.stub_out_nw_api(self.stubs)
|
||||
self.stubs.Set(instance_types, "get_all_types",
|
||||
fake_instance_type_get_all)
|
||||
self.stubs.Set(instance_types,
|
||||
"get_instance_type_by_flavor_id",
|
||||
fake_instance_type_get_by_flavor_id)
|
||||
|
||||
def _make_request(self, url):
|
||||
req = webob.Request.blank(url)
|
||||
req.headers['Accept'] = self.content_type
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
return res
|
||||
|
||||
def _get_flavor(self, body):
|
||||
return jsonutils.loads(body).get('flavor')
|
||||
|
||||
def _get_flavors(self, body):
|
||||
return jsonutils.loads(body).get('flavors')
|
||||
|
||||
def assertFlavorDisabled(self, flavor, disabled):
|
||||
self.assertEqual(str(flavor.get('%sdisabled' % self.prefix)), disabled)
|
||||
|
||||
def test_show(self):
|
||||
url = '/v2/fake/flavors/1'
|
||||
res = self._make_request(url)
|
||||
|
||||
self.assertEqual(res.status_int, 200)
|
||||
self.assertFlavorDisabled(self._get_flavor(res.body), 'False')
|
||||
|
||||
def test_detail(self):
|
||||
url = '/v2/fake/flavors/detail'
|
||||
res = self._make_request(url)
|
||||
|
||||
self.assertEqual(res.status_int, 200)
|
||||
flavors = self._get_flavors(res.body)
|
||||
self.assertFlavorDisabled(flavors[0], 'False')
|
||||
self.assertFlavorDisabled(flavors[1], 'True')
|
||||
|
||||
|
||||
class FlavorDisabledXmlTest(FlavorDisabledTest):
|
||||
content_type = 'application/xml'
|
||||
prefix = '{%s}' % flavor_disabled.Flavor_disabled.namespace
|
||||
|
||||
def _get_flavor(self, body):
|
||||
return etree.XML(body)
|
||||
|
||||
def _get_flavors(self, body):
|
||||
return etree.XML(body).getchildren()
|
||||
@@ -47,7 +47,8 @@ def fake_get_instance_type_by_flavor_id(flavorid):
|
||||
'deleted_at': None,
|
||||
'vcpu_weight': None,
|
||||
'id': 7,
|
||||
'is_public': True
|
||||
'is_public': True,
|
||||
'disabled': False,
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -40,7 +40,6 @@ def fake_get_instance_type_by_flavor_id(flavorid):
|
||||
'extra_specs': {},
|
||||
'deleted_at': None,
|
||||
'vcpu_weight': None,
|
||||
'is_public': True
|
||||
}
|
||||
|
||||
|
||||
@@ -54,11 +53,14 @@ def fake_get_all_types(inactive=0, filters=None):
|
||||
class FlavorextradataTest(test.TestCase):
|
||||
def setUp(self):
|
||||
super(FlavorextradataTest, self).setUp()
|
||||
ext = ('nova.api.openstack.compute.contrib'
|
||||
'.flavorextradata.Flavorextradata')
|
||||
self.flags(osapi_compute_extension=[ext])
|
||||
self.stubs.Set(instance_types, 'get_instance_type_by_flavor_id',
|
||||
fake_get_instance_type_by_flavor_id)
|
||||
self.stubs.Set(instance_types, 'get_all_types', fake_get_all_types)
|
||||
|
||||
def _verify_server_response(self, flavor, expected):
|
||||
def _verify_flavor_response(self, flavor, expected):
|
||||
for key in expected:
|
||||
self.assertEquals(flavor[key], expected[key])
|
||||
|
||||
@@ -73,7 +75,6 @@ class FlavorextradataTest(test.TestCase):
|
||||
'OS-FLV-EXT-DATA:ephemeral': 1,
|
||||
'swap': 512,
|
||||
'rxtx_factor': 1,
|
||||
'os-flavor-access:is_public': True,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +83,7 @@ class FlavorextradataTest(test.TestCase):
|
||||
req.headers['Content-Type'] = 'application/json'
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
body = jsonutils.loads(res.body)
|
||||
self._verify_server_response(body['flavor'], expected['flavor'])
|
||||
self._verify_flavor_response(body['flavor'], expected['flavor'])
|
||||
|
||||
def test_detail(self):
|
||||
expected = [
|
||||
@@ -95,7 +96,6 @@ class FlavorextradataTest(test.TestCase):
|
||||
'OS-FLV-EXT-DATA:ephemeral': 1,
|
||||
'swap': 512,
|
||||
'rxtx_factor': 1,
|
||||
'os-flavor-access:is_public': True,
|
||||
},
|
||||
{
|
||||
'id': '2',
|
||||
@@ -106,7 +106,6 @@ class FlavorextradataTest(test.TestCase):
|
||||
'OS-FLV-EXT-DATA:ephemeral': 1,
|
||||
'swap': 512,
|
||||
'rxtx_factor': 1,
|
||||
'os-flavor-access:is_public': True,
|
||||
},
|
||||
]
|
||||
|
||||
@@ -116,4 +115,4 @@ class FlavorextradataTest(test.TestCase):
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
body = jsonutils.loads(res.body)
|
||||
for i, flavor in enumerate(body['flavors']):
|
||||
self._verify_server_response(flavor, expected[i])
|
||||
self._verify_flavor_response(flavor, expected[i])
|
||||
|
||||
@@ -167,6 +167,7 @@ class ExtensionControllerTest(ExtensionTestCase):
|
||||
"ExtendedStatus",
|
||||
"ExtendedServerAttributes",
|
||||
"FlavorAccess",
|
||||
"FlavorDisabled",
|
||||
"FlavorExtraSpecs",
|
||||
"FlavorExtraData",
|
||||
"FlavorManage",
|
||||
|
||||
@@ -113,7 +113,6 @@ class FlavorsTest(test.TestCase):
|
||||
expected = {
|
||||
"flavor": {
|
||||
"id": "1",
|
||||
"OS-FLV-DISABLED:disabled": False,
|
||||
"name": "flavor 1",
|
||||
"ram": "256",
|
||||
"disk": "10",
|
||||
@@ -142,7 +141,6 @@ class FlavorsTest(test.TestCase):
|
||||
expected = {
|
||||
"flavor": {
|
||||
"id": "1",
|
||||
"OS-FLV-DISABLED:disabled": False,
|
||||
"name": "flavor 1",
|
||||
"ram": "256",
|
||||
"disk": "10",
|
||||
@@ -313,7 +311,6 @@ class FlavorsTest(test.TestCase):
|
||||
"flavors": [
|
||||
{
|
||||
"id": "1",
|
||||
"OS-FLV-DISABLED:disabled": False,
|
||||
"name": "flavor 1",
|
||||
"ram": "256",
|
||||
"disk": "10",
|
||||
@@ -333,7 +330,6 @@ class FlavorsTest(test.TestCase):
|
||||
},
|
||||
{
|
||||
"id": "2",
|
||||
"OS-FLV-DISABLED:disabled": False,
|
||||
"name": "flavor 2",
|
||||
"ram": "512",
|
||||
"disk": "20",
|
||||
@@ -435,7 +431,6 @@ class FlavorsTest(test.TestCase):
|
||||
"flavors": [
|
||||
{
|
||||
"id": "2",
|
||||
"OS-FLV-DISABLED:disabled": False,
|
||||
"name": "flavor 2",
|
||||
"ram": "512",
|
||||
"disk": "20",
|
||||
@@ -793,11 +788,6 @@ class DisabledFlavorsWithRealDBTest(test.TestCase):
|
||||
|
||||
self.assertEqual(flavor['name'], self.disabled_type['name'])
|
||||
|
||||
# FIXME(sirp): the disabled field is currently namespaced so that we
|
||||
# don't impact the OpenStack API. Eventually this should probably be
|
||||
# made a first-class attribute in the next OSAPI version.
|
||||
self.assert_('OS-FLV-DISABLED:disabled' in flavor)
|
||||
|
||||
def test_show_should_include_disabled_flavor_for_admin(self):
|
||||
self.context.is_admin = True
|
||||
|
||||
@@ -805,4 +795,3 @@ class DisabledFlavorsWithRealDBTest(test.TestCase):
|
||||
self.req, self.disabled_type['flavorid'])['flavor']
|
||||
|
||||
self.assertEqual(flavor['name'], self.disabled_type['name'])
|
||||
self.assert_('OS-FLV-DISABLED:disabled' in flavor)
|
||||
|
||||
@@ -32,6 +32,14 @@
|
||||
"namespace": "http://docs.openstack.org/compute/ext/extended_status/api/v1.1",
|
||||
"updated": "2011-11-03T00:00:00+00:00"
|
||||
},
|
||||
{
|
||||
"alias": "OS-FLV-DISABLED",
|
||||
"description": "Support to show the disabled status of a flavor",
|
||||
"links": [],
|
||||
"name": "FlavorDisabled",
|
||||
"namespace": "http://docs.openstack.org/compute/ext/flavor_disabled/api/v1.1",
|
||||
"updated": "2012-08-29T00:00:00+00:00"
|
||||
},
|
||||
{
|
||||
"alias": "OS-FLV-EXT-DATA",
|
||||
"description": "Provide additional data for flavors",
|
||||
|
||||
@@ -32,6 +32,14 @@
|
||||
"namespace": "http://docs.openstack.org/compute/ext/extended_status/api/v1.1",
|
||||
"updated": "%(timestamp)s"
|
||||
},
|
||||
{
|
||||
"alias": "OS-FLV-DISABLED",
|
||||
"description": "%(text)s",
|
||||
"links": [],
|
||||
"name": "FlavorDisabled",
|
||||
"namespace": "http://docs.openstack.org/compute/ext/flavor_disabled/api/v1.1",
|
||||
"updated": "%(timestamp)s"
|
||||
},
|
||||
{
|
||||
"alias": "OS-FLV-EXT-DATA",
|
||||
"description": "%(text)s",
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
<extension alias="OS-EXT-STS" updated="2011-11-03T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/extended_status/api/v1.1" name="ExtendedStatus">
|
||||
<description>Extended Status support</description>
|
||||
</extension>
|
||||
<extension alias="OS-FLV-DISABLED" updated="2012-08-29T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/flavor_disabled/api/v1.1" name="FlavorDisabled">
|
||||
<description>Support to show the disabled status of a flavor</description>
|
||||
</extension>
|
||||
<extension alias="OS-FLV-EXT-DATA" updated="2011-09-14T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/flavor_extra_data/api/v1.1" name="FlavorExtraData">
|
||||
<description>Provide additional data for flavors</description>
|
||||
</extension>
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
<extension alias="OS-EXT-STS" updated="%(timestamp)s" namespace="http://docs.openstack.org/compute/ext/extended_status/api/v1.1" name="ExtendedStatus">
|
||||
<description>%(text)s</description>
|
||||
</extension>
|
||||
<extension alias="OS-FLV-DISABLED" updated="%(timestamp)s" namespace="http://docs.openstack.org/compute/ext/flavor_disabled/api/v1.1" name="FlavorDisabled">
|
||||
<description>%(text)s</description>
|
||||
</extension>
|
||||
<extension alias="OS-FLV-EXT-DATA" updated="%(timestamp)s" namespace="http://docs.openstack.org/compute/ext/flavor_extra_data/api/v1.1" name="FlavorExtraData">
|
||||
<description>%(text)s</description>
|
||||
</extension>
|
||||
|
||||
@@ -93,6 +93,7 @@
|
||||
"compute_extension:extended_server_attributes": [],
|
||||
"compute_extension:extended_status": [],
|
||||
"compute_extension:flavor_access": [],
|
||||
"compute_extension:flavor_disabled": [],
|
||||
"compute_extension:flavorextradata": [],
|
||||
"compute_extension:flavorextraspecs": [],
|
||||
"compute_extension:flavormanage": [],
|
||||
|
||||
Reference in New Issue
Block a user