Merge "ML2: Driver API changes for hierarchical port binding"

This commit is contained in:
Jenkins 2015-01-26 23:19:55 +00:00 committed by Gerrit Code Review
commit f2b1ef2c40
20 changed files with 325 additions and 118 deletions

View File

@ -26,6 +26,12 @@ NETWORK_TYPE = 'network_type'
PHYSICAL_NETWORK = 'physical_network'
SEGMENTATION_ID = 'segmentation_id'
# The following keys are used in the binding level dictionaries
# available via the binding_levels and original_binding_levels
# PortContext properties.
BOUND_DRIVER = 'bound_driver'
BOUND_SEGMENT = 'bound_segment'
@six.add_metaclass(abc.ABCMeta)
class TypeDriver(object):
@ -260,17 +266,100 @@ class PortContext(object):
pass
@abc.abstractproperty
def bound_segment(self):
"""Return the currently bound segment dictionary."""
def binding_levels(self):
"""Return dictionaries describing the current binding levels.
This property returns a list of dictionaries describing each
binding level if the port is bound or partially bound, or None
if the port is unbound. Each returned dictionary contains the
name of the bound driver under the BOUND_DRIVER key, and the
bound segment dictionary under the BOUND_SEGMENT key.
The first entry (index 0) describes the top-level binding,
which always involves one of the port's network's static
segments. In the case of a hierarchical binding, subsequent
entries describe the lower-level bindings in descending order,
which may involve dynamic segments. Adjacent levels where
different drivers bind the same static or dynamic segment are
possible. The last entry (index -1) describes the bottom-level
binding that supplied the port's binding:vif_type and
binding:vif_details attribute values.
Within calls to MechanismDriver.bind_port, descriptions of the
levels above the level currently being bound are returned.
"""
pass
@abc.abstractproperty
def original_bound_segment(self):
"""Return the original bound segment dictionary.
def original_binding_levels(self):
"""Return dictionaries describing the original binding levels.
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.
This property returns a list of dictionaries describing each
original binding level if the port was previously bound, or
None if the port was unbound. The content is as described for
the binding_levels property.
This property is only valid within calls to
update_port_precommit and update_port_postcommit. It returns
None otherwise.
"""
pass
@abc.abstractproperty
def top_bound_segment(self):
"""Return the current top-level bound segment dictionary.
This property returns the current top-level bound segment
dictionary, or None if the port is unbound. For a bound port,
top_bound_segment is equivalent to
binding_levels[0][BOUND_SEGMENT], and returns one of the
port's network's static segments.
"""
pass
@abc.abstractproperty
def original_top_bound_segment(self):
"""Return the original top-level bound segment dictionary.
This property returns the original top-level bound segment
dictionary, or None if the port was previously unbound. For a
previously bound port, original_top_bound_segment is
equivalent to original_binding_levels[0][BOUND_SEGMENT], and
returns one of the port's network's static segments.
This property is only valid within calls to
update_port_precommit and update_port_postcommit. It returns
None otherwise.
"""
pass
@abc.abstractproperty
def bottom_bound_segment(self):
"""Return the current bottom-level bound segment dictionary.
This property returns the current bottom-level bound segment
dictionary, or None if the port is unbound. For a bound port,
bottom_bound_segment is equivalent to
binding_levels[-1][BOUND_SEGMENT], and returns the segment
whose binding supplied the port's binding:vif_type and
binding:vif_details attribute values.
"""
pass
@abc.abstractproperty
def original_bottom_bound_segment(self):
"""Return the original bottom-level bound segment dictionary.
This property returns the orignal bottom-level bound segment
dictionary, or None if the port was previously unbound. For a
previously bound port, original_bottom_bound_segment is
equivalent to original_binding_levels[-1][BOUND_SEGMENT], and
returns the segment whose binding supplied the port's previous
binding:vif_type and binding:vif_details attribute values.
This property is only valid within calls to
update_port_precommit and update_port_postcommit. It returns
None otherwise.
"""
pass
@ -289,17 +378,18 @@ class PortContext(object):
pass
@abc.abstractproperty
def bound_driver(self):
"""Return the currently bound mechanism driver name."""
pass
def segments_to_bind(self):
"""Return the list of segments with which to bind the port.
@abc.abstractproperty
def original_bound_driver(self):
"""Return the original bound mechanism driver name.
This property returns the list of segment dictionaries with
which the mechanism driver may bind the port. When
establishing a top-level binding, these will be the port's
network's static segments. For each subsequent level, these
will be the segments passed to continue_binding by the
mechanism driver that bound the level above.
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.
This property is only valid within calls to
MechanismDriver.bind_port. It returns None otherwise.
"""
pass
@ -315,16 +405,36 @@ class PortContext(object):
@abc.abstractmethod
def set_binding(self, segment_id, vif_type, vif_details,
status=None):
"""Set the binding for the port.
"""Set the bottom-level binding for the port.
:param segment_id: Network segment bound for the port.
:param vif_type: The VIF type for the bound port.
:param vif_details: Dictionary with details for VIF driver.
:param status: Port status to set if not None.
Called by MechanismDriver.bind_port to indicate success and
specify binding details to use for port. The segment_id must
identify an item in network.network_segments.
This method is called by MechanismDriver.bind_port to indicate
success and specify binding details to use for port. The
segment_id must identify an item in the current value of the
segments_to_bind property.
"""
pass
@abc.abstractmethod
def continue_binding(self, segment_id, next_segments_to_bind):
"""Continue binding the port with different segments.
:param segment_id: Network segment partially bound for the port.
:param next_segments_to_bind: Segments to continue binding with.
This method is called by MechanismDriver.bind_port to indicate
it was able to partially bind the port, but that one or more
additional mechanism drivers are required to complete the
binding. The segment_id must identify an item in the current
value of the segments_to_bind property. The list of segments
IDs passed as next_segments_to_bind identify dynamic (or
static) segments of the port's network that will be used to
populate segments_to_bind for the next lower level of a
hierarchical binding.
"""
pass
@ -656,22 +766,36 @@ class MechanismDriver(object):
:param context: PortContext instance describing the port
Called outside any transaction to attempt to establish a port
binding using this mechanism driver. If the driver is able to
bind the port, it must call context.set_binding() with the
binding details. If the binding results are committed after
bind_port() returns, they will be seen by all mechanism
drivers as update_port_precommit() and
update_port_postcommit() calls.
This method is called outside any transaction to attempt to
establish a port binding using this mechanism driver. Bindings
may be created at each of multiple levels of a hierarchical
network, and are established from the top level downward. At
each level, the mechanism driver determines whether it can
bind to any of the network segments in the
context.segments_to_bind property, based on the value of the
context.host property, any relevant port or network
attributes, and its own knowledge of the network topology. At
the top level, context.segments_to_bind contains the static
segments of the port's network. At each lower level of
binding, it contains static or dynamic segments supplied by
the driver that bound at the level above. If the driver is
able to complete the binding of the port to any segment in
context.segments_to_bind, it must call context.set_binding
with the binding details. If it can partially bind the port,
it must call context.continue_binding with the network
segments to be used to bind at the next lower level.
Note that if some other thread or process concurrently binds
or updates the port, these binding results will not be
committed, and update_port_precommit() and
update_port_postcommit() will not be called on the mechanism
drivers with these results. Because binding results can be
discarded rather than committed, drivers should avoid making
persistent state changes in bind_port(), or else must ensure
that such state changes are eventually cleaned up.
If the binding results are committed after bind_port returns,
they will be seen by all mechanism drivers as
update_port_precommit and update_port_postcommit calls. But if
some other thread or process concurrently binds or updates the
port, these binding results will not be committed, and
update_port_precommit and update_port_postcommit will not be
called on the mechanism drivers with these results. Because
binding results can be discarded rather than committed,
drivers should avoid making persistent state changes in
bind_port, or else must ensure that such state changes are
eventually cleaned up.
"""
pass

View File

@ -16,10 +16,15 @@
from oslo.serialization import jsonutils
from neutron.common import constants
from neutron.common import exceptions as exc
from neutron.extensions import portbindings
from neutron.i18n import _LW
from neutron.openstack.common import log
from neutron.plugins.ml2 import db
from neutron.plugins.ml2 import driver_api as api
LOG = log.getLogger(__name__)
class MechanismDriverContext(object):
"""MechanismDriver context base class."""
@ -109,19 +114,54 @@ class PortContext(MechanismDriverContext, api.PortContext):
return self._network_context
@property
def bound_segment(self):
id = self._binding.segment
if id:
for segment in self._network_context.network_segments:
if segment[api.ID] == id:
return segment
def binding_levels(self):
# TODO(rkukura): Implement for hierarchical port binding.
if self._binding.segment:
return [{
api.BOUND_DRIVER: self._binding.driver,
api.BOUND_SEGMENT: self._expand_segment(self._binding.segment)
}]
@property
def original_bound_segment(self):
def original_binding_levels(self):
# TODO(rkukura): Implement for hierarchical port binding.
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
return [{
api.BOUND_DRIVER: self._original_bound_driver,
api.BOUND_SEGMENT:
self._expand_segment(self._original_bound_segment_id)
}]
@property
def top_bound_segment(self):
# TODO(rkukura): Implement for hierarchical port binding.
if self._binding.segment:
return self._expand_segment(self._binding.segment)
@property
def original_top_bound_segment(self):
# TODO(rkukura): Implement for hierarchical port binding.
if self._original_bound_segment_id:
return self._expand_segment(self._original_bound_segment_id)
@property
def bottom_bound_segment(self):
# TODO(rkukura): Implement for hierarchical port binding.
if self._binding.segment:
return self._expand_segment(self._binding.segment)
@property
def original_bottom_bound_segment(self):
# TODO(rkukura): Implement for hierarchical port binding.
if self._original_bound_segment_id:
return self._expand_segment(self._original_bound_segment_id)
def _expand_segment(self, segment_id):
segment = db.get_segment_by_id(self._plugin_context.session,
segment_id)
if not segment:
LOG.warning(_LW("Could not expand segment %s"), segment_id)
return segment
@property
def host(self):
@ -132,12 +172,9 @@ class PortContext(MechanismDriverContext, api.PortContext):
return self._original_port.get(portbindings.HOST_ID)
@property
def bound_driver(self):
return self._binding.driver
@property
def original_bound_driver(self):
return self._original_bound_driver
def segments_to_bind(self):
# TODO(rkukura): Implement for hierarchical port binding.
return self._network_context.network_segments
def host_agents(self, agent_type):
return self._plugin.get_agents(self._plugin_context,
@ -152,6 +189,11 @@ class PortContext(MechanismDriverContext, api.PortContext):
self._binding.vif_details = jsonutils.dumps(vif_details)
self._new_port_status = status
def continue_binding(self, segment_id, next_segments_to_bind):
# TODO(rkukura): Implement for hierarchical port binding.
msg = _("Hierarchical port binding not yet implemented")
raise exc.Invalid(message=msg)
def allocate_dynamic_segment(self, segment):
network_id = self._network_context.current['id']

View File

@ -92,13 +92,13 @@ class APICMechanismDriver(api.MechanismDriver):
tenant_id = self.name_mapper.tenant(context, tenant_id)
# Get segmentation id
if not context.bound_segment:
segment = context.top_bound_segment
if not segment:
LOG.debug("Port %s is not bound to a segment", port)
return
seg = None
if (context.bound_segment.get(api.NETWORK_TYPE)
in [constants.TYPE_VLAN]):
seg = context.bound_segment.get(api.SEGMENTATION_ID)
if (segment.get(api.NETWORK_TYPE) in [constants.TYPE_VLAN]):
seg = segment.get(api.SEGMENTATION_ID)
# hosts on which this vlan is provisioned
host = context.host
# Create a static path attachment for the host/epg/switchport combo

View File

@ -177,7 +177,8 @@ class CiscoNexusMechanismDriver(api.MechanismDriver):
vlan_already_removed.append(switch_ip)
def _is_vm_migration(self, context):
if not context.bound_segment and context.original_bound_segment:
if (not context.bottom_bound_segment and
context.original_bottom_bound_segment):
return context.host != context.original_host
def _port_action(self, port, segment, func):
@ -201,13 +202,13 @@ class CiscoNexusMechanismDriver(api.MechanismDriver):
# else process update event.
if self._is_vm_migration(context):
self._port_action(context.original,
context.original_bound_segment,
context.original_bottom_bound_segment,
self._delete_nxos_db)
else:
if (self._is_deviceowner_compute(context.current) and
self._is_status_active(context.current)):
self._port_action(context.current,
context.bound_segment,
context.bottom_bound_segment,
self._configure_nxos_db)
def update_port_postcommit(self, context):
@ -217,25 +218,25 @@ class CiscoNexusMechanismDriver(api.MechanismDriver):
# else process update event.
if self._is_vm_migration(context):
self._port_action(context.original,
context.original_bound_segment,
context.original_bottom_bound_segment,
self._delete_switch_entry)
else:
if (self._is_deviceowner_compute(context.current) and
self._is_status_active(context.current)):
self._port_action(context.current,
context.bound_segment,
context.bottom_bound_segment,
self._configure_switch_entry)
def delete_port_precommit(self, context):
"""Delete port pre-database commit event."""
if self._is_deviceowner_compute(context.current):
self._port_action(context.current,
context.bound_segment,
context.bottom_bound_segment,
self._delete_nxos_db)
def delete_port_postcommit(self, context):
"""Delete port non-database commit event."""
if self._is_deviceowner_compute(context.current):
self._port_action(context.current,
context.bound_segment,
context.bottom_bound_segment,
self._delete_switch_entry)

View File

@ -202,7 +202,7 @@ class FslsdnMechanismDriver(api.MechanismDriver):
{'port': context.current['id'],
'network': context.network.current['id']})
# Prepared porting binding data
for segment in context.network.network_segments:
for segment in context.segments_to_bind:
if self.check_segment(segment):
context.set_binding(segment[api.ID],
self.vif_type,

View File

@ -156,7 +156,7 @@ class L2populationMechanismDriver(api.MechanismDriver,
"configuration."))
return
segment = context.bound_segment
segment = context.bottom_bound_segment
if not segment:
LOG.warning(_LW("Port %(port)s updated by agent %(agent)s "
"isn't bound to any segment"),

View File

@ -66,7 +66,7 @@ class AgentMechanismDriverBase(api.MechanismDriver):
for agent in context.host_agents(self.agent_type):
LOG.debug("Checking agent: %s", agent)
if agent['alive']:
for segment in context.network.network_segments:
for segment in context.segments_to_bind:
if self.try_to_bind_segment_for_agent(context, segment,
agent):
LOG.debug("Bound using segment: %s", segment)

View File

@ -127,7 +127,7 @@ class BigSwitchMechanismDriver(plugin.NeutronRestProxyV2Base,
port = copy.deepcopy(context.current)
net = context.network.current
port['network'] = net
port['bound_segment'] = context.bound_segment
port['bound_segment'] = context.top_bound_segment
actx = ctx.get_admin_context()
prepped_port = self._extend_port_dict_binding(actx, port)
prepped_port = self._map_state_and_status(prepped_port)
@ -151,7 +151,7 @@ class BigSwitchMechanismDriver(plugin.NeutronRestProxyV2Base,
# TODO(kevinbenton): check controller to see if the port exists
# so this driver can be run in parallel with others that add
# support for external port bindings
for segment in context.network.network_segments:
for segment in context.segments_to_bind:
if segment[api.NETWORK_TYPE] == pconst.TYPE_VLAN:
context.set_binding(
segment[api.ID], portbindings.VIF_TYPE_BRIDGE,
@ -161,7 +161,7 @@ class BigSwitchMechanismDriver(plugin.NeutronRestProxyV2Base,
# IVS hosts will have a vswitch with the same name as the hostname
if self.does_vswitch_exist(context.host):
for segment in context.network.network_segments:
for segment in context.segments_to_bind:
if segment[api.NETWORK_TYPE] == pconst.TYPE_VLAN:
context.set_binding(
segment[api.ID], portbindings.VIF_TYPE_IVS,

View File

@ -62,8 +62,8 @@ class NuageMechanismDriver(plugin.NuagePlugin,
# talking to backend.
# 1) binding has happened successfully.
# 2) Its a VM port.
if ((not context.original_bound_segment and
context.bound_segment) and
if ((not context.original_top_bound_segment and
context.top_bound_segment) and
port['device_owner'].startswith(port_prefix)):
np_name = cfg.CONF.RESTPROXY.default_net_partition_name
self._create_update_port(context._plugin_context,
@ -80,7 +80,7 @@ class NuageMechanismDriver(plugin.NuagePlugin,
"network %(network)s",
{'port': context.current['id'],
'network': context.network.current['id']})
for segment in context.network.network_segments:
for segment in context.segments_to_bind:
if self._check_segment(segment):
context.set_binding(segment[api.ID],
self.vif_type,

View File

@ -119,7 +119,7 @@ class SriovNicSwitchMechanismDriver(api.MechanismDriver):
self.try_to_bind(context)
def try_to_bind(self, context, agent=None):
for segment in context.network.network_segments:
for segment in context.segments_to_bind:
if self.check_segment(segment, agent):
context.set_binding(segment[api.ID],
self.vif_type,

View File

@ -97,7 +97,7 @@ class OpenDaylightMechanismDriver(api.MechanismDriver):
"network %(network)s",
{'port': context.current['id'],
'network': context.network.current['id']})
for segment in context.network.network_segments:
for segment in context.segments_to_bind:
if self.check_segment(segment):
context.set_binding(segment[api.ID],
self.vif_type,

View File

@ -469,7 +469,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
def _notify_port_updated(self, mech_context):
port = mech_context._port
segment = mech_context.bound_segment
segment = mech_context.bottom_bound_segment
if not segment:
# REVISIT(rkukura): This should notify agent to unplug port
network = mech_context.network.current

View File

@ -71,7 +71,7 @@ class RpcCallbacks(type_tunnel.TunnelRpcCallbackMixin):
{'device': device, 'agent_id': agent_id})
return {'device': device}
segment = port_context.bound_segment
segment = port_context.bottom_bound_segment
port = port_context.current
if not segment:

View File

@ -72,16 +72,38 @@ class FakePortContext(api.PortContext):
return self._network_context
@property
def bound_segment(self):
if self._bound_segment_id:
for segment in self._network_context.network_segments:
if segment[api.ID] == self._bound_segment_id:
return segment
def binding_levels(self):
if self._bound_segment:
return [{
api.BOUND_DRIVER: 'fake_driver',
api.BOUND_SEGMENT: self._expand_segment(self._bound_segment)
}]
@property
def original_bound_segment(self):
def original_binding_levels(self):
return None
@property
def top_bound_segment(self):
return self._expand_segment(self._bound_segment)
@property
def original_top_bound_segment(self):
return None
@property
def bottom_bound_segment(self):
return self._expand_segment(self._bound_segment)
@property
def original_bottom_bound_segment(self):
return None
def _expand_segment(self, segment_id):
for segment in self._network_context.network_segments:
if segment[api.ID] == self._bound_segment_id:
return segment
@property
def host(self):
return ''
@ -91,12 +113,8 @@ class FakePortContext(api.PortContext):
return None
@property
def bound_driver(self):
return None
@property
def original_bound_driver(self):
return None
def segments_to_bind(self):
return self._network_context.network_segments
def host_agents(self, agent_type):
if agent_type == self._agent_type:
@ -109,6 +127,9 @@ class FakePortContext(api.PortContext):
self._bound_vif_type = vif_type
self._bound_vif_details = vif_details
def continue_binding(self, segment_id, next_segments_to_bind):
pass
def allocate_dynamic_segment(self, segment):
pass

View File

@ -321,7 +321,7 @@ class FakePortContext(object):
return self._network
@property
def bound_segment(self):
def top_bound_segment(self):
return self._bound_segment
def set_binding(self, segment_id, vif_type, cap_port_filter):

View File

@ -96,13 +96,13 @@ class CiscoML2MechanismTestCase(test_ml2_plugin.Ml2PluginV2TestCase):
# Mock port context values for bound_segments and 'status'.
self.mock_bound_segment = mock.patch.object(
driver_context.PortContext,
'bound_segment',
'bottom_bound_segment',
new_callable=mock.PropertyMock).start()
self.mock_bound_segment.return_value = BOUND_SEGMENT1
self.mock_original_bound_segment = mock.patch.object(
driver_context.PortContext,
'original_bound_segment',
'original_bottom_bound_segment',
new_callable=mock.PropertyMock).start()
self.mock_original_bound_segment.return_value = None
@ -559,16 +559,16 @@ class TestCiscoPortsV2(CiscoML2MechanismTestCase,
The first one should only change the current host_id and remove the
binding resulting in the mechanism drivers receiving:
PortContext.original['binding:host_id']: previous value
PortContext.original_bound_segment: previous value
PortContext.original_bottom_bound_segment: previous value
PortContext.current['binding:host_id']: current (new) value
PortContext.bound_segment: None
PortContext.bottom_bound_segment: None
The second one binds the new host resulting in the mechanism
drivers receiving:
PortContext.original['binding:host_id']: previous value
PortContext.original_bound_segment: None
PortContext.original_bottom_bound_segment: None
PortContext.current['binding:host_id']: previous value
PortContext.bound_segment: new value
PortContext.bottom_bound_segment: new value
"""
# Create network, subnet and port.

View File

@ -93,7 +93,7 @@ class FakePortContext(object):
return self._network
@property
def bound_segment(self):
def bottom_bound_segment(self):
return self._segment

View File

@ -84,18 +84,14 @@ class LoggerMechanismDriver(api.MechanismDriver):
network_context = context.network
LOG.info(_("%(method)s called with port settings %(current)s "
"(original settings %(original)s) "
"bound to segment %(segment)s "
"(original segment %(original_segment)s) "
"using driver %(driver)s "
"(original driver %(original_driver)s) "
"binding levels %(levels)s "
"(original binding levels %(original_levels)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,
'levels': context.binding_levels,
'original_levels': context.original_binding_levels,
'network': network_context.current})
def create_port_precommit(self, context):

View File

@ -91,12 +91,14 @@ class TestMechanismDriver(api.MechanismDriver):
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)
self._check_unbound(context.binding_levels,
context.top_bound_segment,
context.bottom_bound_segment)
assert(context.current['id'] not in self.bound_ports)
else:
assert(isinstance(context.bound_segment, dict))
assert(context.bound_driver == 'test')
self._check_bound(context.binding_levels,
context.top_bound_segment,
context.bottom_bound_segment)
assert(context.current['id'] in self.bound_ports)
if original_expected:
@ -106,20 +108,41 @@ class TestMechanismDriver(api.MechanismDriver):
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)
self._check_unbound(context.original_binding_levels,
context.original_top_bound_segment,
context.original_bottom_bound_segment)
else:
assert(isinstance(context.original_bound_segment, dict))
assert(context.original_bound_driver == 'test')
self._check_bound(context.original_binding_levels,
context.original_top_bound_segment,
context.original_bottom_bound_segment)
else:
assert(context.original is None)
assert(context.original_bound_segment is None)
assert(context.original_bound_driver is None)
self._check_unbound(context.original_binding_levels,
context.original_top_bound_segment,
context.original_bottom_bound_segment)
network_context = context.network
assert(isinstance(network_context, api.NetworkContext))
self._check_network_context(network_context, False)
def _check_unbound(self, levels, top_segment, bottom_segment):
assert(levels is None)
assert(top_segment is None)
assert(bottom_segment is None)
def _check_bound(self, levels, top_segment, bottom_segment):
assert(isinstance(levels, list))
top_level = levels[0]
assert(isinstance(top_level, dict))
assert(isinstance(top_segment, dict))
assert(top_segment == top_level[api.BOUND_SEGMENT])
assert('test' == top_level[api.BOUND_DRIVER])
bottom_level = levels[-1]
assert(isinstance(bottom_level, dict))
assert(isinstance(bottom_segment, dict))
assert(bottom_segment == bottom_level[api.BOUND_SEGMENT])
assert('test' == bottom_level[api.BOUND_DRIVER])
def create_port_precommit(self, context):
self._check_port_context(context, False)
@ -127,8 +150,8 @@ class TestMechanismDriver(api.MechanismDriver):
self._check_port_context(context, False)
def update_port_precommit(self, context):
if (context.original_bound_driver == 'test' and
context.bound_driver != 'test'):
if (context.original_top_bound_segment and
not context.top_bound_segment):
self.bound_ports.remove(context.original['id'])
self._check_port_context(context, True)
@ -144,8 +167,8 @@ class TestMechanismDriver(api.MechanismDriver):
def bind_port(self, context):
self._check_port_context(context, False)
host = context.current.get(portbindings.HOST_ID, None)
segment = context.network.network_segments[0][api.ID]
host = context.host
segment = context.segments_to_bind[0][api.ID]
if host == "host-ovs-no_filter":
context.set_binding(segment, portbindings.VIF_TYPE_OVS,
{portbindings.CAP_PORT_FILTER: False})

View File

@ -88,7 +88,7 @@ class RpcCallbacksTestCase(base.BaseTestCase):
device='fake_device'))
def test_get_device_details_port_context_without_bounded_segment(self):
self.plugin.get_bound_port_context().bound_segment = None
self.plugin.get_bound_port_context().bottom_bound_segment = None
self.assertEqual(
{'device': 'fake_device'},
self.callbacks.get_device_details('fake_context',