]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Add support for extended attributes for extension resources
authorKaiwei Fan <fank@vmware.com>
Tue, 5 Feb 2013 21:26:08 +0000 (13:26 -0800)
committerKaiwei Fan <fank@vmware.com>
Thu, 14 Feb 2013 16:10:57 +0000 (08:10 -0800)
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

quantum/api/extensions.py
quantum/tests/unit/extensions/extendedattribute.py [new file with mode: 0644]
quantum/tests/unit/extensions/extensionattribute.py [new file with mode: 0644]
quantum/tests/unit/test_extension_extended_attribute.py [new file with mode: 0644]

index d321a8668471153befbabd2a5267cc4cd534aa11..3d9911186260bec180cf47636ff10af427eb75b9 100644 (file)
@@ -159,6 +159,26 @@ class ExtensionDescriptor(object):
         """
         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):
 
@@ -427,9 +447,12 @@ class ExtensionManager(object):
         After this function, we will extend the attr_map if an extension
         wants to extend this map.
         """
+        update_exts = []
         for ext in self.extensions.itervalues():
             if not hasattr(ext, 'get_extended_resources'):
                 continue
+            if hasattr(ext, 'update_attributes_map'):
+                update_exts.append(ext)
             try:
                 extended_attrs = ext.get_extended_resources(version)
                 for resource, resource_attrs in extended_attrs.iteritems():
@@ -443,6 +466,10 @@ class ExtensionManager(object):
                 LOG.exception(_("Error fetching extended attributes for "
                                 "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):
         """Checks for required methods in extension objects."""
         try:
diff --git a/quantum/tests/unit/extensions/extendedattribute.py b/quantum/tests/unit/extensions/extendedattribute.py
new file mode 100644 (file)
index 0000000..2451b8d
--- /dev/null
@@ -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 {}
diff --git a/quantum/tests/unit/extensions/extensionattribute.py b/quantum/tests/unit/extensions/extensionattribute.py
new file mode 100644 (file)
index 0000000..0690538
--- /dev/null
@@ -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
diff --git a/quantum/tests/unit/test_extension_extended_attribute.py b/quantum/tests/unit/test_extension_extended_attribute.py
new file mode 100644 (file)
index 0000000..62d46ce
--- /dev/null
@@ -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)