diff --git a/neutron/plugins/ml2/driver_api.py b/neutron/plugins/ml2/driver_api.py index cc68181c887..943d5297816 100644 --- a/neutron/plugins/ml2/driver_api.py +++ b/neutron/plugins/ml2/driver_api.py @@ -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 diff --git a/neutron/plugins/ml2/driver_context.py b/neutron/plugins/ml2/driver_context.py index 735a5c5e3a4..3c946d3b570 100644 --- a/neutron/plugins/ml2/driver_context.py +++ b/neutron/plugins/ml2/driver_context.py @@ -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'] diff --git a/neutron/plugins/ml2/drivers/cisco/apic/mechanism_apic.py b/neutron/plugins/ml2/drivers/cisco/apic/mechanism_apic.py index 6d62dc35aaa..ea13fcffd7e 100644 --- a/neutron/plugins/ml2/drivers/cisco/apic/mechanism_apic.py +++ b/neutron/plugins/ml2/drivers/cisco/apic/mechanism_apic.py @@ -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 diff --git a/neutron/plugins/ml2/drivers/cisco/nexus/mech_cisco_nexus.py b/neutron/plugins/ml2/drivers/cisco/nexus/mech_cisco_nexus.py index e261502fa5b..5219a0f909d 100644 --- a/neutron/plugins/ml2/drivers/cisco/nexus/mech_cisco_nexus.py +++ b/neutron/plugins/ml2/drivers/cisco/nexus/mech_cisco_nexus.py @@ -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) diff --git a/neutron/plugins/ml2/drivers/freescale/mechanism_fslsdn.py b/neutron/plugins/ml2/drivers/freescale/mechanism_fslsdn.py index 5a0e5a506cc..cc594dc8e2d 100755 --- a/neutron/plugins/ml2/drivers/freescale/mechanism_fslsdn.py +++ b/neutron/plugins/ml2/drivers/freescale/mechanism_fslsdn.py @@ -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, diff --git a/neutron/plugins/ml2/drivers/l2pop/mech_driver.py b/neutron/plugins/ml2/drivers/l2pop/mech_driver.py index 4e3696386f3..8091bbc98ec 100644 --- a/neutron/plugins/ml2/drivers/l2pop/mech_driver.py +++ b/neutron/plugins/ml2/drivers/l2pop/mech_driver.py @@ -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"), diff --git a/neutron/plugins/ml2/drivers/mech_agent.py b/neutron/plugins/ml2/drivers/mech_agent.py index 61a24af6140..08365141e15 100644 --- a/neutron/plugins/ml2/drivers/mech_agent.py +++ b/neutron/plugins/ml2/drivers/mech_agent.py @@ -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) diff --git a/neutron/plugins/ml2/drivers/mech_bigswitch/driver.py b/neutron/plugins/ml2/drivers/mech_bigswitch/driver.py index 165c183575a..fc5a016ea56 100644 --- a/neutron/plugins/ml2/drivers/mech_bigswitch/driver.py +++ b/neutron/plugins/ml2/drivers/mech_bigswitch/driver.py @@ -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, diff --git a/neutron/plugins/ml2/drivers/mech_nuage/driver.py b/neutron/plugins/ml2/drivers/mech_nuage/driver.py index b83e3072858..97d12cd4f21 100644 --- a/neutron/plugins/ml2/drivers/mech_nuage/driver.py +++ b/neutron/plugins/ml2/drivers/mech_nuage/driver.py @@ -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, diff --git a/neutron/plugins/ml2/drivers/mech_sriov/mech_driver.py b/neutron/plugins/ml2/drivers/mech_sriov/mech_driver.py index dd3421dc917..b50e26bd172 100644 --- a/neutron/plugins/ml2/drivers/mech_sriov/mech_driver.py +++ b/neutron/plugins/ml2/drivers/mech_sriov/mech_driver.py @@ -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, diff --git a/neutron/plugins/ml2/drivers/mechanism_odl.py b/neutron/plugins/ml2/drivers/mechanism_odl.py index e7403c8779b..8ebe0da7182 100644 --- a/neutron/plugins/ml2/drivers/mechanism_odl.py +++ b/neutron/plugins/ml2/drivers/mechanism_odl.py @@ -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, diff --git a/neutron/plugins/ml2/plugin.py b/neutron/plugins/ml2/plugin.py index d58c8074a91..2520f9cc617 100644 --- a/neutron/plugins/ml2/plugin.py +++ b/neutron/plugins/ml2/plugin.py @@ -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 diff --git a/neutron/plugins/ml2/rpc.py b/neutron/plugins/ml2/rpc.py index a612ed6a194..36cfc3fcaf9 100644 --- a/neutron/plugins/ml2/rpc.py +++ b/neutron/plugins/ml2/rpc.py @@ -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: diff --git a/neutron/tests/unit/ml2/_test_mech_agent.py b/neutron/tests/unit/ml2/_test_mech_agent.py index 71aeecf13c1..c69d837f9b9 100644 --- a/neutron/tests/unit/ml2/_test_mech_agent.py +++ b/neutron/tests/unit/ml2/_test_mech_agent.py @@ -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 diff --git a/neutron/tests/unit/ml2/drivers/cisco/apic/test_cisco_apic_mechanism_driver.py b/neutron/tests/unit/ml2/drivers/cisco/apic/test_cisco_apic_mechanism_driver.py index 58b5638c186..4459e9d4990 100644 --- a/neutron/tests/unit/ml2/drivers/cisco/apic/test_cisco_apic_mechanism_driver.py +++ b/neutron/tests/unit/ml2/drivers/cisco/apic/test_cisco_apic_mechanism_driver.py @@ -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): diff --git a/neutron/tests/unit/ml2/drivers/cisco/nexus/test_cisco_mech.py b/neutron/tests/unit/ml2/drivers/cisco/nexus/test_cisco_mech.py index e1f6388ff50..a0d1c390589 100644 --- a/neutron/tests/unit/ml2/drivers/cisco/nexus/test_cisco_mech.py +++ b/neutron/tests/unit/ml2/drivers/cisco/nexus/test_cisco_mech.py @@ -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. diff --git a/neutron/tests/unit/ml2/drivers/cisco/nexus/test_cisco_nexus.py b/neutron/tests/unit/ml2/drivers/cisco/nexus/test_cisco_nexus.py index 58100149fcf..4f9cefdcfe9 100644 --- a/neutron/tests/unit/ml2/drivers/cisco/nexus/test_cisco_nexus.py +++ b/neutron/tests/unit/ml2/drivers/cisco/nexus/test_cisco_nexus.py @@ -93,7 +93,7 @@ class FakePortContext(object): return self._network @property - def bound_segment(self): + def bottom_bound_segment(self): return self._segment diff --git a/neutron/tests/unit/ml2/drivers/mechanism_logger.py b/neutron/tests/unit/ml2/drivers/mechanism_logger.py index 401badb1663..21996208061 100644 --- a/neutron/tests/unit/ml2/drivers/mechanism_logger.py +++ b/neutron/tests/unit/ml2/drivers/mechanism_logger.py @@ -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): diff --git a/neutron/tests/unit/ml2/drivers/mechanism_test.py b/neutron/tests/unit/ml2/drivers/mechanism_test.py index 090dfad4370..324a873bf2c 100644 --- a/neutron/tests/unit/ml2/drivers/mechanism_test.py +++ b/neutron/tests/unit/ml2/drivers/mechanism_test.py @@ -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}) diff --git a/neutron/tests/unit/ml2/test_rpcapi.py b/neutron/tests/unit/ml2/test_rpcapi.py index 8ed1c9c0fa5..a3b4955f62a 100644 --- a/neutron/tests/unit/ml2/test_rpcapi.py +++ b/neutron/tests/unit/ml2/test_rpcapi.py @@ -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',