Skip to content

Commit d8261fd

Browse files
committed
Add support for Neutron routed segments
When routed segments are in use, a network's 'provider:segmentation_id' is not populated. Instead, we can make use of the port context binding levels to provide access to the segment identifiers and the physical network name. Where a port is already mapped to a subnet, the correct segment to bind to can then be determined, in a similar way to upstream Neutron change I56b22820d29b2d57faf28a2f9b685ab0b2c924b4 Change-Id: Iea7df37b954ff860f414e81a7384449ac4f9d89b
1 parent 6711765 commit d8261fd

File tree

2 files changed

+86
-101
lines changed

2 files changed

+86
-101
lines changed

networking_generic_switch/generic_switch_mech.py

Lines changed: 38 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@ def update_port_postcommit(self, context):
341341
state changes that it does not know or care about.
342342
"""
343343
port = context.current
344-
network = context.network.current
344+
segment = context.top_bound_segment
345345
if self._is_port_bound(port):
346346
binding_profile = port['binding:profile']
347347
local_link_information = binding_profile.get(
@@ -358,7 +358,7 @@ def update_port_postcommit(self, context):
358358
return
359359
# If binding has already succeeded, we should have valid links
360360
# at this point, but check just in case.
361-
if not self._is_link_valid(port, network):
361+
if not self._is_link_valid(port, segment):
362362
return
363363
is_802_3ad = self._is_802_3ad(port)
364364
for link in local_link_information:
@@ -370,7 +370,7 @@ def update_port_postcommit(self, context):
370370
ngs_mac_address=switch_id)
371371

372372
# If segmentation ID is None, set vlan 1
373-
segmentation_id = network.get('provider:segmentation_id') or 1
373+
segmentation_id = segment.get(api.SEGMENTATION_ID) or 1
374374
LOG.debug("Putting switch port %(switch_port)s on "
375375
"%(switch_info)s in vlan %(segmentation_id)s",
376376
{'switch_port': port_id, 'switch_info': switch_info,
@@ -390,10 +390,10 @@ def update_port_postcommit(self, context):
390390
GENERIC_SWITCH_ENTITY)
391391
elif self._is_port_bound(context.original):
392392
# The port has been unbound. This will cause the local link
393-
# information to be lost, so remove the port from the network on
393+
# information to be lost, so remove the port from the segment on
394394
# the switch now while we have the required information.
395-
self._unplug_port_from_network(context.original,
396-
context.network.current)
395+
self._unplug_port_from_segment(context.original,
396+
context.original_top_bound_segment)
397397

398398
def delete_port_precommit(self, context):
399399
"""Delete resources of a port.
@@ -422,7 +422,7 @@ def delete_port_postcommit(self, context):
422422

423423
port = context.current
424424
if self._is_port_bound(port):
425-
self._unplug_port_from_network(port, context.network.current)
425+
self._unplug_port_from_segment(port, context.top_bound_segment)
426426

427427
def bind_port(self, context):
428428
"""Attempt to bind a port.
@@ -471,34 +471,50 @@ def bind_port(self, context):
471471
# For more info please read docstring.
472472

473473
port = context.current
474-
network = context.network.current
475474
binding_profile = port['binding:profile']
476475
local_link_information = binding_profile.get('local_link_information')
477476

478477
if self._is_port_supported(port) and local_link_information:
478+
# Filter segments where port is already assigned to subnet(s)
479+
subnets = []
480+
for fixed_ip in port.get('fixed_ips', []):
481+
subnet_id = fixed_ip.get('subnet_id')
482+
if subnet_id:
483+
subnets.append(context._plugin.get_subnet(
484+
context.plugin_context,
485+
subnet_id))
486+
segments = []
487+
if len(subnets) > 0:
488+
for segment in context.segments_to_bind:
489+
for subnet in subnets:
490+
segment_id = subnet.get('segment_id')
491+
if segment_id is None or segment_id == segment[api.ID]:
492+
segments.append(segment)
493+
else:
494+
segments = context.segments_to_bind
495+
479496
# NOTE(jamesdenton): If any link of the port is invalid, none
480497
# of the links should be processed.
481-
if not self._is_link_valid(port, network):
498+
if not self._is_link_valid(port, segments[0]):
482499
return
483500

484-
segments = context.segments_to_bind
485501
context.set_binding(segments[0][api.ID],
486502
portbindings.VIF_TYPE_OTHER, {})
487503

488504
provisioning_blocks.add_provisioning_component(
489505
context._plugin_context, port['id'], resources.PORT,
490506
GENERIC_SWITCH_ENTITY)
491507

492-
def _is_link_valid(self, port, network):
493-
"""Return whether a link references valid switch and physnet.
508+
def _is_link_valid(self, port, segment):
509+
"""Return whether a link references valid switch and segment.
494510
495511
If the local link information refers to a switch that is not
496512
known to NGS or the switch is not associated with the respective
497513
physnet, the port will not be processed and no exception will
498514
be raised.
499515
500516
:param port: The port to check
501-
:param network: the network mapped to physnet
517+
:param segment: The segment to check against
502518
:returns: Whether the link refers to a configured switch and/or switch
503519
is associated with physnet
504520
"""
@@ -521,9 +537,9 @@ def _is_link_valid(self, port, network):
521537
'device': switch_info})
522538
return False
523539

524-
physnet = network['provider:physical_network']
540+
physnet = segment.get(api.PHYSICAL_NETWORK)
525541
switch_physnets = switch._get_physical_networks()
526-
segmentation_id = network.get('provider:segmentation_id') or 1
542+
segmentation_id = segment.get(api.SEGMENTATION_ID) or 1
527543

528544
if switch_physnets and physnet not in switch_physnets:
529545
LOG.error("Cannot bind port %(port)s as device %(device)s "
@@ -587,15 +603,15 @@ def _is_802_3ad(port):
587603
return False
588604
return local_group_information.get('bond_mode') in ['4', '802.3ad']
589605

590-
def _unplug_port_from_network(self, port, network):
591-
"""Unplug a port from a network.
606+
def _unplug_port_from_segment(self, port, segment):
607+
"""Unplug a port from a segment.
592608
593609
If the configuration required to unplug the port is not present
594610
(e.g. local link information), the port will not be unplugged and no
595611
exception will be raised.
596612
597613
:param port: The port to unplug
598-
:param network: The network from which to unplug the port
614+
:param segment: The segment from which to unplug the port
599615
"""
600616
binding_profile = port['binding:profile']
601617
local_link_information = binding_profile.get('local_link_information')
@@ -613,7 +629,7 @@ def _unplug_port_from_network(self, port, network):
613629
continue
614630
port_id = link.get('port_id')
615631
# If segmentation ID is None, set vlan 1
616-
segmentation_id = network.get('provider:segmentation_id') or 1
632+
segmentation_id = segment.get(api.SEGMENTATION_ID) or 1
617633
LOG.debug("Unplugging port %(port)s on %(switch_info)s from vlan: "
618634
"%(segmentation_id)s",
619635
{'port': port_id, 'switch_info': switch_info,
@@ -627,12 +643,13 @@ def _unplug_port_from_network(self, port, network):
627643
LOG.error("Failed to unplug port %(port_id)s "
628644
"on device: %(switch)s from network %(net_id)s "
629645
"reason: %(exc)s",
630-
{'port_id': port['id'], 'net_id': network['id'],
646+
{'port_id': port['id'],
647+
'net_id': segment['network_id'],
631648
'switch': switch_info, 'exc': e})
632649
raise e
633650
LOG.info('Port %(port_id)s has been unplugged from network '
634651
'%(net_id)s on device %(device)s',
635-
{'port_id': port['id'], 'net_id': network['id'],
652+
{'port_id': port['id'], 'net_id': segment['network_id'],
636653
'device': switch_info})
637654

638655
def _get_devices_by_physnet(self, physnet):

0 commit comments

Comments
 (0)