Skip to content

Commit

Permalink
Merge pull request #3137 from anarkiwi/stackrefactor
Browse files Browse the repository at this point in the history
refactor for future stack redundancy support.
  • Loading branch information
anarkiwi committed Aug 6, 2019
2 parents 5bc1e34 + 5451552 commit c684cb1
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 62 deletions.
90 changes: 45 additions & 45 deletions faucet/dp.py
Original file line number Diff line number Diff line change
Expand Up @@ -703,49 +703,48 @@ def remove_stack_link(cls, graph, dp, port):

def resolve_stack_topology(self, dps):
"""Resolve inter-DP config for stacking."""
root_dp = None
stack_dps = []
for dp in dps:
if dp.stack is not None:
stack_dps.append(dp)
if 'priority' in dp.stack:
test_config_condition(not isinstance(dp.stack['priority'], int), (
'stack priority must be type %s not %s' % (
int, type(dp.stack['priority']))))
test_config_condition(dp.stack['priority'] <= 0, (
'stack priority must be > 0'))
test_config_condition(root_dp is not None, 'cannot have multiple stack roots')
root_dp = dp

if root_dp is None:
test_config_condition(stack_dps, 'stacking enabled but no root_dp')
stack_dps = [dp for dp in dps if dp.stack is not None]
stack_priority_dps = [dp for dp in stack_dps if 'priority' in dp.stack]
stack_port_dps = [dp for dp in dps if dp.stack_ports]

if not stack_priority_dps:
test_config_condition(stack_dps, 'stacking enabled but no root DP')
return

edge_count = Counter()
for dp in stack_priority_dps:
test_config_condition(not isinstance(dp.stack['priority'], int), (
'stack priority must be type %s not %s' % (
int, type(dp.stack['priority']))))
test_config_condition(dp.stack['priority'] <= 0, (
'stack priority must be > 0'))

root_dps = sorted(stack_priority_dps, key=lambda x: x.stack['priority'])
root_dp = root_dps[0]

edge_count = Counter()
graph = networkx.MultiGraph()
for dp in dps:
if dp.stack_ports:
graph.add_node(dp.name)
for port in dp.stack_ports:
edge_name = self.add_stack_link(graph, dp, port)
edge_count[edge_name] += 1
if graph.size():
for edge_name, count in edge_count.items():
test_config_condition(count != 2, '%s defined only in one direction' % edge_name)
if self.name in graph:
if self.stack is None:
self.stack = {}
self.stack['root_dp'] = root_dp
self.stack['graph'] = graph
longest_path_to_root_len = 0
for dp in graph.nodes():
path_to_root_len = len(self.shortest_path(root_dp.name, src_dp=dp))
test_config_condition(
path_to_root_len == 0, '%s not connected to stack' % dp)
longest_path_to_root_len = max(
path_to_root_len, longest_path_to_root_len)
self.stack['longest_path_to_root_len'] = longest_path_to_root_len
for dp in stack_port_dps:
graph.add_node(dp.name)
for port in dp.stack_ports:
edge_name = self.add_stack_link(graph, dp, port)
edge_count[edge_name] += 1
for edge_name, count in edge_count.items():
test_config_condition(count != 2, '%s defined only in one direction' % edge_name)
if graph.size() and self.name in graph:
if self.stack is None:
self.stack = {}
self.stack.update({
'root_dp': root_dp,
'root_dps': root_dps,
'graph': graph})
longest_path_to_root_len = 0
for dp in graph.nodes():
path_to_root_len = len(self.shortest_path(root_dp.name, src_dp=dp))
test_config_condition(
path_to_root_len == 0, '%s not connected to stack' % dp)
longest_path_to_root_len = max(
path_to_root_len, longest_path_to_root_len)
self.stack['longest_path_to_root_len'] = longest_path_to_root_len

if self.tunnel_acls:
self.finalize_tunnel_acls(dps)
Expand Down Expand Up @@ -800,11 +799,15 @@ def is_stack_root(self):
"""Return True if this DP is the root of the stack."""
return self.stack and 'priority' in self.stack

def longest_path_to_root_len(self):
"""Return length of longest path to root or None."""
if self.stack:
return self.stack.get('longest_path_to_root_len', None)
return None

def is_stack_edge(self):
"""Return True if this DP is a stack edge."""
if self.stack and 'longest_path_to_root_len' in self.stack:
return self.stack['longest_path_to_root_len'] == len(self.shortest_path_to_root())
return False
return self.longest_path_to_root_len() == len(self.shortest_path_to_root())

def peer_stack_up_ports(self, peer_dp):
"""Return list of stack ports that are up towards a peer."""
Expand Down Expand Up @@ -892,9 +895,6 @@ def resolve_vlans(vlan_names):
def resolve_stack_dps():
"""Resolve DP references in stacking config."""
if self.stack_ports:
if self.stack is None:
self.stack = {}
self.stack['externals'] = bool(vlans_with_external_ports)
port_stack_dp = {}
for port in self.stack_ports:
stack_dp = port.stack['dp']
Expand Down
6 changes: 4 additions & 2 deletions faucet/valve.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,10 @@ def dp_init(self, new_dp=None):
self.logger, self.dp.tables['flood'], self.pipeline,
self.dp.group_table, self.dp.groups,
self.dp.combinatorial_port_flood,
self.dp.stack, self.dp.stack_ports,
self.dp.shortest_path_to_root, self.dp.shortest_path_port)
self.dp.stack_ports, self.dp.has_externals,
self.dp.shortest_path_to_root, self.dp.shortest_path_port,
self.dp.longest_path_to_root_len, self.dp.is_stack_root,
self.dp.stack.get('graph', None))
else:
self.flood_manager = valve_flood.ValveFloodManager(
self.logger, self.dp.tables['flood'], self.pipeline,
Expand Down
37 changes: 22 additions & 15 deletions faucet/valve_flood.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,16 +310,25 @@ class ValveFloodStackManager(ValveFloodManager):
def __init__(self, logger, flood_table, pipeline, # pylint: disable=too-many-arguments
use_group_table, groups,
combinatorial_port_flood,
stack, stack_ports,
dp_shortest_path_to_root, shortest_path_port):
stack_ports, has_externals,
dp_shortest_path_to_root, shortest_path_port,
longest_path_to_root_len, is_stack_root,
graph):
super(ValveFloodStackManager, self).__init__(
logger, flood_table, pipeline,
use_group_table, groups,
combinatorial_port_flood)
self.stack = stack
self.stack_ports = stack_ports
self.externals = has_externals
self.shortest_path_port = shortest_path_port
self.dp_shortest_path_to_root = dp_shortest_path_to_root
self.longest_path_to_root_len = longest_path_to_root_len
self.is_stack_root = is_stack_root
self.graph = graph
if self.is_stack_root():
self.logger.info('root DP')
else:
self.logger.info('not root DP')
self._reset_peer_distances()

def _reset_peer_distances(self):
Expand All @@ -333,14 +342,13 @@ def _reset_peer_distances(self):
self.away_from_root_stack_ports = [
port for port, port_peer_distance in port_peer_distances
if port_peer_distance > my_root_distance]
self.externals = self.stack.get('externals', False)
self._set_ext_port_flag = []
self._set_nonext_port_flag = []
if self.externals:
self._set_ext_port_flag = [self._set_ext_flag(self.EXT_PORT_FLAG)]
self._set_nonext_port_flag = [self._set_ext_flag(self.NONEXT_PORT_FLAG)]
self._flood_actions_func = self._flood_actions
stack_size = self.stack.get('longest_path_to_root_len', None)
stack_size = self.longest_path_to_root_len()
if stack_size == 2:
self._flood_actions_func = self._flood_actions_size2

Expand All @@ -358,7 +366,7 @@ def _flood_actions_size2(self, in_port, external_ports,
flood_actions = (
flood_prefix + away_flood_actions + local_flood_actions)

if self._dp_is_root():
if self.is_stack_root():
# Default strategy is flood locally and to non-roots.
if in_port:
# If we have external ports, let the non-roots know we have already flooded
Expand Down Expand Up @@ -387,7 +395,7 @@ def _flood_actions_size2(self, in_port, external_ports,
def _flood_actions(self, in_port, external_ports,
away_flood_actions, toward_flood_actions, local_flood_actions):
# General case for stack with maximum distance > 2
if self._dp_is_root():
if self.is_stack_root():
flood_actions = (
self._set_ext_port_flag + away_flood_actions + local_flood_actions)

Expand Down Expand Up @@ -539,7 +547,7 @@ def _build_mask_flood_rules(self, vlan, eth_dst, eth_dst_mask, # pylint: disable
self.pipeline.filter_priority - self.pipeline.select_priority))

for port in self.stack_ports:
if self._dp_is_root():
if self.is_stack_root():
# On the root switch, only flood from one port where multiply connected to a datapath.
away_up_port = None
remote_dp = port.stack['dp']
Expand Down Expand Up @@ -567,7 +575,7 @@ def _build_mask_flood_rules(self, vlan, eth_dst, eth_dst_mask, # pylint: disable
else:
# Non-root switches don't need to learn hosts that only
# broadcast, to save resources.
if not self._dp_is_root() and eth_dst is not None:
if not self.is_stack_root() and eth_dst is not None:
ofmsgs.extend(self.pipeline.select_packets(
self.flood_table, match,
priority_offset=self.classification_offset))
Expand Down Expand Up @@ -596,10 +604,6 @@ def _build_mask_flood_rules(self, vlan, eth_dst, eth_dst_mask, # pylint: disable
ofmsgs.append(port_flood_ofmsg)
return ofmsgs

def _dp_is_root(self):
"""Return True if this datapath is the root of the stack."""
return 'priority' in self.stack

def _edge_dp_for_host(self, other_valves, pkt_meta):
"""Simple distributed unicast learning.
Expand Down Expand Up @@ -638,6 +642,9 @@ def _edge_dp_for_host(self, other_valves, pkt_meta):
def update_stack_topo(self, event, dp, port=None):
"""Update the stack topo according to the event."""

if self.graph is None:
return

def _stack_topo_up_dp(_dp): # pylint: disable=invalid-name
for port in [port for port in _dp.stack_ports]:
if port.is_stack_up():
Expand All @@ -650,10 +657,10 @@ def _stack_topo_down_dp(_dp): # pylint: disable=invalid-name
_stack_topo_down_port(_dp, port)

def _stack_topo_up_port(_dp, _port): # pylint: disable=invalid-name
_dp.add_stack_link(self.stack['graph'], _dp, _port)
_dp.add_stack_link(self.graph, _dp, _port)

def _stack_topo_down_port(_dp, _port): # pylint: disable=invalid-name
_dp.remove_stack_link(self.stack['graph'], _dp, _port)
_dp.remove_stack_link(self.graph, _dp, _port)

if port:
if event:
Expand Down

0 comments on commit c684cb1

Please sign in to comment.