def remove_parallel_switch_links(anm): g_phy = anm['phy'] subs = ank_utils.connected_subgraphs(g_phy, g_phy.switches()) for component in subs: log.debug("Checking for multiple links to switch cluster %s" % str(sorted(component))) # Collect all links into this cluster external_edges = [] for switch in component: for edge in switch.edges(): if edge.dst not in component: external_edges.append(edge) # Group by the node they link to from collections import defaultdict check_dict = defaultdict(list) for edge in external_edges: check_dict[edge.dst].append(edge) # Check to see if any nodes have more than one link into this aggregate for dst, edges in check_dict.items(): if len(edges) > 1: edges_to_remove = sorted(edges)[1:] # remove all but first interfaces = ", ".join( sorted(str(edge.dst_int['phy']) for edge in edges)) interfaces_to_disconnect = ", ".join( sorted( str(edge.dst_int['phy']) for edge in edges_to_remove)) dst.log.warning( "Multiple edges exist to same switch cluster: %s (%s). Removing edges from interfaces %s" % (str(sorted(component)), interfaces, interfaces_to_disconnect)) g_phy.remove_edges_from(edges_to_remove)
def remove_parallel_switch_links(anm): g_phy = anm['phy'] subs = ank_utils.connected_subgraphs(g_phy, g_phy.switches()) for component in subs: log.debug("Checking for multiple links to switch cluster %s" % str(sorted(component))) # Collect all links into this cluster external_edges = [] for switch in component: for edge in switch.edges(): if edge.dst not in component: external_edges.append(edge) # Group by the node they link to from collections import defaultdict check_dict = defaultdict(list) for edge in external_edges: check_dict[edge.dst].append(edge) # Check to see if any nodes have more than one link into this aggregate for dst, edges in check_dict.items(): if len(edges) > 1: edges_to_remove = sorted(edges)[1:] # remove all but first interfaces = ", ".join( sorted(str(edge.dst_int['phy']) for edge in edges)) interfaces_to_disconnect = ", ".join(sorted(str(edge.dst_int['phy']) for edge in edges_to_remove)) dst.log.warning( "Multiple edges exist to same switch cluster: " " %s (%s). Removing edges from interfaces %s" % ( str(sorted(component)), interfaces, interfaces_to_disconnect)) g_phy.remove_edges_from(edges_to_remove)
def build_vlans(anm): import itertools from collections import defaultdict g_l2 = anm['layer2'] g_l1_conn = anm['layer1_conn'] g_phy = anm['phy'] g_vtp = anm.add_overlay('vtp') # g_vlan = anm.add_overlay('vlan') managed_switches = [n for n in g_l2.switches() if n.device_subtype == "managed"] g_vtp.add_nodes_from(g_l1_conn) g_vtp.add_edges_from(g_l1_conn.edges(), retain=['link_type']) edges_to_remove = [edge for edge in g_l1_conn.edges() if edge.link_type == 'is_not_l2'] g_vtp.remove_edges_from(edges_to_remove) edges_to_remove =[] # remove anything not a managed_switch or connected to a managed_switch keep = set() keep.update(managed_switches) for switch in managed_switches: keep.update(switch['vtp'].neighbors()) remove = set(g_vtp) - keep g_vtp.remove_nodes_from(remove) edges_to_remove = [e for e in g_vtp.edges() if not(e.src in managed_switches or e.dst in managed_switches)] g_vtp.remove_edges_from(edges_to_remove) # import ipdb # ipdb.set_trace() set_default_vlans(anm) # copy across vlans from input graph vswitch_id_counter = itertools.count(1) # TODO: aggregate managed switches bcs_to_trim = set() subs = ank_utils.connected_subgraphs(g_vtp, managed_switches) for sub in subs: # identify the VLANs on these switches vlans = defaultdict(list) sub_neigh_ints = set() for switch in sub: l2_switch = switch['layer2'] bcs_to_trim.update(l2_switch.neighbors()) neigh_ints = {iface for iface in switch.neighbor_interfaces() if iface.node.is_l3device() and iface.node not in sub} sub_neigh_ints.update(neigh_ints) for interface in sub_neigh_ints: # store keyed by vlan id vlan = interface['vtp'].vlan vlans[vlan].append(interface) log.debug("Vlans for sub %s are %s", sub, vlans) # create a virtual switch for each # TODO: naming: if this is the only pair then name after these, else # use the switch names too #vswitch_prefix = "_".join(str(sw) for sw in sub) vswitches = [] # store to connect trunks for vlan, interfaces in vlans.items(): # create a virtual switch vswitch_id = "vswitch%s" % vswitch_id_counter.next() # vswitch = g_vlan.add_node(vswitch_id) vswitch = g_l2.add_node(vswitch_id) # TODO: check of switch or just broadcast_domain for higher layer # purposes vswitch.device_type = "switch" vswitch.device_subtype = "virtual" vswitches.append(vswitch) # TODO: layout based on midpoint of previous? # or if same number as real switches, use their co-ordinates? # and then check for coincident? vswitch.x = sum( i.node['phy'].x for i in interfaces) / len(interfaces) + 50 vswitch.y = sum( i.node['phy'].y for i in interfaces) / len(interfaces) + 50 vswitch.vlan = vlan vswitch['layer2'].broadcast_domain = True vswitch['layer2'].vlan = vlan # and connect from vswitch to the interfaces edges_to_add = [(vswitch, iface) for iface in interfaces] g_l2.add_edges_from(edges_to_add, retain=['link_type']) # remove the physical switches g_l2.remove_nodes_from(bcs_to_trim) g_l2.remove_nodes_from(sub) # TODO: also remove any broadcast domains no longer connected # g_l2.remove_nodes_from(disconnected_bcs) # Note: we don't store the interface names as ciuld clobber # eg came from two physical switches, each on gige0 # if need, work backwards from the router iface and its connectivity # and add the trunks # TODO: these need annotations! # create trunks edges_to_add = list(itertools.combinations(vswitches, 2)) # TODO: ensure only once # TODO: filter so only one direction # g_vlan.add_edges_from(edges_to_add, trunk=True) g_vtp.add_edges_from(edges_to_add, trunk=True, retain=['link_type'])
def build_vlans(anm): import itertools from collections import defaultdict g_l2 = anm['layer2'] g_vlan_trunk = anm.add_overlay('vlan_trunk') g_vlans = anm.add_overlay('vlans') managed_switches = [n for n in g_l2.switches() if n.device_subtype == "managed"] # copy across vlans from input graph for router in g_l2.routers(): for interface in router.physical_interfaces(): interface.vlan = interface['input'].vlan if not interface.vlan: pass # check if connectde to a managed switch, if so, warn vswitch_id_counter = itertools.count(1) subs = ank_utils.connected_subgraphs(g_l2, managed_switches) for sub in subs: # identify the VLANs on these switches vlans = defaultdict(list) for switch in sub: neigh_ints = [i.neighbors()[0] for i in switch.interfaces() if i.is_bound] router_ints = [i for i in neigh_ints if i.node.is_router()] for interface in router_ints: # store keyed by vlan id vlans[interface.vlan].append(interface) # create a virtual switch for each # TODO: naming: if this is the only pair then name after these, else # use the switch names too #vswitch_prefix = "_".join(str(sw) for sw in sub) vswitches = [] # store to connect trunks for vlan, interfaces in vlans.items(): # create a virtual switch vswitch_id = "vswitch%s" % vswitch_id_counter.next() vswitch = g_vlans.add_node(vswitch_id) vswitch.device_type = "switch" vswitch.device_subtype = "virtual" vswitches.append(vswitch) # TODO: layout based on midpoint of previous? # or if same number as real switches, use their co-ordinates? # and then check for coincident? vswitch.x = sum( i.node['phy'].x for i in interfaces) / len(interfaces) + 50 vswitch.y = sum( i.node['phy'].y for i in interfaces) / len(interfaces) + 50 vswitch.vlan = vlan g_vlan_trunk.add_node(vswitch) # and connect from vswitch to the interfaces edges_to_add = [(vswitch, iface) for iface in interfaces] g_l2.add_edges_from(edges_to_add) # remove the physical switches g_l2.remove_nodes_from(sub) # Note: we don't store the interface names as ciuld clobber # eg came from two physical switches, each on gige0 # if need, work backwards from the router iface and its connectivity # and add the trunks # TODO: these need annotations! # create trunks edges_to_add = list(itertools.combinations(vswitches, 2)) # TODO: ensure only once # TODO: filter so only one direction g_l2.add_edges_from(edges_to_add, trunk=True) g_vlan_trunk.add_edges_from(edges_to_add, trunk=True)
def build_vlans(anm): import itertools from collections import defaultdict g_l2 = anm['layer2'] g_l1_conn = anm['layer1_conn'] g_phy = anm['phy'] g_vtp = anm.add_overlay('vtp') # g_vlan = anm.add_overlay('vlan') managed_switches = [ n for n in g_l2.switches() if n.device_subtype == "managed" ] g_vtp.add_nodes_from(g_l1_conn) g_vtp.add_edges_from(g_l1_conn.edges(), retain=['link_type']) edges_to_remove = [ edge for edge in g_l1_conn.edges() if edge.link_type == 'is_not_l2' ] g_vtp.remove_edges_from(edges_to_remove) edges_to_remove = [] # remove anything not a managed_switch or connected to a managed_switch keep = set() keep.update(managed_switches) for switch in managed_switches: keep.update(switch['vtp'].neighbors()) remove = set(g_vtp) - keep g_vtp.remove_nodes_from(remove) edges_to_remove = [ e for e in g_vtp.edges() if not (e.src in managed_switches or e.dst in managed_switches) ] g_vtp.remove_edges_from(edges_to_remove) # import ipdb # ipdb.set_trace() set_default_vlans(anm) # copy across vlans from input graph vswitch_id_counter = itertools.count(1) # TODO: aggregate managed switches bcs_to_trim = set() subs = ank_utils.connected_subgraphs(g_vtp, managed_switches) for sub in subs: # identify the VLANs on these switches vlans = defaultdict(list) sub_neigh_ints = set() for switch in sub: l2_switch = switch['layer2'] bcs_to_trim.update(l2_switch.neighbors()) neigh_ints = { iface for iface in switch.neighbor_interfaces() if iface.node.is_l3device() and iface.node not in sub } sub_neigh_ints.update(neigh_ints) for interface in sub_neigh_ints: # store keyed by vlan id vlan = interface['vtp'].vlan vlans[vlan].append(interface) log.debug("Vlans for sub %s are %s", sub, vlans) # create a virtual switch for each # TODO: naming: if this is the only pair then name after these, else # use the switch names too #vswitch_prefix = "_".join(str(sw) for sw in sub) vswitches = [] # store to connect trunks for vlan, interfaces in vlans.items(): # create a virtual switch vswitch_id = "vswitch%s" % vswitch_id_counter.next() # vswitch = g_vlan.add_node(vswitch_id) vswitch = g_l2.add_node(vswitch_id) # TODO: check of switch or just broadcast_domain for higher layer # purposes vswitch.device_type = "switch" vswitch.device_subtype = "virtual" vswitches.append(vswitch) # TODO: layout based on midpoint of previous? # or if same number as real switches, use their co-ordinates? # and then check for coincident? vswitch.x = sum(i.node['phy'].x for i in interfaces) / len(interfaces) + 50 vswitch.y = sum(i.node['phy'].y for i in interfaces) / len(interfaces) + 50 vswitch.vlan = vlan vswitch['layer2'].broadcast_domain = True vswitch['layer2'].vlan = vlan # and connect from vswitch to the interfaces edges_to_add = [(vswitch, iface) for iface in interfaces] g_l2.add_edges_from(edges_to_add, retain=['link_type']) # remove the physical switches g_l2.remove_nodes_from(bcs_to_trim) g_l2.remove_nodes_from(sub) # TODO: also remove any broadcast domains no longer connected # g_l2.remove_nodes_from(disconnected_bcs) # Note: we don't store the interface names as ciuld clobber # eg came from two physical switches, each on gige0 # if need, work backwards from the router iface and its connectivity # and add the trunks # TODO: these need annotations! # create trunks edges_to_add = list(itertools.combinations(vswitches, 2)) # TODO: ensure only once # TODO: filter so only one direction # g_vlan.add_edges_from(edges_to_add, trunk=True) g_vtp.add_edges_from(edges_to_add, trunk=True, retain=['link_type'])