Add support for extended attributes for extension resources
Removed unsed import from quantum/tests/unit/test_extension_extended_attribute.py Address comments. Change update_attributes_map definition/behavior Fixes: bug #1116664 Change-Id: Idc360f5b3b35fb1d40237e1bbce39684508175cf
This commit is contained in:
parent
b393aff794
commit
6d9e4413b1
@ -159,6 +159,26 @@ class ExtensionDescriptor(object):
|
|||||||
"""
|
"""
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def update_attributes_map(self, extended_attributes,
|
||||||
|
extension_attrs_map=None):
|
||||||
|
"""Update attributes map for this extension
|
||||||
|
|
||||||
|
This is default method for extending an extension's attributes map.
|
||||||
|
An extension can use this method and supplying its own resource
|
||||||
|
attribute map in extension_attrs_map argument to extend all its
|
||||||
|
attributes that needs to be extended.
|
||||||
|
|
||||||
|
If an extension does not implement update_attributes_map, the method
|
||||||
|
does nothing and just return.
|
||||||
|
"""
|
||||||
|
if not extension_attrs_map:
|
||||||
|
return
|
||||||
|
|
||||||
|
for resource, attrs in extension_attrs_map.iteritems():
|
||||||
|
extended_attrs = extended_attributes.get(resource)
|
||||||
|
if extended_attrs:
|
||||||
|
attrs.update(extended_attrs)
|
||||||
|
|
||||||
|
|
||||||
class ActionExtensionController(wsgi.Controller):
|
class ActionExtensionController(wsgi.Controller):
|
||||||
|
|
||||||
@ -427,9 +447,12 @@ class ExtensionManager(object):
|
|||||||
After this function, we will extend the attr_map if an extension
|
After this function, we will extend the attr_map if an extension
|
||||||
wants to extend this map.
|
wants to extend this map.
|
||||||
"""
|
"""
|
||||||
|
update_exts = []
|
||||||
for ext in self.extensions.itervalues():
|
for ext in self.extensions.itervalues():
|
||||||
if not hasattr(ext, 'get_extended_resources'):
|
if not hasattr(ext, 'get_extended_resources'):
|
||||||
continue
|
continue
|
||||||
|
if hasattr(ext, 'update_attributes_map'):
|
||||||
|
update_exts.append(ext)
|
||||||
try:
|
try:
|
||||||
extended_attrs = ext.get_extended_resources(version)
|
extended_attrs = ext.get_extended_resources(version)
|
||||||
for resource, resource_attrs in extended_attrs.iteritems():
|
for resource, resource_attrs in extended_attrs.iteritems():
|
||||||
@ -443,6 +466,10 @@ class ExtensionManager(object):
|
|||||||
LOG.exception(_("Error fetching extended attributes for "
|
LOG.exception(_("Error fetching extended attributes for "
|
||||||
"extension '%s'"), ext.get_name())
|
"extension '%s'"), ext.get_name())
|
||||||
|
|
||||||
|
"""Extending extensions' attributes map."""
|
||||||
|
for ext in update_exts:
|
||||||
|
ext.update_attributes_map(attr_map)
|
||||||
|
|
||||||
def _check_extension(self, extension):
|
def _check_extension(self, extension):
|
||||||
"""Checks for required methods in extension objects."""
|
"""Checks for required methods in extension objects."""
|
||||||
try:
|
try:
|
||||||
|
58
quantum/tests/unit/extensions/extendedattribute.py
Normal file
58
quantum/tests/unit/extensions/extendedattribute.py
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
#
|
||||||
|
# Copyright 2013 VMware, Inc. 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.
|
||||||
|
#
|
||||||
|
# @author: Kaiwei Fan, VMware, Inc
|
||||||
|
|
||||||
|
from quantum.api import extensions
|
||||||
|
|
||||||
|
EXTENDED_ATTRIBUTE = 'extended_attribute'
|
||||||
|
EXTENDED_ATTRIBUTES_2_0 = {
|
||||||
|
'ext_test_resources': {
|
||||||
|
EXTENDED_ATTRIBUTE: {'allow_post': True, 'allow_put': False,
|
||||||
|
'validate': {'type:uuid_or_none': None},
|
||||||
|
'default': None, 'is_visible': True},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class Extendedattribute(extensions.ExtensionDescriptor):
|
||||||
|
"""Extension class supporting extended attribute for router."""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_name(cls):
|
||||||
|
return "Extended Extension Attributes"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_alias(cls):
|
||||||
|
return "extended-ext-attr"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_description(cls):
|
||||||
|
return "Provides extended_attr attribute to router"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_namespace(cls):
|
||||||
|
return ""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_updated(cls):
|
||||||
|
return "2013-02-05T00:00:00-00:00"
|
||||||
|
|
||||||
|
def get_extended_resources(self, version):
|
||||||
|
if version == "2.0":
|
||||||
|
return EXTENDED_ATTRIBUTES_2_0
|
||||||
|
else:
|
||||||
|
return {}
|
109
quantum/tests/unit/extensions/extensionattribute.py
Normal file
109
quantum/tests/unit/extensions/extensionattribute.py
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
# Copyright 2013 VMware, Inc.
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
# @author: Kaiwei Fan, VMware, Inc
|
||||||
|
#
|
||||||
|
|
||||||
|
from abc import abstractmethod
|
||||||
|
|
||||||
|
from quantum import manager, quota
|
||||||
|
from quantum.api import extensions
|
||||||
|
from quantum.api.v2 import base
|
||||||
|
|
||||||
|
|
||||||
|
# Attribute Map
|
||||||
|
RESOURCE_ATTRIBUTE_MAP = {
|
||||||
|
'ext_test_resources': {
|
||||||
|
'id': {'allow_post': False, 'allow_put': False,
|
||||||
|
'validate': {'type:uuid': None},
|
||||||
|
'is_visible': True},
|
||||||
|
'name': {'allow_post': True, 'allow_put': True,
|
||||||
|
'validate': {'type:string': None},
|
||||||
|
'is_visible': True, 'default': ''},
|
||||||
|
'tenant_id': {'allow_post': True, 'allow_put': False,
|
||||||
|
'required_by_policy': True,
|
||||||
|
'validate': {'type:string': None},
|
||||||
|
'is_visible': True},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class Extensionattribute(extensions.ExtensionDescriptor):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_name(cls):
|
||||||
|
return "Extension Test Resource"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_alias(cls):
|
||||||
|
return "ext-obj-test"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_description(cls):
|
||||||
|
return "Extension Test Resource"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_namespace(cls):
|
||||||
|
return ""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_updated(cls):
|
||||||
|
return "2013-02-05T10:00:00-00:00"
|
||||||
|
|
||||||
|
def update_attributes_map(self, attributes):
|
||||||
|
super(Extensionattribute, self).update_attributes_map(
|
||||||
|
attributes, extension_attrs_map=RESOURCE_ATTRIBUTE_MAP)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_resources(cls):
|
||||||
|
""" Returns Ext Resources """
|
||||||
|
exts = []
|
||||||
|
plugin = manager.QuantumManager.get_plugin()
|
||||||
|
resource_name = 'ext_test_resource'
|
||||||
|
collection_name = resource_name + "s"
|
||||||
|
params = RESOURCE_ATTRIBUTE_MAP.get(collection_name, dict())
|
||||||
|
|
||||||
|
quota.QUOTAS.register_resource_by_name(resource_name)
|
||||||
|
|
||||||
|
controller = base.create_resource(collection_name,
|
||||||
|
resource_name,
|
||||||
|
plugin, params,
|
||||||
|
member_actions={})
|
||||||
|
|
||||||
|
ex = extensions.ResourceExtension(collection_name,
|
||||||
|
controller,
|
||||||
|
member_actions={})
|
||||||
|
exts.append(ex)
|
||||||
|
|
||||||
|
return exts
|
||||||
|
|
||||||
|
def get_extended_resources(self, version):
|
||||||
|
if version == "2.0":
|
||||||
|
return RESOURCE_ATTRIBUTE_MAP
|
||||||
|
else:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
class ExtensionObjectTestPluginBase(object):
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def create_ext_test_resource(self, context, router):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get_ext_test_resource(self, context, id, fields=None):
|
||||||
|
pass
|
141
quantum/tests/unit/test_extension_extended_attribute.py
Normal file
141
quantum/tests/unit/test_extension_extended_attribute.py
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
# Copyright 2013 VMware, Inc
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""
|
||||||
|
Unit tests for extension extended attribute
|
||||||
|
"""
|
||||||
|
|
||||||
|
import unittest2 as unittest
|
||||||
|
import webob.exc as webexc
|
||||||
|
|
||||||
|
import quantum
|
||||||
|
from quantum.api import extensions
|
||||||
|
from quantum.common import config
|
||||||
|
from quantum import manager
|
||||||
|
from quantum.openstack.common import cfg
|
||||||
|
from quantum.plugins.common import constants
|
||||||
|
from quantum.plugins.openvswitch import ovs_quantum_plugin
|
||||||
|
from quantum.tests.unit.extensions import extendedattribute as extattr
|
||||||
|
from quantum.tests.unit import test_api_v2
|
||||||
|
from quantum.tests.unit import testlib_api
|
||||||
|
from quantum import wsgi
|
||||||
|
|
||||||
|
_uuid = test_api_v2._uuid
|
||||||
|
_get_path = test_api_v2._get_path
|
||||||
|
extensions_path = ':'.join(quantum.tests.unit.extensions.__path__)
|
||||||
|
|
||||||
|
|
||||||
|
class ExtensionExtendedAttributeTestPlugin(
|
||||||
|
ovs_quantum_plugin.OVSQuantumPluginV2):
|
||||||
|
|
||||||
|
supported_extension_aliases = [
|
||||||
|
'ext-obj-test', "extended-ext-attr"
|
||||||
|
]
|
||||||
|
|
||||||
|
def __init__(self, configfile=None):
|
||||||
|
super(ExtensionExtendedAttributeTestPlugin, self)
|
||||||
|
self.objs = []
|
||||||
|
self.objh = {}
|
||||||
|
|
||||||
|
def create_ext_test_resource(self, context, ext_test_resource):
|
||||||
|
obj = ext_test_resource['ext_test_resource']
|
||||||
|
id = _uuid()
|
||||||
|
obj['id'] = id
|
||||||
|
self.objs.append(obj)
|
||||||
|
self.objh.update({id: obj})
|
||||||
|
return obj
|
||||||
|
|
||||||
|
def get_ext_test_resources(self, context, filters=None, fields=None):
|
||||||
|
return self.objs
|
||||||
|
|
||||||
|
def get_ext_test_resource(self, context, id, fields=None):
|
||||||
|
return self.objh[id]
|
||||||
|
|
||||||
|
|
||||||
|
class ExtensionExtendedAttributeTestCase(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
plugin = (
|
||||||
|
"quantum.tests.unit.test_extension_extended_attribute."
|
||||||
|
"ExtensionExtendedAttributeTestPlugin"
|
||||||
|
)
|
||||||
|
|
||||||
|
# point config file to: quantum/tests/etc/quantum.conf.test
|
||||||
|
args = ['--config-file', test_api_v2.etcdir('quantum.conf.test')]
|
||||||
|
config.parse(args=args)
|
||||||
|
|
||||||
|
cfg.CONF.set_override('core_plugin', plugin)
|
||||||
|
|
||||||
|
manager.QuantumManager._instance = None
|
||||||
|
|
||||||
|
ext_mgr = extensions.PluginAwareExtensionManager(
|
||||||
|
extensions_path,
|
||||||
|
{constants.CORE: ExtensionExtendedAttributeTestPlugin}
|
||||||
|
)
|
||||||
|
ext_mgr.extend_resources("2.0", {})
|
||||||
|
extensions.PluginAwareExtensionManager._instance = ext_mgr
|
||||||
|
|
||||||
|
app = config.load_paste_app('extensions_test_app')
|
||||||
|
self._api = extensions.ExtensionMiddleware(app, ext_mgr=ext_mgr)
|
||||||
|
|
||||||
|
self._tenant_id = "8c70909f-b081-452d-872b-df48e6c355d1"
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
# restore original resource attribute map before
|
||||||
|
self._api = None
|
||||||
|
cfg.CONF.reset()
|
||||||
|
|
||||||
|
def _do_request(self, method, path, data=None, params=None, action=None):
|
||||||
|
content_type = 'application/json'
|
||||||
|
body = None
|
||||||
|
if data is not None: # empty dict is valid
|
||||||
|
body = wsgi.Serializer().serialize(data, content_type)
|
||||||
|
|
||||||
|
req = testlib_api.create_request(
|
||||||
|
path, body, content_type,
|
||||||
|
method, query_string=params)
|
||||||
|
res = req.get_response(self._api)
|
||||||
|
if res.status_code >= 400:
|
||||||
|
raise webexc.HTTPClientError(detail=res.body, code=res.status_code)
|
||||||
|
if res.status_code != webexc.HTTPNoContent.code:
|
||||||
|
return res.json
|
||||||
|
|
||||||
|
def _ext_test_resource_create(self, attr=None):
|
||||||
|
data = {
|
||||||
|
"ext_test_resource": {
|
||||||
|
"tenant_id": self._tenant_id,
|
||||||
|
"name": "test",
|
||||||
|
extattr.EXTENDED_ATTRIBUTE: attr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res = self._do_request('POST', _get_path('ext_test_resources'), data)
|
||||||
|
return res['ext_test_resource']
|
||||||
|
|
||||||
|
def test_ext_test_resource_create(self):
|
||||||
|
ext_test_resource = self._ext_test_resource_create()
|
||||||
|
attr = _uuid()
|
||||||
|
ext_test_resource = self._ext_test_resource_create(attr)
|
||||||
|
self.assertEqual(ext_test_resource[extattr.EXTENDED_ATTRIBUTE], attr)
|
||||||
|
|
||||||
|
def test_ext_test_resource_get(self):
|
||||||
|
attr = _uuid()
|
||||||
|
obj = self._ext_test_resource_create(attr)
|
||||||
|
obj_id = obj['id']
|
||||||
|
res = self._do_request('GET', _get_path(
|
||||||
|
'ext_test_resources/{0}'.format(obj_id)))
|
||||||
|
obj2 = res['ext_test_resource']
|
||||||
|
self.assertEqual(obj2[extattr.EXTENDED_ATTRIBUTE], attr)
|
Loading…
Reference in New Issue
Block a user