diff --git a/faucet/dp.py b/faucet/dp.py index 5b5a4ba026..0dd2aa269b 100644 --- a/faucet/dp.py +++ b/faucet/dp.py @@ -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) @@ -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.""" @@ -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'] diff --git a/faucet/valve.py b/faucet/valve.py index 0d74d862be..f59c4ea845 100644 --- a/faucet/valve.py +++ b/faucet/valve.py @@ -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, diff --git a/faucet/valve_flood.py b/faucet/valve_flood.py index e3fe8e475d..a9bf71da80 100644 --- a/faucet/valve_flood.py +++ b/faucet/valve_flood.py @@ -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): @@ -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 @@ -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 @@ -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) @@ -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'] @@ -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)) @@ -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. @@ -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(): @@ -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: