ML2 mechanism driver access to binding details

The following properties are added to the PortContext object passed to
ML2 mechanism drivers for port operations:

* bound_driver - name of current bound driver
* original_bound_driver - name previously bound driver in an update
* original_bound_segment - network segment used in previous binding

Some issues with the existing ML2 port binding unit tests were also
fixed.

The remainder of the fix for bug 1276395, making these binding details
available to mechanism drivers when a port is deleted, will be
addressed as part of the fix for bug 1276391.

Partial-Bug: #1276395
Change-Id: I9ecff4a4e044920ed2dde709c89aeb9bc773220d
This commit is contained in:
Bob Kukura 2014-02-24 16:33:26 -05:00
parent f07f9a3d71
commit d1472deed5
6 changed files with 117 additions and 13 deletions

View File

@ -218,7 +218,7 @@ class PortContext(object):
@abstractproperty
def original(self):
"""Return the original state of the port
"""Return the original state of the port.
Return the original state of the port, prior to a call to
update_port. Method is only valid within calls to
@ -236,6 +236,31 @@ class PortContext(object):
"""Return the currently bound segment dictionary."""
pass
@abstractproperty
def original_bound_segment(self):
"""Return the original bound segment dictionary.
Return the original bound segment dictionary, prior to a call
to update_port. Method is only valid within calls to
update_port_precommit and update_port_postcommit.
"""
pass
@abstractproperty
def bound_driver(self):
"""Return the currently bound mechanism driver name."""
pass
@abstractproperty
def original_bound_driver(self):
"""Return the original bound mechanism driver name.
Return the original bound mechanism driver name, prior to a
call to update_port. Method is only valid within calls to
update_port_precommit and update_port_postcommit.
"""
pass
@abstractmethod
def host_agents(self, agent_type):
"""Get agents of the specified type on port's host.

View File

@ -78,6 +78,12 @@ class PortContext(MechanismDriverContext, api.PortContext):
network)
self._binding = db.ensure_port_binding(plugin_context.session,
port['id'])
if original_port:
self._original_bound_segment_id = self._binding.segment
self._original_bound_driver = self._binding.driver
else:
self._original_bound_segment_id = None
self._original_bound_driver = None
@property
def current(self):
@ -99,6 +105,21 @@ class PortContext(MechanismDriverContext, api.PortContext):
if segment[api.ID] == id:
return segment
@property
def original_bound_segment(self):
if self._original_bound_segment_id:
for segment in self._network_context.network_segments:
if segment[api.ID] == self._original_bound_segment_id:
return segment
@property
def bound_driver(self):
return self._binding.driver
@property
def original_bound_driver(self):
return self._original_bound_driver
def host_agents(self, agent_type):
return self._plugin.get_agents(self._plugin_context,
filters={'agent_type': [agent_type],

View File

@ -67,6 +67,18 @@ class FakePortContext(api.PortContext):
if segment[api.ID] == self._bound_segment_id:
return segment
@property
def original_bound_segment(self):
return None
@property
def bound_driver(self):
return None
@property
def original_bound_driver(self):
return None
def host_agents(self, agent_type):
if agent_type == self._agent_type:
return self._agents

View File

@ -83,11 +83,19 @@ class LoggerMechanismDriver(api.MechanismDriver):
def _log_port_call(self, method_name, context):
network_context = context.network
LOG.info(_("%(method)s called with port settings %(current)s "
"(original settings %(original)s) on network %(network)s"),
"(original settings %(original)s) "
"bound to segment %(segment)s "
"(original segment %(original_segment)s) "
"using driver %(driver)s "
"(original driver %(original_driver)s) "
"on network %(network)s"),
{'method': method_name,
'current': context.current,
'original': context.original,
'segment': context.bound_segment,
'original_segment': context.original_bound_segment,
'driver': context.bound_driver,
'original_driver': context.original_bound_driver,
'network': network_context.current})
def create_port_precommit(self, context):

View File

@ -84,11 +84,34 @@ class TestMechanismDriver(api.MechanismDriver):
assert(isinstance(context, api.PortContext))
assert(isinstance(context.current, dict))
assert(context.current['id'] is not None)
vif_type = context.current.get(portbindings.VIF_TYPE)
assert(vif_type is not None)
if vif_type in (portbindings.VIF_TYPE_UNBOUND,
portbindings.VIF_TYPE_BINDING_FAILED):
assert(context.bound_segment is None)
assert(context.bound_driver is None)
else:
assert(isinstance(context.bound_segment, dict))
assert(context.bound_driver == 'test')
if original_expected:
assert(isinstance(context.original, dict))
assert(context.current['id'] == context.original['id'])
vif_type = context.original.get(portbindings.VIF_TYPE)
assert(vif_type is not None)
if vif_type in (portbindings.VIF_TYPE_UNBOUND,
portbindings.VIF_TYPE_BINDING_FAILED):
assert(context.original_bound_segment is None)
assert(context.original_bound_driver is None)
else:
assert(isinstance(context.original_bound_segment, dict))
assert(context.original_bound_driver == 'test')
else:
assert(not context.original)
assert(context.original is None)
assert(context.original_bound_segment is None)
assert(context.original_bound_driver is None)
network_context = context.network
assert(isinstance(network_context, api.NetworkContext))
self._check_network_context(network_context, False)
@ -112,7 +135,12 @@ class TestMechanismDriver(api.MechanismDriver):
self._check_port_context(context, False)
def bind_port(self, context):
self._check_port_context(context, False)
# REVISIT(rkukura): Currently, bind_port() is called as part
# of either a create or update transaction. The fix for bug
# 1276391 will change it to be called outside any transaction,
# so the context.original* will no longer be available.
self._check_port_context(context, context.original is not None)
host = context.current.get(portbindings.HOST_ID, None)
segment = context.network.network_segments[0][api.ID]
if host == "host-ovs-no_filter":
@ -123,8 +151,18 @@ class TestMechanismDriver(api.MechanismDriver):
{portbindings.CAP_PORT_FILTER: True})
def validate_port_binding(self, context):
self._check_port_context(context, False)
# REVISIT(rkukura): Currently, validate_port_binding() is
# called as part of either a create or update transaction. The
# fix for bug 1276391 will change it to be called outside any
# transaction (or eliminate it altogether), so the
# context.original* will no longer be available.
self._check_port_context(context, context.original is not None)
return True
def unbind_port(self, context):
self._check_port_context(context, False)
# REVISIT(rkukura): Currently, unbind_port() is called as part
# of either an update or delete transaction. The fix for bug
# 1276391 will change it to be called outside any transaction
# (or eliminate it altogether), so the context.original* will
# no longer be available.
self._check_port_context(context, context.original is not None)

View File

@ -108,18 +108,18 @@ class PortBindingTestCase(test_plugin.NeutronDbPluginV2TestCase):
self.assertFalse(notify_mock.called)
def test_update_with_new_host_binding_notifies_agent(self):
self._test_update_port_binding('host-ovs-no-filter',
'host-bridge-no-filter')
self._test_update_port_binding('host-ovs-no_filter',
'host-bridge-filter')
def test_update_with_same_host_binding_does_not_notify(self):
self._test_update_port_binding('host-ovs-no-filter',
'host-ovs-no-filter')
self._test_update_port_binding('host-ovs-no_filter',
'host-ovs-no_filter')
def test_update_without_binding_does_not_notify(self):
self._test_update_port_binding('host-ovs-no-filter')
self._test_update_port_binding('host-ovs-no_filter')
def testt_update_from_empty_to_host_binding_notifies_agent(self):
self._test_update_port_binding('', 'host-ovs-no-filter')
self._test_update_port_binding('', 'host-ovs-no_filter')
def test_update_from_host_to_empty_binding_notifies_agent(self):
self._test_update_port_binding('host-ovs-no-filter', '')
self._test_update_port_binding('host-ovs-no_filter', '')