diff --git a/neutron_lib/api/extensions.py b/neutron_lib/api/extensions.py new file mode 100644 index 000000000..c2f16182d --- /dev/null +++ b/neutron_lib/api/extensions.py @@ -0,0 +1,144 @@ +# Copyright 2011 OpenStack Foundation. +# 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. + +import abc +import six + + +@six.add_metaclass(abc.ABCMeta) +class ExtensionDescriptor(object): + """Base class that defines the contract for extensions.""" + + @abc.abstractmethod + def get_name(self): + """The name of the extension. + + e.g. 'Fox In Socks' + """ + + @abc.abstractmethod + def get_alias(self): + """The alias for the extension. + + e.g. 'FOXNSOX' + """ + + @abc.abstractmethod + def get_description(self): + """Friendly description for the extension. + + e.g. 'The Fox In Socks Extension' + """ + + @abc.abstractmethod + def get_updated(self): + """The timestamp when the extension was last updated. + + e.g. '2011-01-22T13:25:27-06:00' + """ + # NOTE(justinsb): Not sure of the purpose of this is, vs the XML NS + + def get_resources(self): + """List of extensions.ResourceExtension extension objects. + + Resources define new nouns, and are accessible through URLs. + """ + return [] + + def get_actions(self): + """List of extensions.ActionExtension extension objects. + + Actions are verbs callable from the API. + """ + return [] + + def get_request_extensions(self): + """List of extensions.RequestException extension objects. + + Request extensions are used to handle custom request data. + """ + return [] + + def get_extended_resources(self, version): + """Retrieve extended resources or attributes for core resources. + + Extended attributes are implemented by a core plugin similarly + to the attributes defined in the core, and can appear in + request and response messages. Their names are scoped with the + extension's prefix. The core API version is passed to this + function, which must return a + map[][][] + specifying the extended resource attribute properties required + by that API version. + + Extension can add resources and their attr definitions too. + The returned map can be integrated into RESOURCE_ATTRIBUTE_MAP. + """ + return {} + + def get_plugin_interface(self): + """Returns an abstract class which defines contract for the plugin. + + The abstract class should inherit from extensions.PluginInterface, + Methods in this abstract class should be decorated as abstractmethod + """ + + def get_required_extensions(self): + """Returns a list of extensions to be processed before this one.""" + return [] + + def get_optional_extensions(self): + """Returns a list of extensions to be processed before this one. + + Unlike get_required_extensions. This will not fail the loading of + the extension if one of these extensions is not present. This is + useful for an extension that extends multiple resources across + other extensions that should still work for the remaining extensions + when one is missing. + """ + return [] + + 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.items(): + extended_attrs = extended_attributes.get(resource) + if extended_attrs: + attrs.update(extended_attrs) + + def get_pecan_resources(self): + """List of PecanResourceExtension extension objects. + + Resources define new nouns, and are accessible through URLs. + The controllers associated with each instance of + extensions.ResourceExtension should be a subclass of + neutron.pecan_wsgi.controllers.utils.NeutronPecanController. + + If a resource is defined in both get_resources and get_pecan_resources, + the resource defined in get_pecan_resources will take precedence. + """ + return [] diff --git a/neutron_lib/tests/unit/api/test_extensions.py b/neutron_lib/tests/unit/api/test_extensions.py new file mode 100644 index 000000000..5b88bfe69 --- /dev/null +++ b/neutron_lib/tests/unit/api/test_extensions.py @@ -0,0 +1,83 @@ +# Copyright 2016 OpenStack Foundation +# 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 neutron_lib.api import extensions +from neutron_lib.tests import _base as base + + +class InheritFromExtensionDescriptor(extensions.ExtensionDescriptor): + """Class to inherit from ExtensionDescriptor to test its methods + + Because ExtensionDescriptor is an abstract class, in order to test methods + we need to create something to inherit from it so we have something + instantiatable. The only things defined here are those that are required + because in ExtensionDescriptor they are marked as @abc.abstractmethod. + """ + + def get_name(self): + pass + + def get_alias(self): + pass + + def get_description(self): + pass + + def get_updated(self): + pass + + def update_attributes_map_save(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.items(): + extended_attrs = extended_attributes.get(resource) + if extended_attrs: + attrs.update(extended_attrs) + + +class TestExtensionDescriptor(base.BaseTestCase): + + def _setup_attribute_maps(self): + self.extended_attributes = {'resource_one': {'one': 'first'}, + 'resource_two': {'two': 'second'}} + self.extension_attrs_map = {'resource_one': {'three': 'third'}} + + def test_update_attributes_map_works(self): + self._setup_attribute_maps() + extension_description = InheritFromExtensionDescriptor() + extension_description.update_attributes_map(self.extended_attributes, + self.extension_attrs_map) + self.assertEqual(self.extension_attrs_map, + {'resource_one': {'one': 'first', + 'three': 'third'}}) + + def test_update_attributes_map_short_circuit_exit(self): + self._setup_attribute_maps() + extension_description = InheritFromExtensionDescriptor() + extension_description.update_attributes_map(self.extended_attributes) + self.assertEqual(self.extension_attrs_map, + {'resource_one': {'three': 'third'}}) diff --git a/releasenotes/notes/extension_descriptor-04025e86249cc94c.yaml b/releasenotes/notes/extension_descriptor-04025e86249cc94c.yaml new file mode 100644 index 000000000..6e791f984 --- /dev/null +++ b/releasenotes/notes/extension_descriptor-04025e86249cc94c.yaml @@ -0,0 +1,4 @@ +--- +features: + - The ExtensionDescriptor class moved from neutron.api.extensions to + neutron_lib.api.extensions.