Make callback manager Object Oriented friendly

The callback manager was indexing callbacks based on
a callback ID generated from oslo utils reflection.
This presented two problems.

The first was that in py34 get_callable_name would use
__qualname__ which returns the class name the function
is defined on rather than the class of the object itself.
So two classes defined in the same module inheriting from
the same parent class could not both subscribe a method
defined on the parent.

The second more general problem is that two objects which
are instances of the same class cannot subscribe the same
method because they have the same ID.

This adds the hash of the method to the ID to prevent these
issues. The hash by itself could have been used but it's not
very user-friendly so the name is left on for nice log
messages.

Change-Id: Iff1ca8c4ddb58ca5907d21fa0de7f0f292b6fc0e
This commit is contained in:
Kevin Benton 2016-08-12 11:33:23 -06:00 committed by Armando Migliaccio
parent a915f2b690
commit 4d85336ec1
3 changed files with 34 additions and 3 deletions

View File

@ -166,4 +166,6 @@ def _get_id(callback):
# TODO(armax): consider using something other than names
# https://www.python.org/dev/peps/pep-3155/, but this
# might be okay for now.
return reflection.get_callable_name(callback)
parts = (reflection.get_callable_name(callback),
str(hash(callback)))
return '-'.join(parts)

View File

@ -22,6 +22,19 @@ from neutron.callbacks import resources
from neutron.tests import base
class ObjectWithCallback(object):
def __init__(self):
self.counter = 0
def callback(self, *args, **kwargs):
self.counter += 1
class GloriousObjectWithCallback(ObjectWithCallback):
pass
def callback_1(*args, **kwargs):
callback_1.counter += 1
callback_id_1 = manager._get_id(callback_1)
@ -214,3 +227,19 @@ class CallBacksManagerTestCase(base.BaseTestCase):
resources.ROUTER, events.BEFORE_DELETE, mock.ANY)
self.assertEqual(2, callback_1.counter)
self.assertEqual(1, callback_2.counter)
def test_object_instances_as_subscribers(self):
"""Ensures that the manager doesn't think these are equivalent."""
a = GloriousObjectWithCallback()
b = ObjectWithCallback()
c = ObjectWithCallback()
for o in (a, b, c):
self.manager.subscribe(
o.callback, resources.PORT, events.BEFORE_CREATE)
# ensure idempotency remains for a single object
self.manager.subscribe(
o.callback, resources.PORT, events.BEFORE_CREATE)
self.manager.notify(resources.PORT, events.BEFORE_CREATE, mock.ANY)
self.assertEqual(1, a.counter)
self.assertEqual(1, b.counter)
self.assertEqual(1, c.counter)

View File

@ -24,7 +24,6 @@ from oslo_utils import uuidutils
from webob import exc
from neutron.api import extensions
from neutron.api.rpc.agentnotifiers import dhcp_rpc_agent_api
from neutron.api.rpc.agentnotifiers import l3_rpc_agent_api
from neutron.api.rpc.handlers import dhcp_rpc
from neutron.api.rpc.handlers import l3_rpc
@ -1301,7 +1300,8 @@ class OvsDhcpAgentNotifierTestCase(test_agent.AgentDBTestMixIn,
mock.patch.object(
self.plugin, 'filter_hosts_with_network_access',
side_effect=lambda context, network_id, hosts: hosts).start()
self.dhcp_notifier = dhcp_rpc_agent_api.DhcpAgentNotifyAPI()
plugin = manager.NeutronManager.get_plugin()
self.dhcp_notifier = plugin.agent_notifiers[constants.AGENT_TYPE_DHCP]
self.dhcp_notifier_cast = mock.patch(
'neutron.api.rpc.agentnotifiers.dhcp_rpc_agent_api.'
'DhcpAgentNotifyAPI._cast_message').start()