Merge "ML2: Driver API changes for hierarchical port binding"
This commit is contained in:
commit
f2b1ef2c40
|
@ -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
|
||||
|
||||
|
|
|
@ -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']
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -93,7 +93,7 @@ class FakePortContext(object):
|
|||
return self._network
|
||||
|
||||
@property
|
||||
def bound_segment(self):
|
||||
def bottom_bound_segment(self):
|
||||
return self._segment
|
||||
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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})
|
||||
|
|
|
@ -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',
|
||||
|
|
Loading…
Reference in New Issue