Eliminate lookup of "resource extend" funcs by name

By registering functions directly we cut off the dependency of the
"resource extend" functions on the plugin. This is a step towards
the goal of removing the CommonDbMixin mixin class.

Also, we register all "resource extend" functions at plugin create
(in __new__) instead of in the class definition (which caused the
hooks to be registered on import). This ensures the "resource
extend" functions are only registered for the plugins/mixins that
are actually used.

Note that decorators are used to register "resource extend" methods,
similar to the callback receiver decorators.

Related-Blueprint: neutron-lib

Change-Id: I128cfda773d5f9597df9cd61261fdc05f2a174aa
This commit is contained in:
Henry Gessau 2016-12-05 21:45:50 -05:00
parent 0479f0f9d2
commit b3c0d5f239
36 changed files with 328 additions and 233 deletions

View File

@ -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. to neutron-lib in due course, and then it can be used from there.
""" """
import collections
import inspect
from neutron.common import utils from neutron.common import utils
# This dictionary will store methods for extending API resources. # 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): def register_funcs(resource, funcs):
"""Add functions to extend a resource. """Add functions to extend a resource.
@ -59,3 +66,65 @@ def get_funcs(resource):
""" """
return _resource_extend_functions.get(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

View File

@ -26,6 +26,7 @@ from neutron.objects import base as base_obj
from neutron.objects import subnetpool as subnetpool_obj from neutron.objects import subnetpool as subnetpool_obj
@resource_extend.has_resource_extenders
class AddressScopeDbMixin(ext_address_scope.AddressScopePluginBase): class AddressScopeDbMixin(ext_address_scope.AddressScopePluginBase):
"""Mixin class to add address scope to db_base_plugin_v2.""" """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 = self._get_address_scope(context, id)
address_scope.delete() 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.IPV4_ADDRESS_SCOPE] = None
network_res[ext_address_scope.IPV6_ADDRESS_SCOPE] = None network_res[ext_address_scope.IPV6_ADDRESS_SCOPE] = None
subnetpools = {subnet.subnetpool for subnet in network_db.subnets 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: if subnetpool['ip_version'] == constants.IP_VERSION_6:
network_res[ext_address_scope.IPV6_ADDRESS_SCOPE] = as_id network_res[ext_address_scope.IPV6_ADDRESS_SCOPE] = as_id
return network_res return network_res
resource_extend.register_funcs(
attr.NETWORKS, ['_extend_network_dict_address_scope'])

View File

@ -26,6 +26,7 @@ from neutron.objects.port.extensions import (allowedaddresspairs
as obj_addr_pair) as obj_addr_pair)
@resource_extend.has_resource_extenders
class AllowedAddressPairsMixin(object): class AllowedAddressPairsMixin(object):
"""Mixin class for allowed address pairs.""" """Mixin class for allowed address pairs."""
@ -63,19 +64,19 @@ class AllowedAddressPairsMixin(object):
return [self._make_allowed_address_pairs_dict(pair.db_obj) return [self._make_allowed_address_pairs_dict(pair.db_obj)
for pair in pairs] 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 # If port_db is provided, allowed address pairs will be accessed via
# sqlalchemy models. As they're loaded together with ports this # sqlalchemy models. As they're loaded together with ports this
# will not cause an extra query. # will not cause an extra query.
allowed_address_pairs = [ 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] address_pair in port_db.allowed_address_pairs]
port_res[addr_pair.ADDRESS_PAIRS] = allowed_address_pairs port_res[addr_pair.ADDRESS_PAIRS] = allowed_address_pairs
return port_res return port_res
resource_extend.register_funcs(
attr.PORTS, ['_extend_port_dict_allowed_address_pairs'])
def _delete_allowed_address_pairs(self, context, id): def _delete_allowed_address_pairs(self, context, id):
obj_addr_pair.AllowedAddressPair.delete_objects( obj_addr_pair.AllowedAddressPair.delete_objects(
context, port_id=id) context, port_id=id)

View File

@ -11,20 +11,23 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from neutron_lib.plugins import directory
from neutron.api.v2 import attributes from neutron.api.v2 import attributes
from neutron.db import _resource_extend as resource_extend from neutron.db import _resource_extend as resource_extend
from neutron.extensions import availability_zone as az_ext from neutron.extensions import availability_zone as az_ext
from neutron.extensions import network_availability_zone as net_az from neutron.extensions import network_availability_zone as net_az
@resource_extend.has_resource_extenders
class NetworkAvailabilityZoneMixin(net_az.NetworkAvailabilityZonePluginBase): class NetworkAvailabilityZoneMixin(net_az.NetworkAvailabilityZonePluginBase):
"""Mixin class to enable network's availability zone attributes.""" """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_res[az_ext.AZ_HINTS] = az_ext.convert_az_string_to_list(
net_db[az_ext.AZ_HINTS]) net_db[az_ext.AZ_HINTS])
plugin = directory.get_plugin()
net_res[az_ext.AVAILABILITY_ZONES] = ( net_res[az_ext.AVAILABILITY_ZONES] = (
self.get_network_availability_zones(net_db)) plugin.get_network_availability_zones(net_db))
resource_extend.register_funcs(
attributes.NETWORKS, ['_extend_availability_zone'])

View File

@ -11,32 +11,33 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from neutron_lib import constants
from neutron_lib.plugins import directory
from neutron.callbacks import events from neutron.callbacks import events
from neutron.callbacks import registry from neutron.callbacks import registry
from neutron.callbacks import resources from neutron.callbacks import resources
from neutron.common import utils 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.db import l3_attrs_db
from neutron.extensions import availability_zone as az_ext from neutron.extensions import availability_zone as az_ext
from neutron.extensions import l3 from neutron.extensions import l3
@resource_extend.has_resource_extenders
@registry.has_registry_receivers @registry.has_registry_receivers
class RouterAvailabilityZoneMixin(l3_attrs_db.ExtraAttributesMixin): class RouterAvailabilityZoneMixin(l3_attrs_db.ExtraAttributesMixin):
"""Mixin class to enable router's availability zone attributes.""" """Mixin class to enable router's availability zone attributes."""
def __new__(cls, *args, **kwargs): @staticmethod
inst = super(RouterAvailabilityZoneMixin, cls).__new__( @resource_extend.extends([l3.ROUTERS])
cls, *args, **kwargs) def _add_az_to_response(router_res, router_db):
db_base_plugin_v2.NeutronDbPluginV2.register_dict_extend_funcs( l3_plugin = directory.get_plugin(constants.L3)
l3.ROUTERS, [inst._add_az_to_response]) if not utils.is_extension_supported(l3_plugin,
return inst 'router_availability_zone'):
def _add_az_to_response(self, plugin, router_res, router_db):
if not utils.is_extension_supported(self, 'router_availability_zone'):
return return
router_res['availability_zones'] = ( 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]) @registry.receives(resources.ROUTER, [events.PRECOMMIT_CREATE])
def _process_az_request(self, resource, event, trigger, context, def _process_az_request(self, resource, event, trigger, context,

View File

@ -17,7 +17,6 @@ import weakref
from neutron_lib.db import utils as db_utils from neutron_lib.db import utils as db_utils
from oslo_db.sqlalchemy import utils as sa_utils from oslo_db.sqlalchemy import utils as sa_utils
import six
from sqlalchemy import and_ from sqlalchemy import and_
from sqlalchemy.ext import associationproxy from sqlalchemy.ext import associationproxy
from sqlalchemy import or_ from sqlalchemy import or_
@ -36,6 +35,13 @@ model_query = ndb_utils.model_query
resource_fields = ndb_utils.resource_fields 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): class CommonDbMixin(object):
"""Common methods used in core and service plugins.""" """Common methods used in core and service plugins."""
@ -85,11 +91,11 @@ class CommonDbMixin(object):
query_filter = (model.tenant_id == context.tenant_id) query_filter = (model.tenant_id == context.tenant_id)
# Execute query hooks registered from mixins and plugins # Execute query hooks registered from mixins and plugins
for hook in _model_query.get_hooks(model): 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: if query_hook:
query = query_hook(context, model, query) query = query_hook(context, model, query)
filter_hook = self._resolve_ref(hook.get('filter')) filter_hook = _resolve_ref(hook.get('filter'))
if filter_hook: if filter_hook:
query_filter = filter_hook(context, model, query_filter) query_filter = filter_hook(context, model, query_filter)
@ -164,31 +170,19 @@ class CommonDbMixin(object):
query = query.outerjoin(model.rbac_entries) query = query.outerjoin(model.rbac_entries)
query = query.filter(is_shared) query = query.filter(is_shared)
for hook in _model_query.get_hooks(model): for hook in _model_query.get_hooks(model):
result_filter = self._resolve_ref( result_filter = _resolve_ref(hook.get('result_filters', None))
hook.get('result_filters', None))
if result_filter: if result_filter:
query = result_filter(query, filters) query = result_filter(query, filters)
return query return query
def _resolve_ref(self, ref): @staticmethod
"""Finds string ref functions, handles dereference of weakref.""" def _apply_dict_extend_functions(resource_type,
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,
response, db_object): response, db_object):
for func in _resource_extend.get_funcs(resource_type): for func in _resource_extend.get_funcs(resource_type):
args = (response, db_object) resolved_func = _resolve_ref(func)
if not isinstance(func, six.string_types): if resolved_func:
# must call unbound method - use self as 1st argument resolved_func(response, db_object)
args = (self,) + args
func = self._resolve_ref(func)
if func:
func(*args)
def _get_collection_query(self, context, model, filters=None, def _get_collection_query(self, context, model, filters=None,
sorts=None, limit=None, marker_obj=None, sorts=None, limit=None, marker_obj=None,

View File

@ -40,7 +40,8 @@ class DataPlaneStatusMixin(object):
else: else:
self._process_create_port_data_plane_status(context, data, res) 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 port_res[dps_lib.DATA_PLANE_STATUS] = None
if port_db.get(dps_lib.DATA_PLANE_STATUS): if port_db.get(dps_lib.DATA_PLANE_STATUS):

View File

@ -41,6 +41,7 @@ class DNSActionsData(object):
self.previous_dns_domain = previous_dns_domain self.previous_dns_domain = previous_dns_domain
@resource_extend.has_resource_extenders
class DNSDbMixin(object): class DNSDbMixin(object):
"""Mixin class to add DNS methods to db_base_plugin_v2.""" """Mixin class to add DNS methods to db_base_plugin_v2."""
@ -63,7 +64,9 @@ class DNSDbMixin(object):
raise dns.ExternalDNSDriverNotFound( raise dns.ExternalDNSDriverNotFound(
driver=cfg.CONF.external_dns_driver) 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_domain'] = ''
floatingip_res['dns_name'] = '' floatingip_res['dns_name'] = ''
if floatingip_db.dns: if floatingip_db.dns:
@ -71,9 +74,6 @@ class DNSDbMixin(object):
floatingip_res['dns_name'] = floatingip_db.dns['dns_name'] floatingip_res['dns_name'] = floatingip_db.dns['dns_name']
return floatingip_res return floatingip_res
resource_extend.register_funcs(
l3.FLOATINGIPS, ['_extend_floatingip_dict_dns'])
def _process_dns_floatingip_create_precommit(self, context, def _process_dns_floatingip_create_precommit(self, context,
floatingip_data, req_data): floatingip_data, req_data):
# expects to be called within a plugin's session # expects to be called within a plugin's session

View File

@ -64,6 +64,7 @@ def _network_result_filter_hook(query, filters):
return query.filter(~models_v2.Network.external.has()) return query.filter(~models_v2.Network.external.has())
@resource_extend.has_resource_extenders
@registry.has_registry_receivers @registry.has_registry_receivers
class External_net_db_mixin(object): class External_net_db_mixin(object):
"""Mixin class to add external network methods to db_base_plugin_v2.""" """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( return net_obj.ExternalNetwork.objects_exist(
context, network_id=net_id) 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 # Comparing with None for converting uuid into bool
network_res[external_net.EXTERNAL] = network_db.external is not None network_res[external_net.EXTERNAL] = network_db.external is not None
return network_res return network_res
resource_extend.register_funcs(
attributes.NETWORKS, ['_extend_network_dict_l3'])
def _process_l3_create(self, context, net_data, req_data): def _process_l3_create(self, context, net_data, req_data):
external = req_data.get(external_net.EXTERNAL) external = req_data.get(external_net.EXTERNAL)
external_set = validators.is_attr_set(external) external_set = validators.is_attr_set(external)

View File

@ -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 from neutron.objects.port.extensions import extra_dhcp_opt as obj_extra_dhcp
@resource_extend.has_resource_extenders
class ExtraDhcpOptMixin(object): class ExtraDhcpOptMixin(object):
"""Mixin class to add extra options to the DHCP opts file """Mixin class to add extra options to the DHCP opts file
and associate them to a port. and associate them to a port.
@ -114,12 +115,11 @@ class ExtraDhcpOptMixin(object):
return bool(dopts) 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, res[edo_ext.EXTRADHCPOPTS] = [{'opt_name': dho.opt_name,
'opt_value': dho.opt_value, 'opt_value': dho.opt_value,
'ip_version': dho.ip_version} 'ip_version': dho.ip_version}
for dho in port.dhcp_opts] for dho in port.dhcp_opts]
return res return res
resource_extend.register_funcs(
attributes.PORTS, ['_extend_port_dict_extra_dhcp_opt'])

View File

@ -39,18 +39,18 @@ extra_route_opts = [
cfg.CONF.register_opts(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): class ExtraRoute_dbonly_mixin(l3_db.L3_NAT_dbonly_mixin):
"""Mixin class to support extra route configuration on router.""" """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. router_res['routes'] = (ExtraRoute_dbonly_mixin.
_make_extra_route_list( _make_extra_route_list(
router_db['route_list'] router_db['route_list']
)) ))
resource_extend.register_funcs(
l3.ROUTERS, ['_extend_router_dict_extraroute'])
def update_router(self, context, id, router): def update_router(self, context, id, router):
r = router['router'] r = router['router']
if 'routes' in r: if 'routes' in r:

View File

@ -33,10 +33,13 @@ def get_attr_info():
} }
@resource_extend.has_resource_extenders
class ExtraAttributesMixin(object): class ExtraAttributesMixin(object):
"""Mixin class to enable router's extra attributes.""" """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 {} extra_attrs = router_db['extra_attributes'] or {}
for name, info in get_attr_info().items(): for name, info in get_attr_info().items():
from_db = info.get('transform_from_db', lambda x: x) from_db = info.get('transform_from_db', lambda x: x)
@ -61,6 +64,3 @@ class ExtraAttributesMixin(object):
return return
raise RuntimeError(_("Tried to set a key '%s' that doesn't exist " raise RuntimeError(_("Tried to set a key '%s' that doesn't exist "
"in the extra attributes table.") % key) "in the extra attributes table.") % key)
resource_extend.register_funcs(
l3.ROUTERS, ['_extend_extra_router_dict'])

View File

@ -38,13 +38,13 @@ setattr(l3_models.Router, 'enable_snat',
nullable=False)) nullable=False))
@resource_extend.has_resource_extenders
class L3_NAT_dbonly_mixin(l3_db.L3_NAT_dbonly_mixin): class L3_NAT_dbonly_mixin(l3_db.L3_NAT_dbonly_mixin):
"""Mixin class to add configurable gateway modes.""" """Mixin class to add configurable gateway modes."""
resource_extend.register_funcs( @staticmethod
l3.ROUTERS, ['_extend_router_dict_gw_mode']) @resource_extend.extends([l3.ROUTERS])
def _extend_router_dict_gw_mode(router_res, router_db):
def _extend_router_dict_gw_mode(self, router_res, router_db):
if router_db.gw_port_id: if router_db.gw_port_id:
nw_id = router_db.gw_port['network_id'] nw_id = router_db.gw_port['network_id']
router_res[EXTERNAL_GW_INFO] = { router_res[EXTERNAL_GW_INFO] = {

View File

@ -13,11 +13,16 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from neutron_lib.plugins import directory
from neutron.api.v2 import attributes from neutron.api.v2 import attributes
from neutron.db import _resource_extend as resource_extend from neutron.db import _resource_extend as resource_extend
@resource_extend.has_resource_extenders
class PortBindingBaseMixin(object): class PortBindingBaseMixin(object):
# Initialized by core plugin or ml2 mechanism driver(s)
base_binding_dict = None base_binding_dict = None
def _process_portbindings_create_and_update(self, context, port_data, def _process_portbindings_create_and_update(self, context, port_data,
@ -28,13 +33,10 @@ class PortBindingBaseMixin(object):
if self.base_binding_dict: if self.base_binding_dict:
port_res.update(self.base_binding_dict) port_res.update(self.base_binding_dict)
@staticmethod
def _extend_port_dict_binding(plugin, port_res, port_db): @resource_extend.extends([attributes.PORTS])
if not isinstance(plugin, PortBindingBaseMixin): def _extend_port_dict_binding(port_res, port_db):
return plugin = directory.get_plugin()
plugin.extend_port_dict_binding(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])

View File

@ -15,6 +15,7 @@
from neutron_lib.api.definitions import portbindings from neutron_lib.api.definitions import portbindings
from neutron_lib.api import validators from neutron_lib.api import validators
from neutron_lib.plugins import directory
from neutron.api.v2 import attributes from neutron.api.v2 import attributes
from neutron.db import _model_query as model_query from neutron.db import _model_query as model_query
@ -40,8 +41,8 @@ def _port_result_filter_hook(query, filters):
return query return query
@resource_extend.has_resource_extenders
class PortBindingMixin(portbindings_base.PortBindingBaseMixin): class PortBindingMixin(portbindings_base.PortBindingBaseMixin):
extra_binding_dict = None
def __new__(cls, *args, **kwargs): def __new__(cls, *args, **kwargs):
model_query.register_hook( model_query.register_hook(
@ -101,11 +102,10 @@ class PortBindingMixin(portbindings_base.PortBindingBaseMixin):
host = port_db.portbinding.host if port_db.portbinding else None host = port_db.portbinding.host if port_db.portbinding else None
self._extend_port_dict_binding_host(port_res, host) self._extend_port_dict_binding_host(port_res, host)
@staticmethod
def _extend_port_dict_binding(plugin, port_res, port_db): @resource_extend.extends([attributes.PORTS])
if not isinstance(plugin, PortBindingMixin): def _extend_port_dict_binding(port_res, port_db):
return plugin = directory.get_plugin()
plugin.extend_port_dict_binding(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])

View File

@ -13,6 +13,7 @@
# under the License. # under the License.
from neutron_lib.api import validators from neutron_lib.api import validators
from neutron_lib.plugins import directory
from neutron.api.v2 import attributes as attrs from neutron.api.v2 import attributes as attrs
from neutron.common import utils from neutron.common import utils
@ -21,16 +22,16 @@ from neutron.db import portsecurity_db_common
from neutron.extensions import portsecurity as psec from neutron.extensions import portsecurity as psec
@resource_extend.has_resource_extenders
class PortSecurityDbMixin(portsecurity_db_common.PortSecurityDbCommon): 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 if ('port-security' in
getattr(self, 'supported_extension_aliases', [])): getattr(plugin, 'supported_extension_aliases', [])):
super(PortSecurityDbMixin, self)._extend_port_security_dict( super(PortSecurityDbMixin, plugin)._extend_port_security_dict(
response_data, db_data) response_data, db_data)
def _determine_port_security_and_has_ip(self, context, port): def _determine_port_security_and_has_ip(self, context, port):

View File

@ -21,7 +21,8 @@ from neutron.objects.port.extensions import port_security as p_ps
class PortSecurityDbCommon(object): class PortSecurityDbCommon(object):
"""Mixin class to add port security.""" """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: if db_data.get('port_security') is None:
response_data[psec.PORTSECURITY] = psec.DEFAULT_PORT_SECURITY response_data[psec.PORTSECURITY] = psec.DEFAULT_PORT_SECURITY
else: else:

View File

@ -35,6 +35,7 @@ from neutron.db.models import securitygroup as sg_models
from neutron.extensions import securitygroup as ext_sg from neutron.extensions import securitygroup as ext_sg
@resource_extend.has_resource_extenders
@registry.has_registry_receivers @registry.has_registry_receivers
class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase): class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase):
"""Mixin class to add security group to db_base_plugin_v2.""" """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, resources.SECURITY_GROUP_RULE, events.AFTER_DELETE, self,
**kwargs) **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 # Security group bindings will be retrieved from the SQLAlchemy
# model. As they're loaded eagerly with ports because of the # model. As they're loaded eagerly with ports because of the
# joined load they will not cause an extra query. # 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 port_res[ext_sg.SECURITYGROUPS] = security_group_ids
return port_res return port_res
resource_extend.register_funcs(
attributes.PORTS, ['_extend_port_dict_security_group'])
def _process_port_create_security_group(self, context, port, def _process_port_create_security_group(self, context, port,
security_group_ids): security_group_ids):
if validators.is_attr_set(security_group_ids): if validators.is_attr_set(security_group_ids):

View File

@ -16,17 +16,14 @@ from neutron.db import _resource_extend as resource_extend
from neutron.db import standard_attr from neutron.db import standard_attr
@resource_extend.has_resource_extenders
class StandardAttrDescriptionMixin(object): class StandardAttrDescriptionMixin(object):
supported_extension_aliases = ['standard-attr-description'] 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'): if not hasattr(db_object, 'description'):
return return
res['description'] = db_object.description 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)

View File

@ -20,13 +20,13 @@ from neutron.api.v2 import attributes
from neutron.db import _resource_extend as resource_extend from neutron.db import _resource_extend as resource_extend
@resource_extend.has_resource_extenders
class SubnetServiceTypeMixin(object): class SubnetServiceTypeMixin(object):
"""Mixin class to extend subnet with service type attribute""" """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 subnet_res['service_types'] = [service_type['service_type'] for
service_type in service_type in
subnet_db.service_types] subnet_db.service_types]
resource_extend.register_funcs(
attributes.SUBNETS, [_extend_subnet_service_types])

View File

@ -17,13 +17,13 @@ from neutron.db import _resource_extend as resource_extend
from neutron.extensions import vlantransparent from neutron.extensions import vlantransparent
@resource_extend.has_resource_extenders
class Vlantransparent_db_mixin(object): class Vlantransparent_db_mixin(object):
"""Mixin class to add vlan transparent methods to db_base_plugin_v2.""" """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_res[vlantransparent.VLANTRANSPARENT] = (
network_db.vlan_transparent) network_db.vlan_transparent)
return network_res return network_res
resource_extend.register_funcs(
attributes.NETWORKS, ['_extend_network_dict_vlan_transparent'])

View File

@ -109,6 +109,7 @@ def _ml2_port_result_filter_hook(query, filters):
return query.filter(models_v2.Port.port_binding.has(bind_criteria)) return query.filter(models_v2.Port.port_binding.has(bind_criteria))
@resource_extend.has_resource_extenders
@registry.has_registry_receivers @registry.has_registry_receivers
class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2, class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
dvr_mac_db.DVRDbMixin, dvr_mac_db.DVRDbMixin,
@ -602,34 +603,37 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
'port': binding.port_id}) 'port': binding.port_id})
return {} 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. # None when called during unit tests for other plugins.
if port_db.port_binding: if port_db.port_binding:
self._update_port_dict_binding(port_res, port_db.port_binding) plugin._update_port_dict_binding(port_res, port_db.port_binding)
resource_extend.register_funcs(
attributes.PORTS, ['_ml2_extend_port_dict_binding'])
# ML2's resource extend functions allow extension drivers that extend # ML2's resource extend functions allow extension drivers that extend
# attributes for the resources to add those attributes to the result. # 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): @staticmethod
session = self._object_session_or_new_session(netdb) @resource_extend.extends([attributes.NETWORKS])
self.extension_manager.extend_network_dict(session, netdb, result) 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): @staticmethod
session = self._object_session_or_new_session(portdb) @resource_extend.extends([attributes.PORTS])
self.extension_manager.extend_port_dict(session, portdb, result) 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): @staticmethod
session = self._object_session_or_new_session(subnetdb) @resource_extend.extends([attributes.SUBNETS])
self.extension_manager.extend_subnet_dict(session, subnetdb, result) 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 @staticmethod
def _object_session_or_new_session(sql_obj): def _object_session_or_new_session(sql_obj):

View File

@ -42,13 +42,6 @@ IS_DEFAULT = 'is_default'
CHECK_REQUIREMENTS = 'dry-run' 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() @db_api.retry_if_session_inactive()
def _ensure_external_network_default_value_callback( def _ensure_external_network_default_value_callback(
resource, event, trigger, context, request, network, **kwargs): resource, event, trigger, context, request, network, **kwargs):
@ -78,11 +71,9 @@ def _ensure_external_network_default_value_callback(
obj.update() obj.update()
@resource_extend.has_resource_extenders
class AutoAllocatedTopologyMixin(common_db_mixin.CommonDbMixin): class AutoAllocatedTopologyMixin(common_db_mixin.CommonDbMixin):
resource_extend.register_funcs(
attributes.NETWORKS, [_extend_external_network_default])
def __new__(cls, *args, **kwargs): def __new__(cls, *args, **kwargs):
# NOTE(kevinbenton): we subscribe on object construction because # NOTE(kevinbenton): we subscribe on object construction because
# the tests blow away the callback manager for each run # 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) self._l3_plugin = directory.get_plugin(constants.L3)
return self._l3_plugin 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): def get_auto_allocated_topology(self, context, tenant_id, fields=None):
"""Return tenant's network associated to auto-allocated topology. """Return tenant's network associated to auto-allocated topology.

View File

@ -51,6 +51,7 @@ def disable_dvr_extension_by_config(aliases):
aliases.remove('dvr') aliases.remove('dvr')
@resource_extend.has_resource_extenders
class L3RouterPlugin(service_base.ServicePluginBase, class L3RouterPlugin(service_base.ServicePluginBase,
common_db_mixin.CommonDbMixin, common_db_mixin.CommonDbMixin,
extraroute_db.ExtraRoute_db_mixin, extraroute_db.ExtraRoute_db_mixin,
@ -141,10 +142,7 @@ class L3RouterPlugin(service_base.ServicePluginBase,
context, floatingip, context, floatingip,
initial_status=n_const.FLOATINGIP_STATUS_DOWN) initial_status=n_const.FLOATINGIP_STATUS_DOWN)
@staticmethod
def add_flavor_id(plugin, router_res, router_db): @resource_extend.extends([l3.ROUTERS])
router_res['flavor_id'] = router_db['flavor_id'] def add_flavor_id(router_res, router_db):
router_res['flavor_id'] = router_db['flavor_id']
resource_extend.register_funcs(
l3.ROUTERS, [add_flavor_id])

View File

@ -25,6 +25,7 @@ from neutron.db import standard_attr
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@resource_extend.has_resource_extenders
class RevisionPlugin(service_base.ServicePluginBase): class RevisionPlugin(service_base.ServicePluginBase):
"""Plugin to populate revision numbers into standard attr resources.""" """Plugin to populate revision numbers into standard attr resources."""
@ -32,9 +33,6 @@ class RevisionPlugin(service_base.ServicePluginBase):
def __init__(self): def __init__(self):
super(RevisionPlugin, self).__init__() 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) db_api.sqla_listen(se.Session, 'before_flush', self.bump_revisions)
def bump_revisions(self, session, context, instances): def bump_revisions(self, session, context, instances):
@ -76,7 +74,10 @@ class RevisionPlugin(service_base.ServicePluginBase):
def get_plugin_description(self): def get_plugin_description(self):
return "Adds revision numbers to resources." 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 resource_res['revision_number'] = resource_db.revision_number
def _find_related_obj(self, session, obj, relationship_col): def _find_related_obj(self, session, obj, relationship_col):

View File

@ -49,30 +49,7 @@ SEGMENT_NAME_STUB = 'Neutron segment id %s'
MAX_INVENTORY_UPDATE_RETRIES = 10 MAX_INVENTORY_UPDATE_RETRIES = 10
def _extend_network_dict_binding(plugin, network_res, network_db): @resource_extend.has_resource_extenders
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
@registry.has_registry_receivers @registry.has_registry_receivers
class Plugin(db.SegmentDbMixin, segment.SegmentPluginBase): class Plugin(db.SegmentDbMixin, segment.SegmentPluginBase):
@ -81,14 +58,36 @@ class Plugin(db.SegmentDbMixin, segment.SegmentPluginBase):
supported_extension_aliases = ["segment", "ip_allocation", "l2_adjacency"] supported_extension_aliases = ["segment", "ip_allocation", "l2_adjacency"]
def __init__(self): 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() 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 @classmethod
def get_instance(cls): def get_instance(cls):
if cls._instance is None: if cls._instance is None:

View File

@ -44,13 +44,7 @@ resource_model_map = {
} }
def _extend_tags_dict(plugin, response_data, db_data): @resource_extend.has_resource_extenders
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
class TagPlugin(common_db_mixin.CommonDbMixin, tag_ext.TagPluginBase): class TagPlugin(common_db_mixin.CommonDbMixin, tag_ext.TagPluginBase):
"""Implementation of the Neutron Tag Service Plugin.""" """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): def __new__(cls, *args, **kwargs):
inst = super(TagPlugin, cls).__new__(cls, *args, **kwargs) inst = super(TagPlugin, cls).__new__(cls, *args, **kwargs)
inst._filter_methods = [] # prevent GC of our partial functions inst._filter_methods = [] # prevent GC of our partial functions
for resource, model in resource_model_map.items(): for model in resource_model_map.values():
resource_extend.register_funcs(resource, [_extend_tags_dict])
method = functools.partial(tag_methods.apply_tag_filters, model) method = functools.partial(tag_methods.apply_tag_filters, model)
inst._filter_methods.append(method) inst._filter_methods.append(method)
model_query.register_hook(model, "tag", model_query.register_hook(model, "tag",
@ -69,6 +62,14 @@ class TagPlugin(common_db_mixin.CommonDbMixin, tag_ext.TagPluginBase):
result_filters=method) result_filters=method)
return inst 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): def _get_resource(self, context, resource, resource_id):
model = resource_model_map[resource] model = resource_model_map[resource]
try: try:

View File

@ -70,12 +70,6 @@ def _format_timestamp(resource_db, result):
strftime(TIME_FORMAT_WHOLE_SECONDS)) + 'Z' 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): def _add_timestamp(mapper, _conn, target):
if not target.created_at and not target.updated_at: if not target.created_at and not target.updated_at:
time = timeutils.utcnow() time = timeutils.utcnow()
@ -84,14 +78,13 @@ def _add_timestamp(mapper, _conn, target):
return target return target
@resource_extend.has_resource_extenders
class TimeStamp_db_mixin(object): class TimeStamp_db_mixin(object):
"""Mixin class to add Time Stamp methods.""" """Mixin class to add Time Stamp methods."""
def __new__(cls, *args, **kwargs): def __new__(cls, *args, **kwargs):
rs_model_maps = standard_attr.get_standard_attr_resource_model_map() rs_model_maps = standard_attr.get_standard_attr_resource_model_map()
for rsmap, model in rs_model_maps.items(): for model in rs_model_maps.values():
resource_extend.register_funcs(
rsmap, [_extend_resource_dict_timestamp])
model_query.register_hook( model_query.register_hook(
model, model,
"change_since_query", "change_since_query",
@ -105,3 +98,11 @@ class TimeStamp_db_mixin(object):
listen(standard_attr.StandardAttribute, 'before_insert', listen(standard_attr.StandardAttribute, 'before_insert',
_add_timestamp) _add_timestamp)
listen(se.Session, 'before_flush', _update_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)

View File

@ -16,6 +16,7 @@ import copy
from neutron_lib.api.definitions import portbindings from neutron_lib.api.definitions import portbindings
from neutron_lib import context from neutron_lib import context
from neutron_lib.plugins import directory
from neutron_lib.services import base as service_base from neutron_lib.services import base as service_base
from oslo_log import log as logging from oslo_log import log as logging
from oslo_utils import uuidutils from oslo_utils import uuidutils
@ -40,26 +41,7 @@ from neutron.services.trunk.seg_types import validators
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
def _extend_port_trunk_details(core_plugin, port_res, port_db): @resource_extend.has_resource_extenders
"""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
@registry.has_registry_receivers @registry.has_registry_receivers
class TrunkPlugin(service_base.ServicePluginBase, class TrunkPlugin(service_base.ServicePluginBase,
common_db_mixin.CommonDbMixin): common_db_mixin.CommonDbMixin):
@ -70,8 +52,6 @@ class TrunkPlugin(service_base.ServicePluginBase,
__native_sorting_support = True __native_sorting_support = True
def __init__(self): def __init__(self):
resource_extend.register_funcs(
attributes.PORTS, [_extend_port_trunk_details])
self._rpc_backend = None self._rpc_backend = None
self._drivers = [] self._drivers = []
self._segmentation_types = {} self._segmentation_types = {}
@ -85,6 +65,28 @@ class TrunkPlugin(service_base.ServicePluginBase,
LOG.debug('Trunk plugin loaded with driver %s', driver.name) LOG.debug('Trunk plugin loaded with driver %s', driver.name)
self.check_compatibility() 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): def check_compatibility(self):
"""Verify the plugin can load correctly and fail otherwise.""" """Verify the plugin can load correctly and fail otherwise."""
self.check_driver_compatibility() self.check_driver_compatibility()

View File

@ -47,6 +47,7 @@ from neutron.callbacks import registry
from neutron.common import config from neutron.common import config
from neutron.common import rpc as n_rpc from neutron.common import rpc as n_rpc
from neutron.db import _model_query as model_query 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 agentschedulers_db
from neutron.db import api as db_api from neutron.db import api as db_api
from neutron import manager from neutron import manager
@ -168,6 +169,7 @@ class DietTestCase(base.BaseTestCase):
self.addCleanup(mock.patch.stopall) self.addCleanup(mock.patch.stopall)
self.addCleanup(self.reset_model_query_hooks) self.addCleanup(self.reset_model_query_hooks)
self.addCleanup(self.reset_resource_extend_functions)
self.addOnException(self.check_for_systemexit) self.addOnException(self.check_for_systemexit)
self.orig_pid = os.getpid() self.orig_pid = os.getpid()
@ -178,6 +180,10 @@ class DietTestCase(base.BaseTestCase):
def reset_model_query_hooks(): def reset_model_query_hooks():
model_query._model_query_hooks = {} model_query._model_query_hooks = {}
@staticmethod
def reset_resource_extend_functions():
resource_extend._resource_extend_functions = {}
def addOnException(self, handler): def addOnException(self, handler):
def safe_handler(*args, **kwargs): def safe_handler(*args, **kwargs):

View File

@ -18,6 +18,7 @@ import random
from neutron_lib import constants from neutron_lib import constants
from neutron_lib import context from neutron_lib import context
from neutron_lib.plugins import directory
from oslo_utils import uuidutils from oslo_utils import uuidutils
import testscenarios import testscenarios
@ -45,6 +46,7 @@ class L3SchedulerBaseTest(test_db_base_plugin_v2.NeutronDbPluginV2TestCase):
super(L3SchedulerBaseTest, self).setUp(PLUGIN_NAME) super(L3SchedulerBaseTest, self).setUp(PLUGIN_NAME)
self.l3_plugin = l3_router_plugin.L3RouterPlugin() self.l3_plugin = l3_router_plugin.L3RouterPlugin()
directory.add_plugin(constants.L3, self.l3_plugin)
self.adminContext = context.get_admin_context() self.adminContext = context.get_admin_context()
self.adminContext.tenant_id = _uuid() self.adminContext.tenant_id = _uuid()
@ -296,6 +298,7 @@ class L3AZSchedulerBaseTest(test_db_base_plugin_v2.NeutronDbPluginV2TestCase):
super(L3AZSchedulerBaseTest, self).setUp(plugin='ml2') super(L3AZSchedulerBaseTest, self).setUp(plugin='ml2')
self.l3_plugin = l3_router_plugin.L3RouterPlugin() self.l3_plugin = l3_router_plugin.L3RouterPlugin()
directory.add_plugin(constants.L3, self.l3_plugin)
self.l3_plugin.router_scheduler = None self.l3_plugin.router_scheduler = None
self.adminContext = context.get_admin_context() self.adminContext = context.get_admin_context()
self.adminContext.tenant_id = '_func_test_tenant_' self.adminContext.tenant_id = '_func_test_tenant_'

View File

@ -48,6 +48,7 @@ class L3DvrTestCase(test_db_base_plugin_v2.NeutronDbPluginV2TestCase):
self.core_plugin = directory.get_plugin() self.core_plugin = directory.get_plugin()
self.ctx = context.get_admin_context() self.ctx = context.get_admin_context()
self.mixin = FakeL3Plugin() self.mixin = FakeL3Plugin()
directory.add_plugin(const.L3, self.mixin)
def _create_router(self, router): def _create_router(self, router):
with self.ctx.session.begin(subtransactions=True): with self.ctx.session.begin(subtransactions=True):

View File

@ -11,6 +11,8 @@
# under the License. # under the License.
import mock 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 as pd
from neutron.db import portsecurity_db_common as pdc from neutron.db import portsecurity_db_common as pdc
@ -29,6 +31,7 @@ class PortSecurityDbMixinTestCase(base.BaseTestCase):
def setUp(self): def setUp(self):
super(PortSecurityDbMixinTestCase, self).setUp() super(PortSecurityDbMixinTestCase, self).setUp()
self.plugin = FakePlugin() self.plugin = FakePlugin()
directory.add_plugin(constants.CORE, self.plugin)
@mock.patch.object(common, '_extend_port_security_dict') @mock.patch.object(common, '_extend_port_security_dict')
def test__extend_port_security_dict_relies_on_common(self, extend): def test__extend_port_security_dict_relies_on_common(self, extend):

View File

@ -41,11 +41,18 @@ class DataPlaneStatusTestExtensionManager(object):
return dps_ext.Data_plane_status.get_extended_resources(version) return dps_ext.Data_plane_status.get_extended_resources(version)
@resource_extend.has_resource_extenders
class DataPlaneStatusExtensionTestPlugin(db_base_plugin_v2.NeutronDbPluginV2, class DataPlaneStatusExtensionTestPlugin(db_base_plugin_v2.NeutronDbPluginV2,
dps_db.DataPlaneStatusMixin): dps_db.DataPlaneStatusMixin):
supported_extension_aliases = ["data-plane-status"] 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): def update_port(self, context, id, port):
with context.session.begin(subtransactions=True): with context.session.begin(subtransactions=True):
ret_port = super(DataPlaneStatusExtensionTestPlugin, ret_port = super(DataPlaneStatusExtensionTestPlugin,
@ -56,9 +63,6 @@ class DataPlaneStatusExtensionTestPlugin(db_base_plugin_v2.NeutronDbPluginV2,
ret_port) ret_port)
return ret_port return ret_port
resource_extend.register_funcs(attrs.PORTS,
['_extend_port_data_plane_status'])
class DataPlaneStatusExtensionTestCase( class DataPlaneStatusExtensionTestCase(
test_db_base_plugin_v2.NeutronDbPluginV2TestCase): test_db_base_plugin_v2.NeutronDbPluginV2TestCase):

View File

@ -571,6 +571,7 @@ class ExtraAttributesMixinTestCase(testlib_api.SqlTestCase):
def setUp(self): def setUp(self):
super(ExtraAttributesMixinTestCase, self).setUp() super(ExtraAttributesMixinTestCase, self).setUp()
self.mixin = l3_attrs_db.ExtraAttributesMixin() self.mixin = l3_attrs_db.ExtraAttributesMixin()
directory.add_plugin(lib_constants.L3, self.mixin)
self.ctx = context.get_admin_context() self.ctx = context.get_admin_context()
self.router = l3_models.Router() self.router = l3_models.Router()
with self.ctx.session.begin(): with self.ctx.session.begin():

View File

@ -13,6 +13,7 @@
import webob.exc import webob.exc
from neutron.db import db_base_plugin_v2 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.extensions import subnet_service_types
from neutron.tests.unit.db import test_db_base_plugin_v2 from neutron.tests.unit.db import test_db_base_plugin_v2
@ -34,7 +35,8 @@ class SubnetServiceTypesExtensionManager(object):
class SubnetServiceTypesExtensionTestPlugin( 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. """Test plugin to mixin the subnet service_types extension.
""" """