diff --git a/neutron/db/_resource_extend.py b/neutron/db/_resource_extend.py index a5456d19129..0f04acbd7b3 100644 --- a/neutron/db/_resource_extend.py +++ b/neutron/db/_resource_extend.py @@ -15,6 +15,9 @@ NOTE: This module shall not be used by external projects. It will be moved to neutron-lib in due course, and then it can be used from there. """ +import collections +import inspect + from neutron.common import utils # This dictionary will store methods for extending API resources. @@ -25,6 +28,10 @@ _resource_extend_functions = { # ... } +# This dictionary will store @extends decorated methods with a list of +# resources that each method will extend on class initialization. +_DECORATED_EXTEND_METHODS = collections.defaultdict(list) + def register_funcs(resource, funcs): """Add functions to extend a resource. @@ -59,3 +66,65 @@ def get_funcs(resource): """ return _resource_extend_functions.get(resource, []) + + +def extends(resources): + """Use to decorate methods on classes before initialization. + + Any classes that use this must themselves be decorated with the + @has_resource_extenders decorator to setup the __new__ method to + actually register the instance methods after initialization. + + :param resources: Resource collection names. The decorated method will + be registered with each resource as an extend function. + :type resources: list of str + + """ + def decorator(method): + _DECORATED_EXTEND_METHODS[method].extend(resources) + return method + return decorator + + +def has_resource_extenders(klass): + """Decorator to setup __new__ method in classes to extend resources. + + Any method decorated with @extends above is an unbound method on a class. + This decorator sets up the class __new__ method to add the bound + method to _resource_extend_functions after object instantiation. + """ + orig_new = klass.__new__ + new_inherited = '__new__' not in klass.__dict__ + + @staticmethod + def replacement_new(cls, *args, **kwargs): + if new_inherited: + # class didn't define __new__ so we need to call inherited __new__ + super_new = super(klass, cls).__new__ + if super_new is object.__new__: + # object.__new__ doesn't accept args nor kwargs + instance = super_new(cls) + else: + instance = super_new(cls, *args, **kwargs) + else: + instance = orig_new(cls, *args, **kwargs) + if getattr(instance, '_DECORATED_METHODS_REGISTERED', False): + # Avoid running this logic twice for classes inheriting other + # classes with this same decorator. Only one needs to execute + # to subscribe all decorated methods. + return instance + for name, unbound_method in inspect.getmembers(cls): + if (not inspect.ismethod(unbound_method) and + not inspect.isfunction(unbound_method)): + continue + # Handle py27/py34 difference + method = getattr(unbound_method, 'im_func', unbound_method) + if method not in _DECORATED_EXTEND_METHODS: + continue + for resource in _DECORATED_EXTEND_METHODS[method]: + # Register the bound method for the resourse + register_funcs(resource, [method]) + setattr(instance, '_DECORATED_METHODS_REGISTERED', True) + return instance + klass.__new__ = replacement_new + return klass diff --git a/neutron/db/address_scope_db.py b/neutron/db/address_scope_db.py index 3ff6461a21e..5becc826595 100644 --- a/neutron/db/address_scope_db.py +++ b/neutron/db/address_scope_db.py @@ -26,6 +26,7 @@ from neutron.objects import base as base_obj from neutron.objects import subnetpool as subnetpool_obj +@resource_extend.has_resource_extenders class AddressScopeDbMixin(ext_address_scope.AddressScopePluginBase): """Mixin class to add address scope to db_base_plugin_v2.""" @@ -117,7 +118,9 @@ class AddressScopeDbMixin(ext_address_scope.AddressScopePluginBase): address_scope = self._get_address_scope(context, id) address_scope.delete() - def _extend_network_dict_address_scope(self, network_res, network_db): + @staticmethod + @resource_extend.extends([attr.NETWORKS]) + def _extend_network_dict_address_scope(network_res, network_db): network_res[ext_address_scope.IPV4_ADDRESS_SCOPE] = None network_res[ext_address_scope.IPV6_ADDRESS_SCOPE] = None subnetpools = {subnet.subnetpool for subnet in network_db.subnets @@ -132,6 +135,3 @@ class AddressScopeDbMixin(ext_address_scope.AddressScopePluginBase): if subnetpool['ip_version'] == constants.IP_VERSION_6: network_res[ext_address_scope.IPV6_ADDRESS_SCOPE] = as_id return network_res - - resource_extend.register_funcs( - attr.NETWORKS, ['_extend_network_dict_address_scope']) diff --git a/neutron/db/allowedaddresspairs_db.py b/neutron/db/allowedaddresspairs_db.py index ac123b88f6b..114b01142dd 100644 --- a/neutron/db/allowedaddresspairs_db.py +++ b/neutron/db/allowedaddresspairs_db.py @@ -26,6 +26,7 @@ from neutron.objects.port.extensions import (allowedaddresspairs as obj_addr_pair) +@resource_extend.has_resource_extenders class AllowedAddressPairsMixin(object): """Mixin class for allowed address pairs.""" @@ -63,19 +64,19 @@ class AllowedAddressPairsMixin(object): return [self._make_allowed_address_pairs_dict(pair.db_obj) for pair in pairs] - def _extend_port_dict_allowed_address_pairs(self, port_res, port_db): + @staticmethod + @resource_extend.extends([attr.PORTS]) + def _extend_port_dict_allowed_address_pairs(port_res, port_db): # If port_db is provided, allowed address pairs will be accessed via # sqlalchemy models. As they're loaded together with ports this # will not cause an extra query. allowed_address_pairs = [ - self._make_allowed_address_pairs_dict(address_pair) for + AllowedAddressPairsMixin._make_allowed_address_pairs_dict( + address_pair) for address_pair in port_db.allowed_address_pairs] port_res[addr_pair.ADDRESS_PAIRS] = allowed_address_pairs return port_res - resource_extend.register_funcs( - attr.PORTS, ['_extend_port_dict_allowed_address_pairs']) - def _delete_allowed_address_pairs(self, context, id): obj_addr_pair.AllowedAddressPair.delete_objects( context, port_id=id) diff --git a/neutron/db/availability_zone/network.py b/neutron/db/availability_zone/network.py index 422ed2ca7b3..6e9fccf0dcd 100644 --- a/neutron/db/availability_zone/network.py +++ b/neutron/db/availability_zone/network.py @@ -11,20 +11,23 @@ # License for the specific language governing permissions and limitations # under the License. +from neutron_lib.plugins import directory + from neutron.api.v2 import attributes from neutron.db import _resource_extend as resource_extend from neutron.extensions import availability_zone as az_ext from neutron.extensions import network_availability_zone as net_az +@resource_extend.has_resource_extenders class NetworkAvailabilityZoneMixin(net_az.NetworkAvailabilityZonePluginBase): """Mixin class to enable network's availability zone attributes.""" - def _extend_availability_zone(self, net_res, net_db): + @staticmethod + @resource_extend.extends([attributes.NETWORKS]) + def _extend_availability_zone(net_res, net_db): net_res[az_ext.AZ_HINTS] = az_ext.convert_az_string_to_list( net_db[az_ext.AZ_HINTS]) + plugin = directory.get_plugin() net_res[az_ext.AVAILABILITY_ZONES] = ( - self.get_network_availability_zones(net_db)) - - resource_extend.register_funcs( - attributes.NETWORKS, ['_extend_availability_zone']) + plugin.get_network_availability_zones(net_db)) diff --git a/neutron/db/availability_zone/router.py b/neutron/db/availability_zone/router.py index 681ef699da0..6a5d0966f0c 100644 --- a/neutron/db/availability_zone/router.py +++ b/neutron/db/availability_zone/router.py @@ -11,32 +11,33 @@ # License for the specific language governing permissions and limitations # under the License. +from neutron_lib import constants +from neutron_lib.plugins import directory + from neutron.callbacks import events from neutron.callbacks import registry from neutron.callbacks import resources from neutron.common import utils -from neutron.db import db_base_plugin_v2 +from neutron.db import _resource_extend as resource_extend from neutron.db import l3_attrs_db from neutron.extensions import availability_zone as az_ext from neutron.extensions import l3 +@resource_extend.has_resource_extenders @registry.has_registry_receivers class RouterAvailabilityZoneMixin(l3_attrs_db.ExtraAttributesMixin): """Mixin class to enable router's availability zone attributes.""" - def __new__(cls, *args, **kwargs): - inst = super(RouterAvailabilityZoneMixin, cls).__new__( - cls, *args, **kwargs) - db_base_plugin_v2.NeutronDbPluginV2.register_dict_extend_funcs( - l3.ROUTERS, [inst._add_az_to_response]) - return inst - - def _add_az_to_response(self, plugin, router_res, router_db): - if not utils.is_extension_supported(self, 'router_availability_zone'): + @staticmethod + @resource_extend.extends([l3.ROUTERS]) + def _add_az_to_response(router_res, router_db): + l3_plugin = directory.get_plugin(constants.L3) + if not utils.is_extension_supported(l3_plugin, + 'router_availability_zone'): return router_res['availability_zones'] = ( - self.get_router_availability_zones(router_db)) + l3_plugin.get_router_availability_zones(router_db)) @registry.receives(resources.ROUTER, [events.PRECOMMIT_CREATE]) def _process_az_request(self, resource, event, trigger, context, diff --git a/neutron/db/common_db_mixin.py b/neutron/db/common_db_mixin.py index c52427a8c53..bc20d05657c 100644 --- a/neutron/db/common_db_mixin.py +++ b/neutron/db/common_db_mixin.py @@ -17,7 +17,6 @@ import weakref from neutron_lib.db import utils as db_utils from oslo_db.sqlalchemy import utils as sa_utils -import six from sqlalchemy import and_ from sqlalchemy.ext import associationproxy from sqlalchemy import or_ @@ -36,6 +35,13 @@ model_query = ndb_utils.model_query resource_fields = ndb_utils.resource_fields +def _resolve_ref(ref): + """Handles dereference of weakref.""" + if isinstance(ref, weakref.ref): + ref = ref() + return ref + + class CommonDbMixin(object): """Common methods used in core and service plugins.""" @@ -85,11 +91,11 @@ class CommonDbMixin(object): query_filter = (model.tenant_id == context.tenant_id) # Execute query hooks registered from mixins and plugins for hook in _model_query.get_hooks(model): - query_hook = self._resolve_ref(hook.get('query')) + query_hook = _resolve_ref(hook.get('query')) if query_hook: query = query_hook(context, model, query) - filter_hook = self._resolve_ref(hook.get('filter')) + filter_hook = _resolve_ref(hook.get('filter')) if filter_hook: query_filter = filter_hook(context, model, query_filter) @@ -164,31 +170,19 @@ class CommonDbMixin(object): query = query.outerjoin(model.rbac_entries) query = query.filter(is_shared) for hook in _model_query.get_hooks(model): - result_filter = self._resolve_ref( - hook.get('result_filters', None)) + result_filter = _resolve_ref(hook.get('result_filters', None)) if result_filter: query = result_filter(query, filters) return query - def _resolve_ref(self, ref): - """Finds string ref functions, handles dereference of weakref.""" - if isinstance(ref, six.string_types): - ref = getattr(self, ref, None) - if isinstance(ref, weakref.ref): - ref = ref() - return ref - - def _apply_dict_extend_functions(self, resource_type, + @staticmethod + def _apply_dict_extend_functions(resource_type, response, db_object): for func in _resource_extend.get_funcs(resource_type): - args = (response, db_object) - if not isinstance(func, six.string_types): - # must call unbound method - use self as 1st argument - args = (self,) + args - func = self._resolve_ref(func) - if func: - func(*args) + resolved_func = _resolve_ref(func) + if resolved_func: + resolved_func(response, db_object) def _get_collection_query(self, context, model, filters=None, sorts=None, limit=None, marker_obj=None, diff --git a/neutron/db/data_plane_status_db.py b/neutron/db/data_plane_status_db.py index 4e5c23aefc2..92a87aa9b58 100644 --- a/neutron/db/data_plane_status_db.py +++ b/neutron/db/data_plane_status_db.py @@ -40,7 +40,8 @@ class DataPlaneStatusMixin(object): else: self._process_create_port_data_plane_status(context, data, res) - def _extend_port_data_plane_status(self, port_res, port_db): + @staticmethod + def _extend_port_data_plane_status(port_res, port_db): port_res[dps_lib.DATA_PLANE_STATUS] = None if port_db.get(dps_lib.DATA_PLANE_STATUS): diff --git a/neutron/db/dns_db.py b/neutron/db/dns_db.py index f71d2a4a74c..652284500ff 100644 --- a/neutron/db/dns_db.py +++ b/neutron/db/dns_db.py @@ -41,6 +41,7 @@ class DNSActionsData(object): self.previous_dns_domain = previous_dns_domain +@resource_extend.has_resource_extenders class DNSDbMixin(object): """Mixin class to add DNS methods to db_base_plugin_v2.""" @@ -63,7 +64,9 @@ class DNSDbMixin(object): raise dns.ExternalDNSDriverNotFound( driver=cfg.CONF.external_dns_driver) - def _extend_floatingip_dict_dns(self, floatingip_res, floatingip_db): + @staticmethod + @resource_extend.extends([l3.FLOATINGIPS]) + def _extend_floatingip_dict_dns(floatingip_res, floatingip_db): floatingip_res['dns_domain'] = '' floatingip_res['dns_name'] = '' if floatingip_db.dns: @@ -71,9 +74,6 @@ class DNSDbMixin(object): floatingip_res['dns_name'] = floatingip_db.dns['dns_name'] return floatingip_res - resource_extend.register_funcs( - l3.FLOATINGIPS, ['_extend_floatingip_dict_dns']) - def _process_dns_floatingip_create_precommit(self, context, floatingip_data, req_data): # expects to be called within a plugin's session diff --git a/neutron/db/external_net_db.py b/neutron/db/external_net_db.py index bed831228b6..d399821d8af 100644 --- a/neutron/db/external_net_db.py +++ b/neutron/db/external_net_db.py @@ -64,6 +64,7 @@ def _network_result_filter_hook(query, filters): return query.filter(~models_v2.Network.external.has()) +@resource_extend.has_resource_extenders @registry.has_registry_receivers class External_net_db_mixin(object): """Mixin class to add external network methods to db_base_plugin_v2.""" @@ -81,14 +82,13 @@ class External_net_db_mixin(object): return net_obj.ExternalNetwork.objects_exist( context, network_id=net_id) - def _extend_network_dict_l3(self, network_res, network_db): + @staticmethod + @resource_extend.extends([attributes.NETWORKS]) + def _extend_network_dict_l3(network_res, network_db): # Comparing with None for converting uuid into bool network_res[external_net.EXTERNAL] = network_db.external is not None return network_res - resource_extend.register_funcs( - attributes.NETWORKS, ['_extend_network_dict_l3']) - def _process_l3_create(self, context, net_data, req_data): external = req_data.get(external_net.EXTERNAL) external_set = validators.is_attr_set(external) diff --git a/neutron/db/extradhcpopt_db.py b/neutron/db/extradhcpopt_db.py index 409fed60ef8..beceb54d793 100644 --- a/neutron/db/extradhcpopt_db.py +++ b/neutron/db/extradhcpopt_db.py @@ -20,6 +20,7 @@ from neutron.extensions import extra_dhcp_opt as edo_ext from neutron.objects.port.extensions import extra_dhcp_opt as obj_extra_dhcp +@resource_extend.has_resource_extenders class ExtraDhcpOptMixin(object): """Mixin class to add extra options to the DHCP opts file and associate them to a port. @@ -114,12 +115,11 @@ class ExtraDhcpOptMixin(object): return bool(dopts) - def _extend_port_dict_extra_dhcp_opt(self, res, port): + @staticmethod + @resource_extend.extends([attributes.PORTS]) + def _extend_port_dict_extra_dhcp_opt(res, port): res[edo_ext.EXTRADHCPOPTS] = [{'opt_name': dho.opt_name, 'opt_value': dho.opt_value, 'ip_version': dho.ip_version} for dho in port.dhcp_opts] return res - - resource_extend.register_funcs( - attributes.PORTS, ['_extend_port_dict_extra_dhcp_opt']) diff --git a/neutron/db/extraroute_db.py b/neutron/db/extraroute_db.py index 4a781e2cea8..d18597a79d5 100644 --- a/neutron/db/extraroute_db.py +++ b/neutron/db/extraroute_db.py @@ -39,18 +39,18 @@ extra_route_opts = [ cfg.CONF.register_opts(extra_route_opts) +@resource_extend.has_resource_extenders class ExtraRoute_dbonly_mixin(l3_db.L3_NAT_dbonly_mixin): """Mixin class to support extra route configuration on router.""" - def _extend_router_dict_extraroute(self, router_res, router_db): + @staticmethod + @resource_extend.extends([l3.ROUTERS]) + def _extend_router_dict_extraroute(router_res, router_db): router_res['routes'] = (ExtraRoute_dbonly_mixin. _make_extra_route_list( router_db['route_list'] )) - resource_extend.register_funcs( - l3.ROUTERS, ['_extend_router_dict_extraroute']) - def update_router(self, context, id, router): r = router['router'] if 'routes' in r: diff --git a/neutron/db/l3_attrs_db.py b/neutron/db/l3_attrs_db.py index 5d8b0efa92a..ec3bae78eef 100644 --- a/neutron/db/l3_attrs_db.py +++ b/neutron/db/l3_attrs_db.py @@ -33,10 +33,13 @@ def get_attr_info(): } +@resource_extend.has_resource_extenders class ExtraAttributesMixin(object): """Mixin class to enable router's extra attributes.""" - def _extend_extra_router_dict(self, router_res, router_db): + @staticmethod + @resource_extend.extends([l3.ROUTERS]) + def _extend_extra_router_dict(router_res, router_db): extra_attrs = router_db['extra_attributes'] or {} for name, info in get_attr_info().items(): from_db = info.get('transform_from_db', lambda x: x) @@ -61,6 +64,3 @@ class ExtraAttributesMixin(object): return raise RuntimeError(_("Tried to set a key '%s' that doesn't exist " "in the extra attributes table.") % key) - - resource_extend.register_funcs( - l3.ROUTERS, ['_extend_extra_router_dict']) diff --git a/neutron/db/l3_gwmode_db.py b/neutron/db/l3_gwmode_db.py index ff44c29e424..8987dd133ba 100644 --- a/neutron/db/l3_gwmode_db.py +++ b/neutron/db/l3_gwmode_db.py @@ -38,13 +38,13 @@ setattr(l3_models.Router, 'enable_snat', nullable=False)) +@resource_extend.has_resource_extenders class L3_NAT_dbonly_mixin(l3_db.L3_NAT_dbonly_mixin): """Mixin class to add configurable gateway modes.""" - resource_extend.register_funcs( - l3.ROUTERS, ['_extend_router_dict_gw_mode']) - - def _extend_router_dict_gw_mode(self, router_res, router_db): + @staticmethod + @resource_extend.extends([l3.ROUTERS]) + def _extend_router_dict_gw_mode(router_res, router_db): if router_db.gw_port_id: nw_id = router_db.gw_port['network_id'] router_res[EXTERNAL_GW_INFO] = { diff --git a/neutron/db/portbindings_base.py b/neutron/db/portbindings_base.py index c9b51a7fc96..d580029ea04 100644 --- a/neutron/db/portbindings_base.py +++ b/neutron/db/portbindings_base.py @@ -13,11 +13,16 @@ # License for the specific language governing permissions and limitations # under the License. +from neutron_lib.plugins import directory + from neutron.api.v2 import attributes from neutron.db import _resource_extend as resource_extend +@resource_extend.has_resource_extenders class PortBindingBaseMixin(object): + + # Initialized by core plugin or ml2 mechanism driver(s) base_binding_dict = None def _process_portbindings_create_and_update(self, context, port_data, @@ -28,13 +33,10 @@ class PortBindingBaseMixin(object): if self.base_binding_dict: port_res.update(self.base_binding_dict) - -def _extend_port_dict_binding(plugin, port_res, port_db): - if not isinstance(plugin, PortBindingBaseMixin): - return - plugin.extend_port_dict_binding(port_res, port_db) - - -def register_port_dict_function(): - resource_extend.register_funcs( - attributes.PORTS, [_extend_port_dict_binding]) + @staticmethod + @resource_extend.extends([attributes.PORTS]) + def _extend_port_dict_binding(port_res, port_db): + plugin = directory.get_plugin() + if not isinstance(plugin, PortBindingBaseMixin): + return + plugin.extend_port_dict_binding(port_res, port_db) diff --git a/neutron/db/portbindings_db.py b/neutron/db/portbindings_db.py index 65832f633a3..4de5b2ad7ba 100644 --- a/neutron/db/portbindings_db.py +++ b/neutron/db/portbindings_db.py @@ -15,6 +15,7 @@ from neutron_lib.api.definitions import portbindings from neutron_lib.api import validators +from neutron_lib.plugins import directory from neutron.api.v2 import attributes from neutron.db import _model_query as model_query @@ -40,8 +41,8 @@ def _port_result_filter_hook(query, filters): return query +@resource_extend.has_resource_extenders class PortBindingMixin(portbindings_base.PortBindingBaseMixin): - extra_binding_dict = None def __new__(cls, *args, **kwargs): model_query.register_hook( @@ -101,11 +102,10 @@ class PortBindingMixin(portbindings_base.PortBindingBaseMixin): host = port_db.portbinding.host if port_db.portbinding else None self._extend_port_dict_binding_host(port_res, host) - -def _extend_port_dict_binding(plugin, port_res, port_db): - if not isinstance(plugin, PortBindingMixin): - return - plugin.extend_port_dict_binding(port_res, port_db) - - -resource_extend.register_funcs(attributes.PORTS, [_extend_port_dict_binding]) + @staticmethod + @resource_extend.extends([attributes.PORTS]) + def _extend_port_dict_binding(port_res, port_db): + plugin = directory.get_plugin() + if not isinstance(plugin, PortBindingMixin): + return + plugin.extend_port_dict_binding(port_res, port_db) diff --git a/neutron/db/portsecurity_db.py b/neutron/db/portsecurity_db.py index bd551270bbf..0129b8573e1 100644 --- a/neutron/db/portsecurity_db.py +++ b/neutron/db/portsecurity_db.py @@ -13,6 +13,7 @@ # under the License. from neutron_lib.api import validators +from neutron_lib.plugins import directory from neutron.api.v2 import attributes as attrs from neutron.common import utils @@ -21,16 +22,16 @@ from neutron.db import portsecurity_db_common from neutron.extensions import portsecurity as psec +@resource_extend.has_resource_extenders class PortSecurityDbMixin(portsecurity_db_common.PortSecurityDbCommon): - resource_extend.register_funcs( - attrs.NETWORKS, ['_extend_port_security_dict']) - resource_extend.register_funcs( - attrs.PORTS, ['_extend_port_security_dict']) - def _extend_port_security_dict(self, response_data, db_data): + @staticmethod + @resource_extend.extends([attrs.NETWORKS, attrs.PORTS]) + def _extend_port_security_dict(response_data, db_data): + plugin = directory.get_plugin() if ('port-security' in - getattr(self, 'supported_extension_aliases', [])): - super(PortSecurityDbMixin, self)._extend_port_security_dict( + getattr(plugin, 'supported_extension_aliases', [])): + super(PortSecurityDbMixin, plugin)._extend_port_security_dict( response_data, db_data) def _determine_port_security_and_has_ip(self, context, port): diff --git a/neutron/db/portsecurity_db_common.py b/neutron/db/portsecurity_db_common.py index 9e319f35893..566f254be6c 100644 --- a/neutron/db/portsecurity_db_common.py +++ b/neutron/db/portsecurity_db_common.py @@ -21,7 +21,8 @@ from neutron.objects.port.extensions import port_security as p_ps class PortSecurityDbCommon(object): """Mixin class to add port security.""" - def _extend_port_security_dict(self, response_data, db_data): + @staticmethod + def _extend_port_security_dict(response_data, db_data): if db_data.get('port_security') is None: response_data[psec.PORTSECURITY] = psec.DEFAULT_PORT_SECURITY else: diff --git a/neutron/db/securitygroups_db.py b/neutron/db/securitygroups_db.py index ad89b6cb7c2..95f679f0bcb 100644 --- a/neutron/db/securitygroups_db.py +++ b/neutron/db/securitygroups_db.py @@ -35,6 +35,7 @@ from neutron.db.models import securitygroup as sg_models from neutron.extensions import securitygroup as ext_sg +@resource_extend.has_resource_extenders @registry.has_registry_receivers class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase): """Mixin class to add security group to db_base_plugin_v2.""" @@ -662,7 +663,9 @@ class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase): resources.SECURITY_GROUP_RULE, events.AFTER_DELETE, self, **kwargs) - def _extend_port_dict_security_group(self, port_res, port_db): + @staticmethod + @resource_extend.extends([attributes.PORTS]) + def _extend_port_dict_security_group(port_res, port_db): # Security group bindings will be retrieved from the SQLAlchemy # model. As they're loaded eagerly with ports because of the # joined load they will not cause an extra query. @@ -671,9 +674,6 @@ class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase): port_res[ext_sg.SECURITYGROUPS] = security_group_ids return port_res - resource_extend.register_funcs( - attributes.PORTS, ['_extend_port_dict_security_group']) - def _process_port_create_security_group(self, context, port, security_group_ids): if validators.is_attr_set(security_group_ids): diff --git a/neutron/db/standardattrdescription_db.py b/neutron/db/standardattrdescription_db.py index 98548ece5b4..f2294dea933 100644 --- a/neutron/db/standardattrdescription_db.py +++ b/neutron/db/standardattrdescription_db.py @@ -16,17 +16,14 @@ from neutron.db import _resource_extend as resource_extend from neutron.db import standard_attr +@resource_extend.has_resource_extenders class StandardAttrDescriptionMixin(object): supported_extension_aliases = ['standard-attr-description'] - def _extend_standard_attr_description(self, res, db_object): + @staticmethod + @resource_extend.extends( + list(standard_attr.get_standard_attr_resource_model_map())) + def _extend_standard_attr_description(res, db_object): if not hasattr(db_object, 'description'): return res['description'] = db_object.description - - def __new__(cls, *args, **kwargs): - for resource in standard_attr.get_standard_attr_resource_model_map(): - resource_extend.register_funcs( - resource, ['_extend_standard_attr_description']) - return super(StandardAttrDescriptionMixin, cls).__new__(cls, *args, - **kwargs) diff --git a/neutron/db/subnet_service_type_db_models.py b/neutron/db/subnet_service_type_db_models.py index 60f2be95a69..22e77d89998 100644 --- a/neutron/db/subnet_service_type_db_models.py +++ b/neutron/db/subnet_service_type_db_models.py @@ -20,13 +20,13 @@ from neutron.api.v2 import attributes from neutron.db import _resource_extend as resource_extend +@resource_extend.has_resource_extenders class SubnetServiceTypeMixin(object): """Mixin class to extend subnet with service type attribute""" - def _extend_subnet_service_types(self, subnet_res, subnet_db): + @staticmethod + @resource_extend.extends([attributes.SUBNETS]) + def _extend_subnet_service_types(subnet_res, subnet_db): subnet_res['service_types'] = [service_type['service_type'] for service_type in subnet_db.service_types] - - resource_extend.register_funcs( - attributes.SUBNETS, [_extend_subnet_service_types]) diff --git a/neutron/db/vlantransparent_db.py b/neutron/db/vlantransparent_db.py index 1af6ad4bbf1..33f76748077 100644 --- a/neutron/db/vlantransparent_db.py +++ b/neutron/db/vlantransparent_db.py @@ -17,13 +17,13 @@ from neutron.db import _resource_extend as resource_extend from neutron.extensions import vlantransparent +@resource_extend.has_resource_extenders class Vlantransparent_db_mixin(object): """Mixin class to add vlan transparent methods to db_base_plugin_v2.""" - def _extend_network_dict_vlan_transparent(self, network_res, network_db): + @staticmethod + @resource_extend.extends([attributes.NETWORKS]) + def _extend_network_dict_vlan_transparent(network_res, network_db): network_res[vlantransparent.VLANTRANSPARENT] = ( network_db.vlan_transparent) return network_res - - resource_extend.register_funcs( - attributes.NETWORKS, ['_extend_network_dict_vlan_transparent']) diff --git a/neutron/plugins/ml2/plugin.py b/neutron/plugins/ml2/plugin.py index d18a9cf6eef..9d64d924735 100644 --- a/neutron/plugins/ml2/plugin.py +++ b/neutron/plugins/ml2/plugin.py @@ -109,6 +109,7 @@ def _ml2_port_result_filter_hook(query, filters): return query.filter(models_v2.Port.port_binding.has(bind_criteria)) +@resource_extend.has_resource_extenders @registry.has_registry_receivers class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2, dvr_mac_db.DVRDbMixin, @@ -602,34 +603,37 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2, 'port': binding.port_id}) return {} - def _ml2_extend_port_dict_binding(self, port_res, port_db): + @staticmethod + @resource_extend.extends([attributes.PORTS]) + def _ml2_extend_port_dict_binding(port_res, port_db): + plugin = directory.get_plugin() # None when called during unit tests for other plugins. if port_db.port_binding: - self._update_port_dict_binding(port_res, port_db.port_binding) - - resource_extend.register_funcs( - attributes.PORTS, ['_ml2_extend_port_dict_binding']) + plugin._update_port_dict_binding(port_res, port_db.port_binding) # ML2's resource extend functions allow extension drivers that extend # attributes for the resources to add those attributes to the result. - resource_extend.register_funcs( - attributes.NETWORKS, ['_ml2_md_extend_network_dict']) - resource_extend.register_funcs( - attributes.PORTS, ['_ml2_md_extend_port_dict']) - resource_extend.register_funcs( - attributes.SUBNETS, ['_ml2_md_extend_subnet_dict']) - def _ml2_md_extend_network_dict(self, result, netdb): - session = self._object_session_or_new_session(netdb) - self.extension_manager.extend_network_dict(session, netdb, result) + @staticmethod + @resource_extend.extends([attributes.NETWORKS]) + def _ml2_md_extend_network_dict(result, netdb): + plugin = directory.get_plugin() + session = plugin._object_session_or_new_session(netdb) + plugin.extension_manager.extend_network_dict(session, netdb, result) - def _ml2_md_extend_port_dict(self, result, portdb): - session = self._object_session_or_new_session(portdb) - self.extension_manager.extend_port_dict(session, portdb, result) + @staticmethod + @resource_extend.extends([attributes.PORTS]) + def _ml2_md_extend_port_dict(result, portdb): + plugin = directory.get_plugin() + session = plugin._object_session_or_new_session(portdb) + plugin.extension_manager.extend_port_dict(session, portdb, result) - def _ml2_md_extend_subnet_dict(self, result, subnetdb): - session = self._object_session_or_new_session(subnetdb) - self.extension_manager.extend_subnet_dict(session, subnetdb, result) + @staticmethod + @resource_extend.extends([attributes.SUBNETS]) + def _ml2_md_extend_subnet_dict(result, subnetdb): + plugin = directory.get_plugin() + session = plugin._object_session_or_new_session(subnetdb) + plugin.extension_manager.extend_subnet_dict(session, subnetdb, result) @staticmethod def _object_session_or_new_session(sql_obj): diff --git a/neutron/services/auto_allocate/db.py b/neutron/services/auto_allocate/db.py index 40f6942be67..099544f9ae3 100644 --- a/neutron/services/auto_allocate/db.py +++ b/neutron/services/auto_allocate/db.py @@ -42,13 +42,6 @@ IS_DEFAULT = 'is_default' CHECK_REQUIREMENTS = 'dry-run' -def _extend_external_network_default(core_plugin, net_res, net_db): - """Add is_default field to 'show' response.""" - if net_db.external is not None: - net_res[IS_DEFAULT] = net_db.external.is_default - return net_res - - @db_api.retry_if_session_inactive() def _ensure_external_network_default_value_callback( resource, event, trigger, context, request, network, **kwargs): @@ -78,11 +71,9 @@ def _ensure_external_network_default_value_callback( obj.update() +@resource_extend.has_resource_extenders class AutoAllocatedTopologyMixin(common_db_mixin.CommonDbMixin): - resource_extend.register_funcs( - attributes.NETWORKS, [_extend_external_network_default]) - def __new__(cls, *args, **kwargs): # NOTE(kevinbenton): we subscribe on object construction because # the tests blow away the callback manager for each run @@ -116,6 +107,14 @@ class AutoAllocatedTopologyMixin(common_db_mixin.CommonDbMixin): self._l3_plugin = directory.get_plugin(constants.L3) return self._l3_plugin + @staticmethod + @resource_extend.extends([attributes.NETWORKS]) + def _extend_external_network_default(net_res, net_db): + """Add is_default field to 'show' response.""" + if net_db.external is not None: + net_res[IS_DEFAULT] = net_db.external.is_default + return net_res + def get_auto_allocated_topology(self, context, tenant_id, fields=None): """Return tenant's network associated to auto-allocated topology. diff --git a/neutron/services/l3_router/l3_router_plugin.py b/neutron/services/l3_router/l3_router_plugin.py index c2ab0a69a71..cbf893aff33 100644 --- a/neutron/services/l3_router/l3_router_plugin.py +++ b/neutron/services/l3_router/l3_router_plugin.py @@ -51,6 +51,7 @@ def disable_dvr_extension_by_config(aliases): aliases.remove('dvr') +@resource_extend.has_resource_extenders class L3RouterPlugin(service_base.ServicePluginBase, common_db_mixin.CommonDbMixin, extraroute_db.ExtraRoute_db_mixin, @@ -141,10 +142,7 @@ class L3RouterPlugin(service_base.ServicePluginBase, context, floatingip, initial_status=n_const.FLOATINGIP_STATUS_DOWN) - -def add_flavor_id(plugin, router_res, router_db): - router_res['flavor_id'] = router_db['flavor_id'] - - -resource_extend.register_funcs( - l3.ROUTERS, [add_flavor_id]) + @staticmethod + @resource_extend.extends([l3.ROUTERS]) + def add_flavor_id(router_res, router_db): + router_res['flavor_id'] = router_db['flavor_id'] diff --git a/neutron/services/revisions/revision_plugin.py b/neutron/services/revisions/revision_plugin.py index 8a3bda06d8f..4aa9fa358b0 100644 --- a/neutron/services/revisions/revision_plugin.py +++ b/neutron/services/revisions/revision_plugin.py @@ -25,6 +25,7 @@ from neutron.db import standard_attr LOG = logging.getLogger(__name__) +@resource_extend.has_resource_extenders class RevisionPlugin(service_base.ServicePluginBase): """Plugin to populate revision numbers into standard attr resources.""" @@ -32,9 +33,6 @@ class RevisionPlugin(service_base.ServicePluginBase): def __init__(self): super(RevisionPlugin, self).__init__() - for resource in standard_attr.get_standard_attr_resource_model_map(): - resource_extend.register_funcs( - resource, [self.extend_resource_dict_revision]) db_api.sqla_listen(se.Session, 'before_flush', self.bump_revisions) def bump_revisions(self, session, context, instances): @@ -76,7 +74,10 @@ class RevisionPlugin(service_base.ServicePluginBase): def get_plugin_description(self): return "Adds revision numbers to resources." - def extend_resource_dict_revision(self, plugin, resource_res, resource_db): + @staticmethod + @resource_extend.extends( + list(standard_attr.get_standard_attr_resource_model_map())) + def extend_resource_dict_revision(resource_res, resource_db): resource_res['revision_number'] = resource_db.revision_number def _find_related_obj(self, session, obj, relationship_col): diff --git a/neutron/services/segments/plugin.py b/neutron/services/segments/plugin.py index b4167553c11..4734225dd15 100644 --- a/neutron/services/segments/plugin.py +++ b/neutron/services/segments/plugin.py @@ -49,30 +49,7 @@ SEGMENT_NAME_STUB = 'Neutron segment id %s' MAX_INVENTORY_UPDATE_RETRIES = 10 -def _extend_network_dict_binding(plugin, network_res, network_db): - if not directory.get_plugin('segments'): - return - - # TODO(carl_baldwin) Make this work with service subnets when it's a thing. - is_adjacent = (not network_db.subnets - or not network_db.subnets[0].segment_id) - network_res[l2_adjacency.L2_ADJACENCY] = is_adjacent - - -def _extend_subnet_dict_binding(plugin, subnet_res, subnet_db): - subnet_res['segment_id'] = subnet_db.get('segment_id') - - -def _extend_port_dict_binding(plugin, port_res, port_db): - if not directory.get_plugin('segments'): - return - - value = ip_allocation.IP_ALLOCATION_IMMEDIATE - if port_db.get('ip_allocation'): - value = port_db.get('ip_allocation') - port_res[ip_allocation.IP_ALLOCATION] = value - - +@resource_extend.has_resource_extenders @registry.has_registry_receivers class Plugin(db.SegmentDbMixin, segment.SegmentPluginBase): @@ -81,14 +58,36 @@ class Plugin(db.SegmentDbMixin, segment.SegmentPluginBase): supported_extension_aliases = ["segment", "ip_allocation", "l2_adjacency"] def __init__(self): - resource_extend.register_funcs( - attributes.NETWORKS, [_extend_network_dict_binding]) - resource_extend.register_funcs( - attributes.SUBNETS, [_extend_subnet_dict_binding]) - resource_extend.register_funcs( - attributes.PORTS, [_extend_port_dict_binding]) self.nova_updater = NovaSegmentNotifier() + @staticmethod + @resource_extend.extends([attributes.NETWORKS]) + def _extend_network_dict_binding(network_res, network_db): + if not directory.get_plugin('segments'): + return + + # TODO(carl_baldwin) Make this work with service subnets when + # it's a thing. + is_adjacent = (not network_db.subnets + or not network_db.subnets[0].segment_id) + network_res[l2_adjacency.L2_ADJACENCY] = is_adjacent + + @staticmethod + @resource_extend.extends([attributes.SUBNETS]) + def _extend_subnet_dict_binding(subnet_res, subnet_db): + subnet_res['segment_id'] = subnet_db.get('segment_id') + + @staticmethod + @resource_extend.extends([attributes.PORTS]) + def _extend_port_dict_binding(port_res, port_db): + if not directory.get_plugin('segments'): + return + + value = ip_allocation.IP_ALLOCATION_IMMEDIATE + if port_db.get('ip_allocation'): + value = port_db.get('ip_allocation') + port_res[ip_allocation.IP_ALLOCATION] = value + @classmethod def get_instance(cls): if cls._instance is None: diff --git a/neutron/services/tag/tag_plugin.py b/neutron/services/tag/tag_plugin.py index b7978e717d1..b81e1331e94 100644 --- a/neutron/services/tag/tag_plugin.py +++ b/neutron/services/tag/tag_plugin.py @@ -44,13 +44,7 @@ resource_model_map = { } -def _extend_tags_dict(plugin, response_data, db_data): - if not directory.get_plugin(tag_ext.TAG_PLUGIN_TYPE): - return - tags = [tag_db.tag for tag_db in db_data.standard_attr.tags] - response_data['tags'] = tags - - +@resource_extend.has_resource_extenders class TagPlugin(common_db_mixin.CommonDbMixin, tag_ext.TagPluginBase): """Implementation of the Neutron Tag Service Plugin.""" @@ -59,8 +53,7 @@ class TagPlugin(common_db_mixin.CommonDbMixin, tag_ext.TagPluginBase): def __new__(cls, *args, **kwargs): inst = super(TagPlugin, cls).__new__(cls, *args, **kwargs) inst._filter_methods = [] # prevent GC of our partial functions - for resource, model in resource_model_map.items(): - resource_extend.register_funcs(resource, [_extend_tags_dict]) + for model in resource_model_map.values(): method = functools.partial(tag_methods.apply_tag_filters, model) inst._filter_methods.append(method) model_query.register_hook(model, "tag", @@ -69,6 +62,14 @@ class TagPlugin(common_db_mixin.CommonDbMixin, tag_ext.TagPluginBase): result_filters=method) return inst + @staticmethod + @resource_extend.extends(list(resource_model_map)) + def _extend_tags_dict(response_data, db_data): + if not directory.get_plugin(tag_ext.TAG_PLUGIN_TYPE): + return + tags = [tag_db.tag for tag_db in db_data.standard_attr.tags] + response_data['tags'] = tags + def _get_resource(self, context, resource, resource_id): model = resource_model_map[resource] try: diff --git a/neutron/services/timestamp/timestamp_db.py b/neutron/services/timestamp/timestamp_db.py index fc93768f3a8..d1602939807 100644 --- a/neutron/services/timestamp/timestamp_db.py +++ b/neutron/services/timestamp/timestamp_db.py @@ -70,12 +70,6 @@ def _format_timestamp(resource_db, result): strftime(TIME_FORMAT_WHOLE_SECONDS)) + 'Z' -def _extend_resource_dict_timestamp(plugin_obj, resource_res, resource_db): - if (resource_db and resource_db.created_at and - resource_db.updated_at): - _format_timestamp(resource_db, resource_res) - - def _add_timestamp(mapper, _conn, target): if not target.created_at and not target.updated_at: time = timeutils.utcnow() @@ -84,14 +78,13 @@ def _add_timestamp(mapper, _conn, target): return target +@resource_extend.has_resource_extenders class TimeStamp_db_mixin(object): """Mixin class to add Time Stamp methods.""" def __new__(cls, *args, **kwargs): rs_model_maps = standard_attr.get_standard_attr_resource_model_map() - for rsmap, model in rs_model_maps.items(): - resource_extend.register_funcs( - rsmap, [_extend_resource_dict_timestamp]) + for model in rs_model_maps.values(): model_query.register_hook( model, "change_since_query", @@ -105,3 +98,11 @@ class TimeStamp_db_mixin(object): listen(standard_attr.StandardAttribute, 'before_insert', _add_timestamp) listen(se.Session, 'before_flush', _update_timestamp) + + @staticmethod + @resource_extend.extends( + list(standard_attr.get_standard_attr_resource_model_map())) + def _extend_resource_dict_timestamp(resource_res, resource_db): + if (resource_db and resource_db.created_at and + resource_db.updated_at): + _format_timestamp(resource_db, resource_res) diff --git a/neutron/services/trunk/plugin.py b/neutron/services/trunk/plugin.py index 75e57eef0e9..8710e862c85 100644 --- a/neutron/services/trunk/plugin.py +++ b/neutron/services/trunk/plugin.py @@ -16,6 +16,7 @@ import copy from neutron_lib.api.definitions import portbindings from neutron_lib import context +from neutron_lib.plugins import directory from neutron_lib.services import base as service_base from oslo_log import log as logging from oslo_utils import uuidutils @@ -40,26 +41,7 @@ from neutron.services.trunk.seg_types import validators LOG = logging.getLogger(__name__) -def _extend_port_trunk_details(core_plugin, port_res, port_db): - """Add trunk details to a port.""" - if port_db.trunk_port: - subports = { - x.port_id: {'segmentation_id': x.segmentation_id, - 'segmentation_type': x.segmentation_type, - 'port_id': x.port_id} - for x in port_db.trunk_port.sub_ports - } - ports = core_plugin.get_ports( - context.get_admin_context(), filters={'id': subports}) - for port in ports: - subports[port['id']]['mac_address'] = port['mac_address'] - trunk_details = {'trunk_id': port_db.trunk_port.id, - 'sub_ports': [x for x in subports.values()]} - port_res['trunk_details'] = trunk_details - - return port_res - - +@resource_extend.has_resource_extenders @registry.has_registry_receivers class TrunkPlugin(service_base.ServicePluginBase, common_db_mixin.CommonDbMixin): @@ -70,8 +52,6 @@ class TrunkPlugin(service_base.ServicePluginBase, __native_sorting_support = True def __init__(self): - resource_extend.register_funcs( - attributes.PORTS, [_extend_port_trunk_details]) self._rpc_backend = None self._drivers = [] self._segmentation_types = {} @@ -85,6 +65,28 @@ class TrunkPlugin(service_base.ServicePluginBase, LOG.debug('Trunk plugin loaded with driver %s', driver.name) self.check_compatibility() + @staticmethod + @resource_extend.extends([attributes.PORTS]) + def _extend_port_trunk_details(port_res, port_db): + """Add trunk details to a port.""" + if port_db.trunk_port: + subports = { + x.port_id: {'segmentation_id': x.segmentation_id, + 'segmentation_type': x.segmentation_type, + 'port_id': x.port_id} + for x in port_db.trunk_port.sub_ports + } + core_plugin = directory.get_plugin() + ports = core_plugin.get_ports( + context.get_admin_context(), filters={'id': subports}) + for port in ports: + subports[port['id']]['mac_address'] = port['mac_address'] + trunk_details = {'trunk_id': port_db.trunk_port.id, + 'sub_ports': [x for x in subports.values()]} + port_res['trunk_details'] = trunk_details + + return port_res + def check_compatibility(self): """Verify the plugin can load correctly and fail otherwise.""" self.check_driver_compatibility() diff --git a/neutron/tests/base.py b/neutron/tests/base.py index 944c4fc66f5..4a4afcb3665 100644 --- a/neutron/tests/base.py +++ b/neutron/tests/base.py @@ -46,6 +46,7 @@ from neutron.callbacks import manager as registry_manager from neutron.common import config from neutron.common import rpc as n_rpc from neutron.db import _model_query as model_query +from neutron.db import _resource_extend as resource_extend from neutron.db import agentschedulers_db from neutron.db import api as db_api from neutron import manager @@ -167,6 +168,7 @@ class DietTestCase(base.BaseTestCase): self.addCleanup(mock.patch.stopall) self.addCleanup(self.reset_model_query_hooks) + self.addCleanup(self.reset_resource_extend_functions) self.addOnException(self.check_for_systemexit) self.orig_pid = os.getpid() @@ -177,6 +179,10 @@ class DietTestCase(base.BaseTestCase): def reset_model_query_hooks(): model_query._model_query_hooks = {} + @staticmethod + def reset_resource_extend_functions(): + resource_extend._resource_extend_functions = {} + def addOnException(self, handler): def safe_handler(*args, **kwargs): diff --git a/neutron/tests/functional/scheduler/test_l3_agent_scheduler.py b/neutron/tests/functional/scheduler/test_l3_agent_scheduler.py index 9231b8285cb..262ac8481f7 100644 --- a/neutron/tests/functional/scheduler/test_l3_agent_scheduler.py +++ b/neutron/tests/functional/scheduler/test_l3_agent_scheduler.py @@ -18,6 +18,7 @@ import random from neutron_lib import constants from neutron_lib import context +from neutron_lib.plugins import directory from oslo_utils import uuidutils import testscenarios @@ -45,6 +46,7 @@ class L3SchedulerBaseTest(test_db_base_plugin_v2.NeutronDbPluginV2TestCase): super(L3SchedulerBaseTest, self).setUp(PLUGIN_NAME) self.l3_plugin = l3_router_plugin.L3RouterPlugin() + directory.add_plugin(constants.L3, self.l3_plugin) self.adminContext = context.get_admin_context() self.adminContext.tenant_id = _uuid() @@ -296,6 +298,7 @@ class L3AZSchedulerBaseTest(test_db_base_plugin_v2.NeutronDbPluginV2TestCase): super(L3AZSchedulerBaseTest, self).setUp(plugin='ml2') self.l3_plugin = l3_router_plugin.L3RouterPlugin() + directory.add_plugin(constants.L3, self.l3_plugin) self.l3_plugin.router_scheduler = None self.adminContext = context.get_admin_context() self.adminContext.tenant_id = '_func_test_tenant_' diff --git a/neutron/tests/unit/db/test_l3_dvr_db.py b/neutron/tests/unit/db/test_l3_dvr_db.py index 8d8a7b7b917..23461e2d4b6 100644 --- a/neutron/tests/unit/db/test_l3_dvr_db.py +++ b/neutron/tests/unit/db/test_l3_dvr_db.py @@ -48,6 +48,7 @@ class L3DvrTestCase(test_db_base_plugin_v2.NeutronDbPluginV2TestCase): self.core_plugin = directory.get_plugin() self.ctx = context.get_admin_context() self.mixin = FakeL3Plugin() + directory.add_plugin(const.L3, self.mixin) def _create_router(self, router): with self.ctx.session.begin(subtransactions=True): diff --git a/neutron/tests/unit/db/test_portsecurity_db.py b/neutron/tests/unit/db/test_portsecurity_db.py index 5ed483070f1..e164cc5be91 100644 --- a/neutron/tests/unit/db/test_portsecurity_db.py +++ b/neutron/tests/unit/db/test_portsecurity_db.py @@ -11,6 +11,8 @@ # under the License. import mock +from neutron_lib import constants +from neutron_lib.plugins import directory from neutron.db import portsecurity_db as pd from neutron.db import portsecurity_db_common as pdc @@ -29,6 +31,7 @@ class PortSecurityDbMixinTestCase(base.BaseTestCase): def setUp(self): super(PortSecurityDbMixinTestCase, self).setUp() self.plugin = FakePlugin() + directory.add_plugin(constants.CORE, self.plugin) @mock.patch.object(common, '_extend_port_security_dict') def test__extend_port_security_dict_relies_on_common(self, extend): diff --git a/neutron/tests/unit/extensions/test_data_plane_status.py b/neutron/tests/unit/extensions/test_data_plane_status.py index 40ceb8ebcf4..edbc0a0fa3c 100644 --- a/neutron/tests/unit/extensions/test_data_plane_status.py +++ b/neutron/tests/unit/extensions/test_data_plane_status.py @@ -41,11 +41,18 @@ class DataPlaneStatusTestExtensionManager(object): return dps_ext.Data_plane_status.get_extended_resources(version) +@resource_extend.has_resource_extenders class DataPlaneStatusExtensionTestPlugin(db_base_plugin_v2.NeutronDbPluginV2, dps_db.DataPlaneStatusMixin): supported_extension_aliases = ["data-plane-status"] + @staticmethod + @resource_extend.extends([attrs.PORTS]) + def _extend_port_data_plane_status(port_res, port_db): + return dps_db.DataPlaneStatusMixin._extend_port_data_plane_status( + port_res, port_db) + def update_port(self, context, id, port): with context.session.begin(subtransactions=True): ret_port = super(DataPlaneStatusExtensionTestPlugin, @@ -56,9 +63,6 @@ class DataPlaneStatusExtensionTestPlugin(db_base_plugin_v2.NeutronDbPluginV2, ret_port) return ret_port - resource_extend.register_funcs(attrs.PORTS, - ['_extend_port_data_plane_status']) - class DataPlaneStatusExtensionTestCase( test_db_base_plugin_v2.NeutronDbPluginV2TestCase): diff --git a/neutron/tests/unit/extensions/test_l3.py b/neutron/tests/unit/extensions/test_l3.py index ca54850d665..b08d0544dae 100644 --- a/neutron/tests/unit/extensions/test_l3.py +++ b/neutron/tests/unit/extensions/test_l3.py @@ -571,6 +571,7 @@ class ExtraAttributesMixinTestCase(testlib_api.SqlTestCase): def setUp(self): super(ExtraAttributesMixinTestCase, self).setUp() self.mixin = l3_attrs_db.ExtraAttributesMixin() + directory.add_plugin(lib_constants.L3, self.mixin) self.ctx = context.get_admin_context() self.router = l3_models.Router() with self.ctx.session.begin(): diff --git a/neutron/tests/unit/extensions/test_subnet_service_types.py b/neutron/tests/unit/extensions/test_subnet_service_types.py index 59e421ff33f..fd86c081b05 100644 --- a/neutron/tests/unit/extensions/test_subnet_service_types.py +++ b/neutron/tests/unit/extensions/test_subnet_service_types.py @@ -13,6 +13,7 @@ import webob.exc from neutron.db import db_base_plugin_v2 +from neutron.db import subnet_service_type_db_models from neutron.extensions import subnet_service_types from neutron.tests.unit.db import test_db_base_plugin_v2 @@ -34,7 +35,8 @@ class SubnetServiceTypesExtensionManager(object): class SubnetServiceTypesExtensionTestPlugin( - db_base_plugin_v2.NeutronDbPluginV2): + db_base_plugin_v2.NeutronDbPluginV2, + subnet_service_type_db_models.SubnetServiceTypeMixin): """Test plugin to mixin the subnet service_types extension. """