def build_layer3(anm): """ l3_connectivity graph: switch nodes aggregated and exploded""" g_in = anm['input'] gl2_conn = anm['layer2_conn'] g_l3 = anm.add_overlay("layer3") g_l3.add_nodes_from(gl2_conn, retain=['label', 'devsubtype']) ank_utils.copy_attr_from(g_in, g_l3, "multicast") g_l3.add_nodes_from(g_in.switches(), retain=['asn']) g_l3.add_edges_from(gl2_conn.edges(), retain=['link_type']) edges_to_remove = [ edge for edge in gl2_conn.edges() if edge.link_type == 'is_not_l3' ] g_l3.remove_edges_from(edges_to_remove) edges_to_remove = [] switches = g_l3.switches() ank_utils.aggregate_nodes(g_l3, switches) exploded_edges = ank_utils.explode_nodes(g_l3, switches, retain=['link_type']) # also explode virtual switches vswitches = [ n for n in g_l3.nodes() if n['layer2'].device_type == "switch" and n['layer2'].device_subtype == "virtual" ] # explode each seperately? for edge in exploded_edges: edge.multipoint = True edge.src_int.multipoint = True edge.dst_int.multipoint = True
def apply_design_rules(anm): """Applies appropriate design rules to ANM""" g_in = anm['input'] build_phy(anm) g_phy = anm['phy'] import autonetkit autonetkit.update_http(anm) build_l3_connectivity(anm) check_server_asns(anm) autonetkit.update_http(anm) build_vrf(anm) # need to do before to add loopbacks before ip allocations from autonetkit.design.ip import build_ip, build_ipv4,build_ipv6 build_ip(anm) # ip infrastructure topology #TODO: set defaults at the start, rather than inline, ie set g_in.data.address_family then use later address_family = g_in.data.address_family or "v4" # default is v4 #TODO: can remove the infrastructure now create g_ip seperately if address_family == "None": log.info("IP addressing disabled, disabling routing protocol configuration") anm['phy'].data.enable_routing = False if address_family == "None": log.info("IP addressing disabled, skipping IPv4") anm.add_overlay("ipv4") # create empty so rest of code follows through g_phy.update(g_phy, use_ipv4 = False) elif address_family in ("v4", "dual_stack"): build_ipv4(anm, infrastructure = True) g_phy.update(g_phy, use_ipv4 = True) elif address_family == "v6": # Allocate v4 loopbacks for router ids build_ipv4(anm, infrastructure = False) g_phy.update(g_phy, use_ipv4 = False) #TODO: Create a collision domain overlay for ip addressing - l2 overlay? if address_family == "None": log.info("IP addressing disabled, not allocating IPv6") anm.add_overlay("ipv6") # create empty so rest of code follows through g_phy.update(g_phy, use_ipv6 = False) elif address_family in ("v6", "dual_stack"): build_ipv6(anm) g_phy.update(g_phy, use_ipv6 = True) else: anm.add_overlay("ipv6") # placeholder for compiler logic default_igp = g_in.data.igp or "ospf" non_igp_nodes = [n for n in g_in if not n.igp] #TODO: should this be modifying g_in? g_in.update(non_igp_nodes, igp=default_igp) # store igp onto each node ank_utils.copy_attr_from(g_in, g_phy, "include_csr") try: from autonetkit_cisco import build_network as cisco_build_network except ImportError, e: log.debug("Unable to load autonetkit_cisco %s" % e)
def apply_design_rules(anm): """Applies appropriate design rules to ANM""" g_in = anm['input'] build_phy(anm) g_phy = anm['phy'] from autonetkit.design.osi_layers import (build_layer2, build_layer2_broadcast, build_layer3) build_layer2(anm) build_layer2_broadcast(anm) build_layer3(anm) build_l3_connectivity(anm) check_server_asns(anm) from autonetkit.design.mpls import build_vrf build_vrf(anm) # do before to add loopbacks before ip allocations from autonetkit.design.ip import build_ip, build_ipv4, build_ipv6 # TODO: replace this with layer2 overlay topology creation build_ip(anm) # ip infrastructure topology address_family = g_in.data.address_family or "v4" # default is v4 # TODO: can remove the infrastructure now create g_ip seperately if address_family == "None": log.info("IP addressing disabled, disabling routing protocol ", "configuration") anm['phy'].data.enable_routing = False if address_family == "None": log.info("IP addressing disabled, skipping IPv4") anm.add_overlay("ipv4") # create empty so rest of code follows g_phy.update(g_phy, use_ipv4=False) elif address_family in ("v4", "dual_stack"): build_ipv4(anm, infrastructure=True) g_phy.update(g_phy, use_ipv4=True) elif address_family == "v6": # Allocate v4 loopbacks for router ids build_ipv4(anm, infrastructure=False) g_phy.update(g_phy, use_ipv4=False) # TODO: Create collision domain overlay for ip addressing - l2 overlay? if address_family == "None": log.info("IP addressing disabled, not allocating IPv6") anm.add_overlay("ipv6") # create empty so rest of code follows g_phy.update(g_phy, use_ipv6=False) elif address_family in ("v6", "dual_stack"): build_ipv6(anm) g_phy.update(g_phy, use_ipv6=True) else: anm.add_overlay("ipv6") # placeholder for compiler logic default_igp = g_in.data.igp or "ospf" ank_utils.set_node_default(g_in, igp=default_igp) ank_utils.copy_attr_from(g_in, g_phy, "include_csr") try: from autonetkit_cisco import build_network as cisco_build_network except ImportError, e: log.debug("Unable to load autonetkit_cisco %s" % e)
def build_rip(anm): """Build rip overlay""" g_in = anm['input'] g_l3 = anm['layer3'] g_rip = anm.add_overlay("rip") g_phy = anm['phy'] if not anm['phy'].data.enable_routing: g_rip.log.info("Routing disabled, not configuring rip") return if not any(n.igp == "rip" for n in g_phy): log.debug("No rip nodes") return rip_nodes = [n for n in g_l3 if n['phy'].igp == "rip"] g_rip.add_nodes_from(rip_nodes) g_rip.add_edges_from(g_l3.edges(), warn=False) ank_utils.copy_int_attr_from(g_l3, g_rip, "multipoint") ank_utils.copy_attr_from( g_in, g_rip, "custom_config_rip", dst_attr="custom_config") g_rip.remove_edges_from( [link for link in g_rip.edges() if link.src.asn != link.dst.asn]) for node in g_rip: node.process_id = node.asn for link in g_rip.edges(): link.metric = 1 # default for edge in g_rip.edges(): for interface in edge.interfaces(): interface.metric = edge.metric interface.multipoint = edge.multipoint
def apply_design_rules(anm): """Applies appropriate design rules to ANM""" g_in = anm['input'] build_phy(anm) g_phy = anm['phy'] build_l3_connectivity(anm) check_server_asns(anm) from autonetkit.design.mpls import build_vrf build_vrf(anm) # need to do before to add loopbacks before ip allocations from autonetkit.design.ip import build_ip, build_ipv4, build_ipv6 #TODO: replace this with layer2 overlay topology creation build_ip(anm) # ip infrastructure topology #TODO: set defaults at the start, rather than inline, ie set g_in.data.address_family then use later address_family = g_in.data.address_family or "v4" # default is v4 #TODO: can remove the infrastructure now create g_ip seperately if address_family == "None": log.info( "IP addressing disabled, disabling routing protocol configuration") anm['phy'].data.enable_routing = False if address_family == "None": log.info("IP addressing disabled, skipping IPv4") anm.add_overlay("ipv4") # create empty so rest of code follows through g_phy.update(g_phy, use_ipv4=False) elif address_family in ("v4", "dual_stack"): build_ipv4(anm, infrastructure=True) g_phy.update(g_phy, use_ipv4=True) elif address_family == "v6": # Allocate v4 loopbacks for router ids build_ipv4(anm, infrastructure=False) g_phy.update(g_phy, use_ipv4=False) #TODO: Create a collision domain overlay for ip addressing - l2 overlay? if address_family == "None": log.info("IP addressing disabled, not allocating IPv6") anm.add_overlay("ipv6") # create empty so rest of code follows through g_phy.update(g_phy, use_ipv6=False) elif address_family in ("v6", "dual_stack"): build_ipv6(anm) g_phy.update(g_phy, use_ipv6=True) else: anm.add_overlay("ipv6") # placeholder for compiler logic default_igp = g_in.data.igp or "ospf" ank_utils.set_node_default(g_in, igp=default_igp) ank_utils.copy_attr_from(g_in, g_phy, "include_csr") try: from autonetkit_cisco import build_network as cisco_build_network except ImportError, e: log.debug("Unable to load autonetkit_cisco %s" % e)
def build_phy(anm): G_in = anm['input'] G_phy = anm['phy'] G_phy.add_nodes_from(G_in, retain=['label', 'update', 'device_type', 'device_subtype', 'asn', 'platform', 'host', 'syntax']) if G_in.data.Creator == "Topology Zoo Toolset": ank.copy_attr_from(G_in, G_phy, "Network") #TODO: move this into graphml (and later gml) reader G_phy.add_edges_from(G_in.edges(type="physical")) G_phy.allocate_interfaces() #TODO: make this automatic if adding to the physical graph?
def build_bgp(anm): """Build iBGP end eBGP overlays""" # eBGP g_in = anm['input'] g_l3 = anm['layer3'] if not anm['phy'].data.enable_routing: log.info("Routing disabled, not configuring BGP") return build_ebgp(anm) build_ebgp_v4(anm) build_ebgp_v6(anm) """TODO: remove from here once compiler updated""" g_bgp = anm.add_overlay("bgp", directed=True) g_bgp.add_nodes_from(g_l3.routers()) edges_to_add = [e for e in g_l3.edges() if e.src in g_bgp and e.dst in g_bgp] g_bgp.add_edges_from(edges_to_add, bidirectional=True) ank_utils.copy_int_attr_from(g_l3, g_bgp, "multipoint") # remove ibgp links """TODO: remove up to here once compiler updated""" ank_utils.copy_attr_from( g_in, g_bgp, "custom_config_bgp", dst_attr="custom_config") # log.info("Building eBGP") ebgp_nodes = [d for d in g_bgp if any( edge.type == 'ebgp' for edge in d.edges())] g_bgp.update(ebgp_nodes, ebgp=True) for ebgp_edge in g_bgp.edges(type="ebgp"): for interface in ebgp_edge.interfaces(): interface.ebgp = True for edge in g_bgp.edges(type='ibgp'): # TODO: need interface querying/selection. rather than hard-coded ids # TODO: create a new port (once API allows) rarher than binding to # loopback zero edge.bind_interface(edge.src, 0) # TODO: need to initialise interface zero to be a loopback rather than physical type # TODO: wat is this for? for node in g_bgp: for interface in node.interfaces(): interface.multipoint = any(e.multipoint for e in interface.edges()) # log.info("Building iBGP") build_ibgp(anm) build_ibgp_v4(anm) build_ibgp_v6(anm)
def build_phy(anm): """Build physical overlay""" g_in = anm['input'] g_phy = anm['phy'] g_phy.add_nodes_from(g_in, retain=['label', 'update', 'device_type', 'asn', 'device_subtype', 'platform', 'host', 'syntax']) if g_in.data.Creator == "Topology Zoo Toolset": ank_utils.copy_attr_from(g_in, g_phy, "Network") g_phy.add_edges_from(g_in.edges(type="physical")) g_phy.allocate_interfaces( ) # TODO: make this automatic if adding to the physical graph?
def build_bgp(anm): """Build iBGP end eBGP overlays""" # eBGP g_in = anm['input'] g_l3 = anm['layer3'] if not anm['phy'].data.enable_routing: log.info("Routing disabled, not configuring BGP") return build_ebgp(anm) build_ebgp_v4(anm) build_ebgp_v6(anm) """TODO: remove from here once compiler updated""" g_bgp = anm.add_overlay("bgp", directed=True) g_bgp.add_nodes_from(g_l3.routers()) edges_to_add = [e for e in g_l3.edges() if e.src in g_bgp and e.dst in g_bgp] g_bgp.add_edges_from(edges_to_add, bidirectional=True) ank_utils.copy_int_attr_from(g_l3, g_bgp, "multipoint") # remove ibgp links """TODO: remove up to here once compiler updated""" ank_utils.copy_attr_from( g_in, g_bgp, "custom_config_bgp", dst_attr="custom_config") log.info("Building eBGP") ebgp_nodes = [d for d in g_bgp if any( edge.type == 'ebgp' for edge in d.edges())] g_bgp.update(ebgp_nodes, ebgp=True) for ebgp_edge in g_bgp.edges(type="ebgp"): for interface in ebgp_edge.interfaces(): interface.ebgp = True for edge in g_bgp.edges(type='ibgp'): # TODO: need interface querying/selection. rather than hard-coded ids # TODO: create a new port (once API allows) rarher than binding to # loopback zero edge.bind_interface(edge.src, 0) # TODO: need to initialise interface zero to be a loopback rather than physical type # TODO: wat is this for? for node in g_bgp: for interface in node.interfaces(): interface.multipoint = any(e.multipoint for e in interface.edges()) log.info("Building iBGP") build_ibgp(anm) build_ibgp_v4(anm) build_ibgp_v6(anm)
def build_phy(anm): """Build physical overlay""" g_in = anm['input'] g_phy = anm['phy'] g_phy.data.enable_routing = g_in.data.enable_routing if g_in.data.mgmt_block: g_phy.data['mgmt_block'] = g_in.data['mgmt_block'] if g_in.data.vpcid_block: g_phy.data['vpcid_block'] = g_in.data['vpcid_block'] if g_phy.data.enable_routing is None: g_in.data.enable_routing = True # default if not set g_phy.add_nodes_from(g_in, retain=['label', 'update', 'device_type', 'devsubtype', 'asn', 'specified_int_names', 'x', 'y', 'device_subtype', 'platform', 'host', 'syntax', 'profile', 'syslog']) if g_in.data.Creator == "Topology Zoo Toolset": ank_utils.copy_attr_from(g_in, g_phy, "Network") ank_utils.set_node_default(g_phy, Network=None) g_phy.add_edges_from(g_in.edges(type="physical")) # TODO: make this automatic if adding to the physical graph? ank_utils.set_node_default(g_phy, use_ipv4=False, use_ipv6=False) ank_utils.copy_attr_from(g_in, g_phy, "custom_config_global", dst_attr="custom_config") for node in g_phy: if node['input'].custom_config_loopback_zero: lo_zero_config = node['input'].custom_config_loopback_zero node.loopback_zero.custom_config = lo_zero_config custom_config_phy_ints = node['input'].custom_config_phy_ints for interface in node: if custom_config_phy_ints: interface.custom_config = custom_config_phy_ints specified_id = interface['input'].get("specified_id") if specified_id: interface.specified_id = specified_id # map across #TODO: tidy this code up for node in g_phy: for interface in node: remote_edges = interface.edges() if len(remote_edges): interface.description = 'to %s' \ % remote_edges[0].dst.label
def build_ipv4(anm, infrastructure=True): """Builds IPv4 graph""" import autonetkit.plugins.ipv4 as ipv4 g_ipv4 = anm.add_overlay("ipv4") g_ip = anm['ip'] g_in = anm['input'] g_ipv4.add_nodes_from( g_ip, retain="collision_domain") # retain if collision domain or not # Copy ASN attribute chosen for collision domains (used in alloc algorithm) ank_utils.copy_attr_from(g_ip, g_ipv4, "asn", nbunch = g_ipv4.nodes("collision_domain")) g_ipv4.add_edges_from(g_ip.edges()) # check if ip ranges have been specified on g_in infra_block, loopback_block, vrf_loopback_block = extract_ipv4_blocks(anm) # See if IP addresses specified on each interface l3_devices = [d for d in g_in if d.device_type in ("router", "server")] manual_alloc_devices = set() for device in l3_devices: physical_interfaces = list(device.physical_interfaces) if all(interface.ipv4_address for interface in physical_interfaces if interface.is_bound ): manual_alloc_devices.add(device) # add as a manual allocated device if manual_alloc_devices == set(l3_devices): manual_alloc_ipv4_infrastructure = True else: manual_alloc_ipv4_infrastructure = False #TODO: need to set allocate_ipv4 by default in the readers if manual_alloc_ipv4_infrastructure: manual_ipv4_infrastructure_allocation(anm) else: ipv4.allocate_infra(g_ipv4, infra_block) if g_in.data.alloc_ipv4_loopbacks is False: manual_ipv4_loopback_allocation(anm) else: ipv4.allocate_loopbacks(g_ipv4, loopback_block) #TODO: need to also support secondary_loopbacks for IPv6 #TODO: only call if secondaries are set ipv4.allocate_vrf_loopbacks(g_ipv4, vrf_loopback_block) #TODO: replace this with direct allocation to interfaces in ip alloc plugin for node in g_ipv4.nodes("is_l3device"): node.loopback_zero.ip_address = node.loopback
def build_phy(anm): """Build physical overlay""" g_in = anm['input'] g_phy = anm['phy'] g_phy.data.enable_routing = g_in.data.enable_routing if g_phy.data.enable_routing is None: g_in.data.enable_routing = True # default if not set g_phy.add_nodes_from(g_in, retain=[ 'label', 'update', 'device_type', 'asn', 'specified_int_names', 'x', 'y', 'device_subtype', 'platform', 'host', 'syntax' ]) if g_in.data.Creator == "Topology Zoo Toolset": ank_utils.copy_attr_from(g_in, g_phy, "Network") ank_utils.set_node_default(g_phy, Network=None) g_phy.add_edges_from(g_in.edges(type="physical")) # TODO: make this automatic if adding to the physical graph? ank_utils.set_node_default(g_phy, use_ipv4=False, use_ipv6=False) ank_utils.copy_attr_from(g_in, g_phy, "custom_config_global", dst_attr="custom_config") for node in g_phy: if node['input'].custom_config_loopback_zero: lo_zero_config = node['input'].custom_config_loopback_zero node.loopback_zero.custom_config = lo_zero_config custom_config_phy_ints = node['input'].custom_config_phy_ints for interface in node: if custom_config_phy_ints: interface.custom_config = custom_config_phy_ints specified_id = interface['input'].get("specified_id") if specified_id: interface.specified_id = specified_id # map across #TODO: tidy this code up for node in g_phy: for interface in node: remote_edges = interface.edges() if len(remote_edges): interface.description = 'to %s' \ % remote_edges[0].dst.label
def apply_design_rules(anm): g_in = anm['input'] build_phy(anm) g_phy = anm['phy'] autonetkit.update_http(anm) build_l3_connectivity(anm) build_vrf(anm) # need to do before to add loopbacks before ip allocations build_ip(anm) # ip infrastructure topology #TODO: set defaults at the start, rather than inline, ie set g_in.data.address_family then use later address_family = g_in.data.address_family or "v4" # default is v4 #TODO: can remove the infrastructure now create g_ip seperately if address_family in ("v4", "dual_stack"): build_ipv4(anm, infrastructure = True) g_phy.update(g_phy, use_ipv4 = True) else: build_ipv4(anm, infrastructure = False) #TODO: Create a collision domain overlay for ip addressing - l2 overlay? if address_family in ("v6", "dual_stack"): build_ipv6(anm) g_phy.update(g_phy, use_ipv6 = True) else: anm.add_overlay("ipv6") # placeholder for compiler logic default_igp = g_in.data.igp or "ospf" non_igp_nodes = [n for n in g_in if not n.igp] #TODO: should this be modifying g_in? g_in.update(non_igp_nodes, igp=default_igp) # store igp onto each node ank_utils.copy_attr_from(g_in, g_phy, "include_csr") build_ospf(anm) build_isis(anm) build_bgp(anm) autonetkit.update_http(anm) # post-processing mark_ebgp_vrf(anm) build_ibgp_vpn_v4(anm) # build after bgp as is based on #autonetkit.update_http(anm) return anm
def build_ipv4(anm, infrastructure=True): """Builds IPv4 graph""" g_ipv4 = anm.add_overlay("ipv4") g_ip = anm["ip"] g_in = anm["input"] g_ipv4.add_nodes_from(g_ip, retain="collision_domain") # retain if collision domain or not # Copy ASN attribute chosen for collision domains (used in alloc algorithm) ank_utils.copy_attr_from(g_ip, g_ipv4, "asn", nbunch=g_ipv4.nodes("collision_domain")) g_ipv4.add_edges_from(g_ip.edges()) autonetkit.update_http(anm) # TODO: need to set allocate_ipv4 by default in the readers if g_in.data.alloc_ipv4_infrastructure is False: manual_ipv4_infrastructure_allocation(anm) else: import autonetkit.plugins.ipv4 as ipv4 ipv4.allocate_ips(g_ipv4, infrastructure=True, loopbacks=False) # ank_utils.save(g_ipv4) if g_in.data.alloc_ipv4_loopbacks is False: manual_ipv4_loopback_allocation(anm) else: import autonetkit.plugins.ipv4 as ipv4 ipv4.allocate_ips(g_ipv4, infrastructure=False, loopbacks=True) # ank_utils.save(g_ipv4) # TODO: need to also support secondary_loopbacks for IPv6 ipv4.allocate_ips(g_ipv4, infrastructure=False, loopbacks=False, secondary_loopbacks=True) autonetkit.update_http(anm) # TODO: replace this with direct allocation to interfaces in ip alloc plugin for node in g_ipv4.nodes("is_l3device"): node.loopback_zero.ip_address = node.loopback for interface in node: edges = list(interface.edges()) if len(edges): edge = edges[0] # first (only) edge interface.ip_address = edge.ip_address interface.subnet = edge.dst.subnet # from collision domain # TODO: also map loopbacks to loopback interface 0 autonetkit.update_http(anm)
def build_phy(anm): """Build physical overlay""" g_in = anm['input'] g_phy = anm['phy'] g_phy.data.enable_routing = g_in.data.enable_routing if g_phy.data.enable_routing is None: g_in.data.enable_routing = True # default if not set g_phy.add_nodes_from(g_in, retain=['label', 'update', 'device_type', 'asn', 'specified_int_names', 'device_subtype', 'platform', 'host', 'syntax']) if g_in.data.Creator == "Topology Zoo Toolset": ank_utils.copy_attr_from(g_in, g_phy, "Network") g_phy.add_edges_from(g_in.edges(type="physical")) # TODO: make this automatic if adding to the physical graph? if g_in.data.Creator == "Maestro": g_phy.data.mgmt_interfaces_enabled = g_in.data.mgmt_interfaces_enabled #TODO: remove this code now allocated externally g_phy.data.mgmt_address_start = g_in.data.mgmt_address_start g_phy.data.mgmt_address_end = g_in.data.mgmt_address_end g_phy.data.mgmt_prefixlen = g_in.data.mgmt_prefixlen g_phy.data.mgmt_prefixlen = g_in.data.mgmt_prefixlen ank_utils.copy_attr_from(g_in, g_phy, "use_cdp") ank_utils.copy_attr_from(g_in, g_phy, "use_onepk") ank_utils.copy_attr_from(g_in, g_phy, "label_full") ank_utils.copy_attr_from(g_in, g_phy, "indices") g_phy.allocate_interfaces() for node in g_phy: for interface in node: specified_id = interface['input'].get("specified_id") if specified_id: interface.specified_id = specified_id # map across for node in g_phy.nodes("specified_int_names"): for interface in node: edge = interface.edges()[0] directed_edge = anm['input_directed'].edge(edge) interface.name = directed_edge.name
def build_eigrp(anm): """Build eigrp overlay""" g_in = anm['input'] # add regardless, so allows quick check of node in anm['isis'] in compilers g_l3 = anm['layer3'] g_eigrp = anm.add_overlay("eigrp") g_phy = anm['phy'] if not anm['phy'].data.enable_routing: g_eigrp.log.info("Routing disabled, not configuring EIGRP") return if not any(n.igp == "eigrp" for n in g_phy): log.debug("No EIGRP nodes") return eigrp_nodes = [n for n in g_l3 if n['phy'].igp == "eigrp"] g_eigrp.add_nodes_from(eigrp_nodes) g_eigrp.add_edges_from(g_l3.edges(), warn=False) ank_utils.copy_int_attr_from(g_l3, g_eigrp, "multipoint") ank_utils.copy_attr_from(g_in, g_eigrp, "custom_config_eigrp", dst_attr="custom_config") # Merge and explode switches ank_utils.aggregate_nodes(g_eigrp, g_eigrp.switches()) exploded_edges = ank_utils.explode_nodes(g_eigrp, g_eigrp.switches()) for edge in exploded_edges: edge.multipoint = True g_eigrp.remove_edges_from( [link for link in g_eigrp.edges() if link.src.asn != link.dst.asn]) for node in g_eigrp: node.process_id = node.asn for link in g_eigrp.edges(): link.metric = 1 # default for edge in g_eigrp.edges(): for interface in edge.interfaces(): interface.metric = edge.metric interface.multipoint = edge.multipoint
def build_eigrp(anm): """Build eigrp overlay""" g_in = anm['input'] # add regardless, so allows quick check of node in anm['isis'] in compilers g_l3 = anm['layer3'] g_eigrp = anm.add_overlay("eigrp") g_phy = anm['phy'] if not anm['phy'].data.enable_routing: g_eigrp.log.info("Routing disabled, not configuring EIGRP") return if not any(n.igp == "eigrp" for n in g_phy): log.debug("No EIGRP nodes") return eigrp_nodes = [n for n in g_l3 if n['phy'].igp == "eigrp"] g_eigrp.add_nodes_from(eigrp_nodes) g_eigrp.add_edges_from(g_l3.edges(), warn=False) ank_utils.copy_int_attr_from(g_l3, g_eigrp, "multipoint") ank_utils.copy_attr_from( g_in, g_eigrp, "custom_config_eigrp", dst_attr="custom_config") # Merge and explode switches ank_utils.aggregate_nodes(g_eigrp, g_eigrp.switches()) exploded_edges = ank_utils.explode_nodes(g_eigrp, g_eigrp.switches()) for edge in exploded_edges: edge.multipoint = True g_eigrp.remove_edges_from( [link for link in g_eigrp.edges() if link.src.asn != link.dst.asn]) for node in g_eigrp: node.process_id = node.asn for link in g_eigrp.edges(): link.metric = 1 # default for edge in g_eigrp.edges(): for interface in edge.interfaces(): interface.metric = edge.metric interface.multipoint = edge.multipoint
def apply_design_rules(anm): g_in = anm['input'] build_phy(anm) g_phy = anm['phy'] build_vrf(anm) # need to do before to add loopbacks before ip allocations build_ip(anm) # ip infrastructure topology #TODO: set defaults at the start, rather than inline, ie set g_in.data.address_family then use later address_family = g_in.data.address_family or "v4" # default is v4 #TODO: can remove the infrastructure now create g_ip seperately if address_family in ("v4", "dual_stack"): build_ipv4(anm, infrastructure=True) g_phy.update(g_phy, use_ipv4=True) else: build_ipv4(anm, infrastructure=False) #TODO: Create a collision domain overlay for ip addressing - l2 overlay? if address_family in ("v6", "dual_stack"): build_ipv6(anm) g_phy.update(g_phy, use_ipv6=True) else: anm.add_overlay("ipv6") # placeholder for compiler logic default_igp = g_in.data.igp or "ospf" non_igp_nodes = [n for n in g_in if not n.igp] #TODO: should this be modifying g_in? g_in.update(non_igp_nodes, igp=default_igp) # store igp onto each node ank_utils.copy_attr_from(g_in, g_phy, "include_csr") build_ospf(anm) build_isis(anm) build_bgp(anm) autonetkit.update_http(anm) # post-processing mark_ebgp_vrf(anm) build_ibgp_vpn_v4(anm) # build after bgp as is based on #autonetkit.update_http(anm) return anm
def build_phy(anm): """Build physical overlay""" g_in = anm['input'] g_phy = anm['phy'] g_phy.add_nodes_from(g_in, retain=['label', 'update', 'device_type', 'asn', 'device_subtype', 'platform', 'host', 'syntax']) if g_in.data.Creator == "Topology Zoo Toolset": ank_utils.copy_attr_from(g_in, g_phy, "Network") g_phy.add_edges_from(g_in.edges(type="physical")) # TODO: make this automatic if adding to the physical graph? g_phy.allocate_interfaces() specified_int_names = g_in.data.specified_int_names if specified_int_names: for node in g_phy: for interface in node: edge = interface.edges()[0] directed_edge = anm['input_directed'].edge(edge) interface.name = directed_edge.name
def build_isis(anm): """Build isis overlay""" g_in = anm['input'] # add regardless, so allows quick check of node in anm['isis'] in compilers g_l3 = anm['layer3'] g_phy = anm['phy'] g_isis = anm.add_overlay("isis") if not anm['phy'].data.enable_routing: g_isis.log.info("Routing disabled, not configuring ISIS") return if not any(n.igp == "isis" for n in g_phy): g_isis.log.debug("No ISIS nodes") return isis_nodes = [n for n in g_l3 if n['phy'].igp == "isis"] g_isis.add_nodes_from(isis_nodes) g_isis.add_edges_from(g_l3.edges(), warn=False) ank_utils.copy_int_attr_from(g_l3, g_isis, "multipoint") ank_utils.copy_attr_from(g_in, g_isis, "custom_config_isis", dst_attr="custom_config") g_isis.remove_edges_from( [link for link in g_isis.edges() if link.src.asn != link.dst.asn]) build_network_entity_title(anm) for node in g_isis.routers(): node.process_id = node.asn for link in g_isis.edges(): link.metric = 1 # default for edge in g_isis.edges(): for interface in edge.interfaces(): interface.metric = edge.metric interface.multipoint = edge.multipoint
def build_ipv4(anm, infrastructure=True): """Builds IPv4 graph""" import autonetkit.plugins.ipv4 as ipv4 g_ipv4 = anm.add_overlay("ipv4") g_ip = anm['ip'] g_in = anm['input'] g_ipv4.add_nodes_from( g_ip, retain="collision_domain") # retain if collision domain or not # Copy ASN attribute chosen for collision domains (used in alloc algorithm) ank_utils.copy_attr_from(g_ip, g_ipv4, "asn", nbunch=g_ipv4.nodes("collision_domain")) g_ipv4.add_edges_from(g_ip.edges()) # check if ip ranges have been specified on g_in infra_block, loopback_block, vrf_loopback_block = extract_ipv4_blocks(anm) #TODO: need to set allocate_ipv4 by default in the readers if g_in.data.alloc_ipv4_infrastructure is False: manual_ipv4_infrastructure_allocation(anm) else: ipv4.allocate_infra(g_ipv4, infra_block) if g_in.data.alloc_ipv4_loopbacks is False: manual_ipv4_loopback_allocation(anm) else: ipv4.allocate_loopbacks(g_ipv4, loopback_block) #TODO: need to also support secondary_loopbacks for IPv6 ipv4.allocate_vrf_loopbacks(g_ipv4, vrf_loopback_block) #TODO: replace this with direct allocation to interfaces in ip alloc plugin for node in g_ipv4.nodes("is_l3device"): node.loopback_zero.ip_address = node.loopback for interface in node: edges = list(interface.edges()) if len(edges): edge = edges[0] # first (only) edge interface.ip_address = edge.ip_address interface.subnet = edge.dst.subnet # from collision domain
def build_phy(anm): """Build physical overlay""" g_in = anm['input'] g_phy = anm['phy'] g_phy.add_nodes_from(g_in, retain=[ 'label', 'update', 'device_type', 'asn', 'specified_int_names', 'device_subtype', 'platform', 'host', 'syntax' ]) if g_in.data.Creator == "Topology Zoo Toolset": ank_utils.copy_attr_from(g_in, g_phy, "Network") if g_in.data.Creator == "Maestro": g_phy.data.mgmt_interfaces_enabled = g_in.data.mgmt_interfaces_enabled g_phy.data.mgmt_address_start = g_in.data.mgmt_address_start g_phy.data.mgmt_address_end = g_in.data.mgmt_address_end g_phy.data.mgmt_prefixlen = g_in.data.mgmt_prefixlen ank_utils.copy_attr_from(g_in, g_phy, "use_cdp") ank_utils.copy_attr_from(g_in, g_phy, "use_onepk") g_phy.add_edges_from(g_in.edges(type="physical")) # TODO: make this automatic if adding to the physical graph? g_phy.allocate_interfaces() for node in g_phy.nodes("specified_int_names"): for interface in node: edge = interface.edges()[0] directed_edge = anm['input_directed'].edge(edge) interface.name = directed_edge.name
def build_phy(anm): """Build physical overlay""" g_in = anm['input'] g_phy = anm['phy'] g_phy.add_nodes_from(g_in, retain=['label', 'update', 'device_type', 'asn', 'specified_int_names', 'device_subtype', 'platform', 'host', 'syntax']) if g_in.data.Creator == "Topology Zoo Toolset": ank_utils.copy_attr_from(g_in, g_phy, "Network") if g_in.data.Creator == "Maestro": g_phy.data.mgmt_interfaces_enabled = g_in.data.mgmt_interfaces_enabled g_phy.data.mgmt_address_start = g_in.data.mgmt_address_start g_phy.data.mgmt_address_end = g_in.data.mgmt_address_end g_phy.data.mgmt_prefixlen = g_in.data.mgmt_prefixlen ank_utils.copy_attr_from(g_in, g_phy, "use_cdp") ank_utils.copy_attr_from(g_in, g_phy, "use_onepk") g_phy.add_edges_from(g_in.edges(type="physical")) # TODO: make this automatic if adding to the physical graph? g_phy.allocate_interfaces() for node in g_phy.nodes("specified_int_names"): for interface in node: edge = interface.edges()[0] directed_edge = anm['input_directed'].edge(edge) interface.name = directed_edge.name
def build_mcast(anm): g_in = anm['input'] g_phy = anm['phy'] g_l3 = anm['layer3'] g_mcast = anm.add_overlay("mcast") g_mcast.add_nodes_from(g_l3, retain=['label', 'update', 'device_type', 'devsubtype', 'asn', 'specified_int_names', 'profile', 'vxlan_vni_configured']) g_mcast.add_edges_from(g_l3.edges()) ank_utils.copy_attr_from(g_in, g_mcast, "multicast") nodes_to_be_removed =[] for node in g_mcast: if node.multicast is None or node.multicast is False: nodes_to_be_removed.append(node) g_mcast.remove_nodes_from(nodes_to_be_removed) for node in g_mcast: for interface in node.interfaces(): if interface.is_bound: interface.pim = 1
def build_isis(anm): """Build isis overlay""" g_in = anm['input'] # add regardless, so allows quick check of node in anm['isis'] in compilers g_l3 = anm['layer3'] g_phy = anm['phy'] g_isis = anm.add_overlay("isis") if not anm['phy'].data.enable_routing: g_isis.log.info("Routing disabled, not configuring ISIS") return if not any(n.igp == "isis" for n in g_phy): g_isis.log.debug("No ISIS nodes") return isis_nodes = [n for n in g_l3 if n['phy'].igp == "isis"] g_isis.add_nodes_from(isis_nodes) g_isis.add_edges_from(g_l3.edges(), warn=False) ank_utils.copy_int_attr_from(g_l3, g_isis, "multipoint") ank_utils.copy_attr_from( g_in, g_isis, "custom_config_isis", dst_attr="custom_config") g_isis.remove_edges_from( [link for link in g_isis.edges() if link.src.asn != link.dst.asn]) build_network_entity_title(anm) for node in g_isis.routers(): node.process_id = node.asn for link in g_isis.edges(): link.metric = 1 # default for edge in g_isis.edges(): for interface in edge.interfaces(): interface.metric = edge.metric interface.multipoint = edge.multipoint
def build_ipv4(anm, infrastructure=True): """Builds IPv4 graph""" import autonetkit.plugins.ipv4 as ipv4 g_ipv4 = anm.add_overlay("ipv4") g_ip = anm['ip'] g_in = anm['input'] g_ipv4.add_nodes_from( g_ip, retain="collision_domain") # retain if collision domain or not # Copy ASN attribute chosen for collision domains (used in alloc algorithm) ank_utils.copy_attr_from(g_ip, g_ipv4, "asn", nbunch = g_ipv4.nodes("collision_domain")) g_ipv4.add_edges_from(g_ip.edges()) # check if ip ranges have been specified on g_in infra_block, loopback_block, vrf_loopback_block = extract_ipv4_blocks(anm) #TODO: need to set allocate_ipv4 by default in the readers if g_in.data.alloc_ipv4_infrastructure is False: manual_ipv4_infrastructure_allocation(anm) else: ipv4.allocate_infra(g_ipv4, infra_block) if g_in.data.alloc_ipv4_loopbacks is False: manual_ipv4_loopback_allocation(anm) else: ipv4.allocate_loopbacks(g_ipv4, loopback_block) #TODO: need to also support secondary_loopbacks for IPv6 ipv4.allocate_vrf_loopbacks(g_ipv4, vrf_loopback_block) #TODO: replace this with direct allocation to interfaces in ip alloc plugin for node in g_ipv4.nodes("is_l3device"): node.loopback_zero.ip_address = node.loopback for interface in node: edges = list(interface.edges()) if len(edges): edge = edges[0] # first (only) edge interface.ip_address = edge.ip_address interface.subnet = edge.dst.subnet # from collision domain
def build_isis(anm): """Build isis overlay""" g_in = anm['input'] # add regardless, so allows quick check of node in anm['isis'] in compilers g_l3 = anm['layer3'] g_isis = anm.add_overlay("isis") if not anm['phy'].data.enable_routing: g_isis.log.info("Routing disabled, not configuring ISIS") return if not any(n.igp == "isis" for n in g_in): g_isis.log.debug("No ISIS nodes") return g_isis.add_nodes_from(g_l3) g_isis.add_edges_from(g_l3.edges()) ank_utils.copy_int_attr_from(g_l3, g_isis, "multipoint") g_ipv4 = anm['ipv4'] ank_utils.copy_attr_from(g_in, g_isis, "custom_config_isis", dst_attr="custom_config") g_isis.remove_edges_from( [link for link in g_isis.edges() if link.src.asn != link.dst.asn]) for node in g_isis.routers(): ip_node = g_ipv4.node(node) node.net = ip_to_net_ent_title_ios(ip_node.loopback) node.process_id = node.asn for link in g_isis.edges(): link.metric = 1 # default for edge in g_isis.edges(): for interface in edge.interfaces(): interface.metric = edge.metric interface.multipoint = edge.multipoint
def build_phy(anm): """Build physical overlay""" g_in = anm['input'] g_phy = anm['phy'] g_phy.data.enable_routing = g_in.data.enable_routing if g_phy.data.enable_routing is None: g_in.data.enable_routing = True # default if not set g_phy.add_nodes_from(g_in, retain=['label', 'update', 'device_type', 'asn', 'specified_int_names', 'x', 'y', 'device_subtype', 'platform', 'host', 'syntax']) if g_in.data.Creator == "Topology Zoo Toolset": ank_utils.copy_attr_from(g_in, g_phy, "Network") ank_utils.set_node_default(g_phy, Network=None) g_phy.add_edges_from(g_in.edges(type="physical")) # TODO: make this automatic if adding to the physical graph? ank_utils.set_node_default(g_phy, use_ipv4=False, use_ipv6=False) ank_utils.copy_attr_from(g_in, g_phy, "custom_config_global", dst_attr="custom_config") for node in g_phy: if node['input'].custom_config_loopback_zero: lo_zero_config = node['input'].custom_config_loopback_zero node.loopback_zero.custom_config = lo_zero_config custom_config_phy_ints = node['input'].custom_config_phy_ints for interface in node: if custom_config_phy_ints: interface.custom_config = custom_config_phy_ints specified_id = interface['input'].get("specified_id") if specified_id: interface.specified_id = specified_id # map across remove_parallel_switch_links(anm)
def build(input_graph): """Main function to build network overlay topologies""" anm = autonetkit.anm.AbstractNetworkModel() input_undirected = nx.Graph(input_graph) g_in = anm.add_overlay("input", graph=input_undirected) anm.add_overlay("input_directed", graph=input_graph, directed=True) # set defaults if not g_in.data.specified_int_names: # if not specified then automatically assign interface names g_in.data.specified_int_names = False import autonetkit.plugins.graph_product as graph_product graph_product.expand(g_in) # apply graph products if relevant expand_fqdn = False # TODO: make this set from config and also in the input file if expand_fqdn and len(ank_utils.unique_attr(g_in, "asn")) > 1: # Multiple ASNs set, use label format device.asn anm.set_node_label(".", ["label", "pop", "asn"]) g_in.update(g_in.nodes("is_router", platform="junosphere"), syntax="junos") g_in.update(g_in.nodes("is_router", platform="dynagen"), syntax="ios") g_in.update(g_in.nodes("is_router", platform="netkit"), syntax="quagga") g_graphics = anm.add_overlay("graphics") # plotting data g_graphics.add_nodes_from(g_in, retain=["x", "y", "device_type", "device_subtype", "pop", "asn"]) build_phy(anm) autonetkit.update_http(anm) g_phy = anm["phy"] build_vrf(anm) # need to do before to add loopbacks before ip allocations build_ip(anm) # ip infrastructure topology autonetkit.update_http(anm) address_family = g_in.data.address_family or "v4" # default is v4 # TODO: can remove the infrastructure now create g_ip seperately if address_family in ("v4", "dual_stack"): build_ipv4(anm, infrastructure=True) g_phy.update(g_phy, use_ipv4=True) else: build_ipv4(anm, infrastructure=False) # TODO: Create a collision domain overlay for ip addressing - l2 overlay? if address_family in ("v6", "dual_stack"): build_ipv6(anm) g_phy.update(g_phy, use_ipv6=True) default_igp = g_in.data.igp or "ospf" non_igp_nodes = [n for n in g_in if not n.igp] # TODO: should this be modifying g_in? g_in.update(non_igp_nodes, igp=default_igp) # store igp onto each node anm.add_overlay("ospf") anm.add_overlay("isis") ank_utils.copy_attr_from(g_in, g_phy, "include_csr") build_ospf(anm) build_isis(anm) build_bgp(anm) autonetkit.update_http(anm) return anm
def build_ipv4(anm, infrastructure=True): """Builds IPv4 graph""" import autonetkit.plugins.ipv4 as ipv4 import netaddr g_ipv4 = anm.add_overlay('ipv4') g_ip = anm['ip'] g_in = anm['input'] # retain if collision domain or not g_ipv4.add_nodes_from(g_ip, retain=['label', 'allocate', 'broadcast_domain']) # Copy ASN attribute chosen for collision domains (used in alloc algorithm) ank_utils.copy_attr_from( g_ip, g_ipv4, 'asn', nbunch=g_ipv4.nodes('broadcast_domain')) # work around until fall-through implemented vswitches = [n for n in g_ip.nodes() if n['layer2'].device_type == "switch" and n['layer2'].device_subtype == "virtual"] ank_utils.copy_attr_from(g_ip, g_ipv4, 'asn', nbunch=vswitches) g_ipv4.add_edges_from(g_ip.edges()) # check if ip ranges have been specified on g_in (infra_block, loopback_block, vrf_loopback_block) = \ extract_ipv4_blocks(anm) # TODO: don't present if using manual allocation if any(i for n in g_ip.nodes() for i in n.loopback_interfaces() if not i.is_loopback_zero): block_message = "IPv4 Secondary Loopbacks: %s" % vrf_loopback_block log.info(block_message) # See if IP addresses specified on each interface # do we need this still? in ANM? - differnt because input graph.... but # can map back to self overlay first then phy??? l3_devices = [d for d in g_in if d.device_type in ('router', 'server')] # TODO: need to account for devices whose interfaces are in only e.g. vpns manual_alloc_devices = set() for device in l3_devices: physical_interfaces = list(device.physical_interfaces()) allocated = list( interface.ipv4_address for interface in physical_interfaces if interface.is_bound and interface['ipv4'].allocate is not False and interface['ipv4'].is_bound) if all(interface.ipv4_address for interface in physical_interfaces if interface.is_bound and interface['ip'].allocate is not False and interface['ip'].is_bound): # add as a manual allocated device manual_alloc_devices.add(device) if manual_alloc_devices == set(l3_devices): manual_alloc_ipv4_infrastructure = True else: log.info("Allocating from IPv4 infrastructure block: %s" % infra_block) manual_alloc_ipv4_infrastructure = False # warn if any set allocated = [] unallocated = [] for node in l3_devices: # TODO: make these inverse sets allocated += sorted([i for i in node.physical_interfaces() if i.is_bound and i.ipv4_address]) unallocated += sorted([i for i in node.physical_interfaces() if i.is_bound and not i.ipv4_address and i['ipv4'].is_bound]) # TODO: what if IP is set but not a prefix? if len(allocated): # TODO: if set is > 50% of nodes then list those that are NOT set log.warning( "Using automatic IPv4 interface allocation. IPv4 interface addresses specified on interfaces %s will be ignored." % allocated) # TODO: need to set allocate_ipv4 by default in the readers if manual_alloc_ipv4_infrastructure: manual_ipv4_infrastructure_allocation(anm) else: ipv4.allocate_infra(g_ipv4, infra_block) if g_in.data.alloc_ipv4_loopbacks is False: manual_ipv4_loopback_allocation(anm) else: log.info("Allocating from IPv4 loopback block: %s" % loopback_block) # Check if some nodes are allocated allocated = sorted([n for n in g_ip if n['input'].loopback_v4]) unallocated = sorted([n for n in g_ip if not n['input'].loopback_v4]) if len(allocated): log.warning( "Using automatic IPv4 loopback allocation. IPv4 loopback addresses specified on nodes %s will be ignored." % allocated) # TODO: if set is > 50% of nodes then list those that are NOT set ipv4.allocate_loopbacks(g_ipv4, loopback_block) # TODO: need to also support secondary_loopbacks for IPv6 # TODO: only call if secondaries are set ipv4.allocate_secondary_loopbacks(g_ipv4, vrf_loopback_block) # TODO: replace this with direct allocation to interfaces in ip alloc plugin # TODO: add option for nonzero interfaces on node - ie # node.secondary_loopbacks for node in g_ipv4: node.static_routes = [] for node in g_ipv4.routers(): node.loopback_zero.ip_address = node.loopback node.loopback_zero.subnet = netaddr.IPNetwork("%s/32" % node.loopback) for interface in node.loopback_interfaces(): if not interface.is_loopback_zero: # TODO: fix this inconsistency elsewhere interface.ip_address = interface.loopback
def build_bgp(anm): """Build iBGP end eBGP overlays""" # eBGP g_in = anm['input'] g_phy = anm['phy'] if not anm['phy'].data.enable_routing: log.info("Routing disabled, not configuring BGP") return build_ebgp(anm) build_ebgp_v4(anm) build_ebgp_v6(anm) """TODO: remove from here once compiler updated""" g_bgp = anm.add_overlay("bgp", directed=True) g_bgp.add_nodes_from(g_in.nodes("is_router")) ebgp_edges = [edge for edge in g_in.edges() if not edge.attr_equal("asn")] g_bgp.add_edges_from(ebgp_edges, bidirectional=True, type='ebgp') #TODO: why don't we include edge_id here ebgp_switches = [n for n in g_in.nodes("is_switch") if not ank_utils.neigh_equal(g_phy, n, "asn")] g_bgp.add_nodes_from(ebgp_switches, retain=['asn']) log.debug("eBGP switches are %s" % ebgp_switches) g_bgp.add_edges_from((e for e in g_in.edges() if e.src in ebgp_switches or e.dst in ebgp_switches), bidirectional=True, type='ebgp') ank_utils.aggregate_nodes(g_bgp, ebgp_switches, retain="edge_id") ebgp_switches = list(g_bgp.nodes("is_switch")) # need to recalculate as may have aggregated log.debug("aggregated eBGP switches are %s" % ebgp_switches) exploded_edges = ank_utils.explode_nodes(g_bgp, ebgp_switches, retain="edge_id") same_asn_edges = [] for edge in exploded_edges: if edge.src.asn == edge.dst.asn: same_asn_edges.append(edge) else: edge.multipoint = True """TODO: remove up to here once compiler updated""" g_bgp.remove_edges_from(same_asn_edges) # now iBGP ank_utils.copy_attr_from(g_in, g_bgp, "ibgp_level") ank_utils.copy_attr_from(g_in, g_bgp, "ibgp_l2_cluster") ank_utils.copy_attr_from(g_in, g_bgp, "ibgp_l3_cluster") for node in g_bgp: # set defaults if node.ibgp_level is None: node.ibgp_level = 1 if node.ibgp_level == "None": # if unicode string from yEd node.ibgp_level = 1 #TODO CHECK FOR IBGP NONE node.ibgp_level = int(node.ibgp_level) # ensure is numeric if not node.ibgp_l2_cluster or node.ibgp_l2_cluster == "None": # ibgp_l2_cluster defaults to region node.ibgp_l2_cluster = node.region or "default_l2_cluster" if not node.ibgp_l3_cluster or node.ibgp_l3_cluster == "None": # ibgp_l3_cluster defaults to ASN node.ibgp_l3_cluster = node.asn for asn, devices in ank_utils.groupby("asn", g_bgp): # group by nodes in phy graph routers = list(g_bgp.node(n) for n in devices if n.is_router) # list of nodes from bgp graph ibgp_levels = {int(r.ibgp_level) for r in routers} max_level = max(ibgp_levels) # all possible edge src/dst pairs ibgp_routers = [r for r in routers if r.ibgp_level > 0] all_pairs = [(s, t) for s in ibgp_routers for t in ibgp_routers if s != t] if max_level == 3: up_links, down_links, over_links = three_tier_ibgp_edges(ibgp_routers) elif max_level == 2: #TODO: check when this is reached - as RR is not HRR.... due to naming/levels mapping up_links, down_links, over_links = build_two_tier_ibgp(ibgp_routers) elif max_level == 1: up_links = [] down_links = [] over_links = [(s, t) for (s, t) in all_pairs if s.ibgp_l3_cluster == t.ibgp_l3_cluster and s.ibgp_l2_cluster == t.ibgp_l2_cluster ] else: # no iBGP up_links = [] down_links = [] over_links = [] if max_level > 0: g_bgp.add_edges_from(up_links, type='ibgp', direction='up') g_bgp.add_edges_from(down_links, type='ibgp', direction='down') g_bgp.add_edges_from(over_links, type='ibgp', direction='over') else: log.debug("No iBGP routers in %s" % asn) # and set label back ibgp_label_to_level = { 0: "None", # Explicitly set role to "None" -> Not in iBGP 3: "RR", 1: "RRC", 2: "HRR", } for node in g_bgp: node.ibgp_role = ibgp_label_to_level[node.ibgp_level] ebgp_nodes = [d for d in g_bgp if any( edge.type == 'ebgp' for edge in d.edges())] g_bgp.update(ebgp_nodes, ebgp=True) for ebgp_edge in g_bgp.edges(type = "ebgp"): for interface in ebgp_edge.interfaces(): interface.ebgp = True for edge in g_bgp.edges(type='ibgp'): # TODO: need interface querying/selection. rather than hard-coded ids edge.bind_interface(edge.src, 0) #TODO: need to initialise interface zero to be a loopback rather than physical type for node in g_bgp: for interface in node.interfaces(): interface.multipoint = any(e.multipoint for e in interface.edges()) build_ibgp_v4(anm) build_ibgp_v6(anm)
g_phy.update(g_phy, use_ipv4=False) # TODO: Create collision domain overlay for ip addressing - l2 overlay? if address_family == "None": log.info("IP addressing disabled, not allocating IPv6") anm.add_overlay("ipv6") # create empty so rest of code follows g_phy.update(g_phy, use_ipv6=False) elif address_family in ("v6", "dual_stack"): build_ipv6(anm) g_phy.update(g_phy, use_ipv6=True) else: anm.add_overlay("ipv6") # placeholder for compiler logic default_igp = g_in.data.igp or "ospf" ank_utils.set_node_default(g_in, igp=default_igp) ank_utils.copy_attr_from(g_in, g_phy, "igp") ank_utils.copy_attr_from(g_in, g_phy, "include_csr") try: from autonetkit_cisco import build_network as cisco_build_network except ImportError, error: log.debug("Unable to load autonetkit_cisco %s" % error) else: cisco_build_network.pre_design(anm) # log.info("Building IGP") from autonetkit.design.igp import build_igp build_igp(anm) # log.info("Building BGP")
def build_ospf(anm): """ Build OSPF graph. Allowable area combinations: 0 -> 0 0 -> x (x!= 0) x -> 0 (x!= 0) x -> x (x != 0) Not-allowed: x -> x (x != y != 0) #TODO: build check that verifies these rules """ import netaddr g_in = anm['input'] # add regardless, so allows quick check of node in anm['ospf'] in compilers g_ospf = anm.add_overlay("ospf") if not any(n.igp == "ospf" for n in g_in): log.debug("No OSPF nodes") return g_ospf.add_nodes_from(g_in.nodes("is_router", igp="ospf"), retain=['asn']) g_ospf.add_nodes_from(g_in.nodes("is_switch"), retain=['asn']) g_ospf.add_edges_from(g_in.edges(), retain=['edge_id']) ank_utils.copy_attr_from(g_in, g_ospf, "ospf_area", dst_attr="area") ank_utils.copy_edge_attr_from(g_in, g_ospf, "ospf_cost", dst_attr="cost", type=float) ank_utils.aggregate_nodes(g_ospf, g_ospf.nodes("is_switch"), retain="edge_id") exploded_edges = ank_utils.explode_nodes(g_ospf, g_ospf.nodes("is_switch"), retain="edge_id") for edge in exploded_edges: edge.multipoint = True g_ospf.remove_edges_from([ link for link in g_ospf.edges() if link.src.asn != link.dst.asn ]) # remove inter-AS links area_zero_ip = netaddr.IPAddress("0.0.0.0") area_zero_int = 0 area_zero_ids = {area_zero_ip, area_zero_int} default_area = area_zero_int if any(router.area == "0.0.0.0" for router in g_ospf): # string comparison as hasn't yet been cast to IPAddress default_area = area_zero_ip for router in g_ospf: if not router.area or router.area == "None": router.area = default_area # check if 0.0.0.0 used anywhere, if so then use 0.0.0.0 as format else: try: router.area = int(router.area) except ValueError: try: router.area = netaddr.IPAddress(router.area) except netaddr.core.AddrFormatError: log.warning("Invalid OSPF area %s for %s. Using default" " of %s" % (router.area, router, default_area)) router.area = default_area for router in g_ospf: # and set area on interface for edge in router.edges(): if edge.area: continue # allocated (from other "direction", as undirected) if router.area == edge.dst.area: edge.area = router.area # intra-area continue if router.area in area_zero_ids or edge.dst.area in area_zero_ids: # backbone to other area if router.area in area_zero_ids: # router in backbone, use other area edge.area = edge.dst.area else: # router not in backbone, use its area edge.area = router.area for router in g_ospf: areas = {edge.area for edge in router.edges()} router.areas = list(areas) # edges router participates in if len(areas) in area_zero_ids: router.type = "backbone" # no ospf edges (eg single node in AS) elif len(areas) == 1: # single area: either backbone (all 0) or internal (all nonzero) if len(areas & area_zero_ids): # intersection has at least one element -> router has area zero router.type = "backbone" else: router.type = "internal" else: # multiple areas if len(areas & area_zero_ids): # intersection has at least one element -> router has area zero router.type = "backbone ABR" else: log.warning( "%s spans multiple areas but is not a member of area 0" % router) router.type = "INVALID" if (any(area_zero_int in router.areas for router in g_ospf) and any(area_zero_ip in router.areas for router in g_ospf)): log.warning("Using both area 0 and area 0.0.0.0") for link in g_ospf.edges(): if not link.cost: link.cost = 1 # map areas and costs onto interfaces #TODO: later map them directly rather than with edges - this is part of the transition for edge in g_ospf.edges(): for interface in edge.interfaces(): interface.cost = edge.cost interface.area = edge.area interface.multipoint = edge.multipoint for router in g_ospf: router.loopback_zero.area = router.area router.loopback_zero.cost = 0
def build_ospf(anm): """ Build OSPF graph. Allowable area combinations: 0 -> 0 0 -> x (x!= 0) x -> 0 (x!= 0) x -> x (x != 0) Not-allowed: x -> x (x != y != 0) """ import netaddr g_in = anm['input'] g_l3 = anm['layer3'] g_phy = anm['phy'] # add regardless, so allows quick check of node in anm['ospf'] in compilers g_ospf = anm.add_overlay("ospf") if not anm['phy'].data.enable_routing: g_ospf.log.info("Routing disabled, not configuring OSPF") return if not any(n.igp == "ospf" for n in g_phy): g_ospf.log.debug("No OSPF nodes") return ospf_nodes = [n for n in g_l3 if n['phy'].igp == "ospf"] g_ospf.add_nodes_from(ospf_nodes) g_ospf.add_edges_from(g_l3.edges(), warn=False) ank_utils.copy_int_attr_from(g_l3, g_ospf, "multipoint") # TODO: work out why this doesnt work #ank_utils.copy_int_attr_from(g_in, g_ospf, "ospf_cost", dst_attr="cost", type=int, default = 1) for node in g_ospf: for interface in node.physical_interfaces(): interface.cost = 1 ank_utils.copy_attr_from(g_in, g_ospf, "ospf_area", dst_attr="area") #ank_utils.copy_edge_attr_from(g_in, g_ospf, "ospf_cost", dst_attr="cost", type=int, default = 1) ank_utils.copy_attr_from( g_in, g_ospf, "custom_config_ospf", dst_attr="custom_config") g_ospf.remove_edges_from([link for link in g_ospf.edges( ) if link.src.asn != link.dst.asn]) # remove inter-AS links area_zero_ip = netaddr.IPAddress("0.0.0.0") area_zero_int = 0 area_zero_ids = {area_zero_ip, area_zero_int} default_area = area_zero_int if any(router.area == "0.0.0.0" for router in g_ospf): # string comparison as hasn't yet been cast to IPAddress default_area = area_zero_ip for router in g_ospf: if not router.area or router.area == "None": router.area = default_area # check if 0.0.0.0 used anywhere, if so then use 0.0.0.0 as format else: try: router.area = int(router.area) except ValueError: try: router.area = netaddr.IPAddress(router.area) except netaddr.core.AddrFormatError: router.log.warning("Invalid OSPF area %s. Using default" " of %s" % (router.area, default_area)) router.area = default_area # TODO: use interfaces throughout, rather than edges for router in g_ospf: # and set area on interface for edge in router.edges(): if edge.area: continue # allocated (from other "direction", as undirected) if router.area == edge.dst.area: edge.area = router.area # intra-area continue if router.area in area_zero_ids or edge.dst.area in area_zero_ids: # backbone to other area if router.area in area_zero_ids: # router in backbone, use other area edge.area = edge.dst.area else: # router not in backbone, use its area edge.area = router.area for router in g_ospf: areas = {edge.area for edge in router.edges()} router.areas = list(areas) # edges router participates in if len(areas) in area_zero_ids: router.type = "backbone" # no ospf edges (eg single node in AS) elif len(areas) == 1: # single area: either backbone (all 0) or internal (all nonzero) if len(areas & area_zero_ids): # intersection has at least one element -> router has area zero router.type = "backbone" else: router.type = "internal" else: # multiple areas if len(areas & area_zero_ids): # intersection has at least one element -> router has area zero router.type = "backbone ABR" elif router.area in area_zero_ids: router.log.debug( "Router belongs to area %s but has no area zero interfaces", router.area) router.type = "backbone ABR" else: router.log.warning( "spans multiple areas but is not a member of area 0") router.type = "INVALID" if (any(area_zero_int in router.areas for router in g_ospf) and any(area_zero_ip in router.areas for router in g_ospf)): router.log.warning("Using both area 0 and area 0.0.0.0") for link in g_ospf.edges(): if not link.cost: link.cost = 1 # map areas and costs onto interfaces # TODO: later map them directly rather than with edges - part of # the transition for edge in g_ospf.edges(): for interface in edge.interfaces(): interface.cost = edge.cost interface.area = edge.area interface.multipoint = edge.multipoint for router in g_ospf: router.loopback_zero.area = router.area router.loopback_zero.cost = 0 router.process_id = router.asn
def build_ipv4(anm, infrastructure=True): """Builds IPv4 graph""" import autonetkit.plugins.ipv4 as ipv4 import netaddr g_ipv4 = anm.add_overlay('ipv4') g_ip = anm['ip'] g_in = anm['input'] g_ipv4.add_nodes_from(g_ip, retain=['label', 'broadcast_domain' ]) # retain if collision domain or not # Copy ASN attribute chosen for collision domains (used in alloc algorithm) ank_utils.copy_attr_from(g_ip, g_ipv4, 'asn', nbunch=g_ipv4.nodes('broadcast_domain')) g_ipv4.add_edges_from(g_ip.edges()) # check if ip ranges have been specified on g_in (infra_block, loopback_block, vrf_loopback_block) = \ extract_ipv4_blocks(anm) block_message = "IPv4 allocations: Infrastructure: %s, Loopback: %s" % ( infra_block, loopback_block) if any(i for n in g_ip.nodes() for i in n.loopback_interfaces if not i.is_loopback_zero): block_message += " Secondary Loopbacks: %s" % vrf_loopback_block log.info(block_message) # See if IP addresses specified on each interface # do we need this still? in ANM? l3_devices = [d for d in g_in if d.device_type in ('router', 'server')] manual_alloc_devices = set() for device in l3_devices: physical_interfaces = list(device.physical_interfaces) allocated = list(interface.ipv4_address for interface in physical_interfaces if interface.is_bound) if all(interface.ipv4_address for interface in physical_interfaces if interface.is_bound): manual_alloc_devices.add( device) # add as a manual allocated device if manual_alloc_devices == set(l3_devices): manual_alloc_ipv4_infrastructure = True else: manual_alloc_ipv4_infrastructure = False # warn if any set allocated = [] unallocated = [] for node in l3_devices: allocated += sorted([ i for i in node.physical_interfaces if i.is_bound and i.ipv4_address ]) unallocated += sorted([ i for i in node.physical_interfaces if i.is_bound and not i.ipv4_address ]) #TODO: what if IP is set but not a prefix? if len(allocated): #TODO: if set is > 50% of nodes then list those that are NOT set log.warning( "Using automatic IPv4 interface allocation. IPv4 interface addresses specified on interfaces %s will be ignored." % allocated) # TODO: need to set allocate_ipv4 by default in the readers if manual_alloc_ipv4_infrastructure: manual_ipv4_infrastructure_allocation(anm) else: ipv4.allocate_infra(g_ipv4, infra_block) if g_in.data.alloc_ipv4_loopbacks is False: manual_ipv4_loopback_allocation(anm) else: # Check if some nodes are allocated allocated = sorted([n for n in g_ip if n['input'].loopback_v4]) unallocated = sorted([n for n in g_ip if not n['input'].loopback_v4]) if len(allocated): log.warning( "Using automatic IPv4 loopback allocation. IPv4 loopback addresses specified on nodes %s will be ignored." % allocated) #TODO: if set is > 50% of nodes then list those that are NOT set ipv4.allocate_loopbacks(g_ipv4, loopback_block) # TODO: need to also support secondary_loopbacks for IPv6 # TODO: only call if secondaries are set ipv4.allocate_vrf_loopbacks(g_ipv4, vrf_loopback_block) # TODO: replace this with direct allocation to interfaces in ip alloc plugin #TODO: add option for nonzero interfaces on node - ie node.secondary_loopbacks for node in g_ipv4.routers(): node.loopback_zero.ip_address = node.loopback node.loopback_zero.subnet = netaddr.IPNetwork("%s/32" % node.loopback) for interface in node.loopback_interfaces: if not interface.is_loopback_zero: interface.ip_address = interface.loopback #TODO: fix this inconsistency elsewhere
def build_ipv4(anm, infrastructure=True): """Builds IPv4 graph""" import autonetkit.plugins.ipv4 as ipv4 import netaddr g_ipv4 = anm.add_overlay("ipv4") g_ip = anm["ip"] g_in = anm["input"] g_ipv4.add_nodes_from(g_ip, retain=["label", "broadcast_domain"]) # retain if collision domain or not # Copy ASN attribute chosen for collision domains (used in alloc algorithm) ank_utils.copy_attr_from(g_ip, g_ipv4, "asn", nbunch=g_ipv4.nodes("broadcast_domain")) g_ipv4.add_edges_from(g_ip.edges()) # check if ip ranges have been specified on g_in (infra_block, loopback_block, vrf_loopback_block) = extract_ipv4_blocks(anm) # TODO: don't present if using manual allocation block_message = "IPv4 allocations: Infrastructure: %s, Loopback: %s" % (infra_block, loopback_block) if any(i for n in g_ip.nodes() for i in n.loopback_interfaces if not i.is_loopback_zero): block_message += " Secondary Loopbacks: %s" % vrf_loopback_block log.info(block_message) # See if IP addresses specified on each interface # do we need this still? in ANM? - differnt because input graph.... but can map back to self overlay first then phy??? l3_devices = [d for d in g_in if d.device_type in ("router", "server")] manual_alloc_devices = set() for device in l3_devices: physical_interfaces = list(device.physical_interfaces) allocated = list(interface.ipv4_address for interface in physical_interfaces if interface.is_bound) if all(interface.ipv4_address for interface in physical_interfaces if interface.is_bound): manual_alloc_devices.add(device) # add as a manual allocated device if manual_alloc_devices == set(l3_devices): manual_alloc_ipv4_infrastructure = True else: manual_alloc_ipv4_infrastructure = False # warn if any set allocated = [] unallocated = [] for node in l3_devices: allocated += sorted([i for i in node.physical_interfaces if i.is_bound and i.ipv4_address]) unallocated += sorted([i for i in node.physical_interfaces if i.is_bound and not i.ipv4_address]) # TODO: what if IP is set but not a prefix? if len(allocated): # TODO: if set is > 50% of nodes then list those that are NOT set log.warning( "Using automatic IPv4 interface allocation. IPv4 interface addresses specified on interfaces %s will be ignored." % allocated ) # TODO: need to set allocate_ipv4 by default in the readers if manual_alloc_ipv4_infrastructure: manual_ipv4_infrastructure_allocation(anm) else: ipv4.allocate_infra(g_ipv4, infra_block) if g_in.data.alloc_ipv4_loopbacks is False: manual_ipv4_loopback_allocation(anm) else: # Check if some nodes are allocated allocated = sorted([n for n in g_ip if n["input"].loopback_v4]) unallocated = sorted([n for n in g_ip if not n["input"].loopback_v4]) if len(allocated): log.warning( "Using automatic IPv4 loopback allocation. IPv4 loopback addresses specified on nodes %s will be ignored." % allocated ) # TODO: if set is > 50% of nodes then list those that are NOT set ipv4.allocate_loopbacks(g_ipv4, loopback_block) # TODO: need to also support secondary_loopbacks for IPv6 # TODO: only call if secondaries are set ipv4.allocate_vrf_loopbacks(g_ipv4, vrf_loopback_block) # TODO: replace this with direct allocation to interfaces in ip alloc plugin # TODO: add option for nonzero interfaces on node - ie node.secondary_loopbacks for node in g_ipv4.routers(): node.loopback_zero.ip_address = node.loopback node.loopback_zero.subnet = netaddr.IPNetwork("%s/32" % node.loopback) for interface in node.loopback_interfaces: if not interface.is_loopback_zero: interface.ip_address = interface.loopback # TODO: fix this inconsistency elsewhere
def build_ipv4(anm, infrastructure=True): """Builds IPv4 graph""" import autonetkit.plugins.ipv4 as ipv4 import netaddr g_ipv4 = anm.add_overlay('ipv4') g_ip = anm['ip'] g_in = anm['input'] # retain if collision domain or not g_ipv4.add_nodes_from(g_ip, retain=['label', 'allocate', 'broadcast_domain']) ank_utils.copy_attr_from(g_in, g_ipv4, 'name') # Copy ASN attribute chosen for collision domains (used in alloc algorithm) ank_utils.copy_attr_from( g_ip, g_ipv4, 'asn', nbunch=g_ipv4.nodes('broadcast_domain')) # work around until fall-through implemented vswitches = [n for n in g_ip.nodes() if n['layer2'].device_type == "switch" and n['layer2'].device_subtype == "virtual"] ank_utils.copy_attr_from(g_ip, g_ipv4, 'asn', nbunch=vswitches) g_ipv4.add_edges_from(g_ip.edges()) # check if ip ranges have been specified on g_in (infra_block, loopback_block, vrf_loopback_block) = \ extract_ipv4_blocks(anm) # TODO: don't present if using manual allocation if any(i for n in g_ip.nodes() for i in n.loopback_interfaces() if not i.is_loopback_zero): block_message = "IPv4 Secondary Loopbacks: %s" % vrf_loopback_block log.info(block_message) # See if IP addresses specified on each interface # do we need this still? in ANM? - differnt because input graph.... but # can map back to self overlay first then phy??? l3_devices = [d for d in g_in if d.device_type in ('router', 'server')] # TODO: need to account for devices whose interfaces are in only e.g. vpns manual_alloc_devices = set() for device in l3_devices: physical_interfaces = list(device.edge_interfaces()) allocated = list( interface.ipv4_address for interface in physical_interfaces if interface.is_bound and interface['ipv4'].allocate is not False and interface['ipv4'].is_bound) if all(interface.ipv4_address for interface in physical_interfaces if interface.is_bound and interface['ip'].allocate is not False and interface['ip'].is_bound): # add as a manual allocated device manual_alloc_devices.add(device) if manual_alloc_devices == set(l3_devices): manual_alloc_ipv4_infrastructure = True else: log.info("Allocating from IPv4 infrastructure block: %s" % infra_block) manual_alloc_ipv4_infrastructure = False # warn if any set allocated = [] unallocated = [] for node in l3_devices: # TODO: make these inverse sets allocated += sorted([i for i in node.edge_interfaces() if i.is_bound and i.ipv4_address]) unallocated += sorted([i for i in node.edge_interfaces() if i.is_bound and not i.ipv4_address and i['ipv4'].is_bound]) # TODO: what if IP is set but not a prefix? if len(allocated): # TODO: if set is > 50% of nodes then list those that are NOT set log.warning( "Using automatic IPv4 interface allocation. IPv4 interface addresses specified on interfaces %s will be ignored." % allocated) # TODO: need to set allocate_ipv4 by default in the readers if manual_alloc_ipv4_infrastructure: manual_ipv4_infrastructure_allocation(anm) else: ipv4.allocate_infra(g_ipv4, infra_block) if g_in.data.alloc_ipv4_loopbacks is False: manual_ipv4_loopback_allocation(anm) else: log.info("Allocating from IPv4 loopback block: %s" % loopback_block) # Check if some nodes are allocated allocated = sorted([n for n in g_ip if n['input'].loopback_v4]) unallocated = sorted([n for n in g_ip if not n['input'].loopback_v4]) if len(allocated): log.warning( "Using automatic IPv4 loopback allocation. IPv4 loopback addresses specified on nodes %s will be ignored." % allocated) # TODO: if set is > 50% of nodes then list those that are NOT set ipv4.allocate_loopbacks(g_ipv4, loopback_block) # TODO: need to also support secondary_loopbacks for IPv6 # TODO: only call if secondaries are set ipv4.allocate_secondary_loopbacks(g_ipv4, vrf_loopback_block) # TODO: replace this with direct allocation to interfaces in ip alloc plugin # TODO: add option for nonzero interfaces on node - ie # node.secondary_loopbacks for node in g_ipv4: node.static_routes = [] for node in g_ipv4.routers(): node.loopback_zero.ip_address = node.loopback node.loopback_zero.subnet = netaddr.IPNetwork("%s/32" % node.loopback) for interface in node.loopback_interfaces(): if not interface.is_loopback_zero: # TODO: fix this inconsistency elsewhere interface.ip_address = interface.loopback
# TODO: Create collision domain overlay for ip addressing - l2 overlay? if address_family == "None": log.info("IP addressing disabled, not allocating IPv6") anm.add_overlay("ipv6") # create empty so rest of code follows g_phy.update(g_phy, use_ipv6=False) elif address_family in ("v6", "dual_stack"): build_ipv6(anm) g_phy.update(g_phy, use_ipv6=True) else: anm.add_overlay("ipv6") # placeholder for compiler logic assign_loopback_ip_pool(anm) # default_igp = g_in.data.igp or "ospf" default_igp = g_in.data.igp ank_utils.set_node_default(g_in, igp=default_igp) ank_utils.copy_attr_from(g_in, g_phy, "igp") ank_utils.copy_attr_from(g_in, g_phy, "include_csr") try: from autonetkit_cisco import build_network as cisco_build_network except ImportError, error: log.debug("Unable to load autonetkit_cisco %s" % error) else: cisco_build_network.pre_design(anm) # log.info("Building IGP") from autonetkit.design.igp import build_igp build_igp(anm)
def build_ospf(anm): """ Build OSPF graph. Allowable area combinations: 0 -> 0 0 -> x (x!= 0) x -> 0 (x!= 0) x -> x (x != 0) Not-allowed: x -> x (x != y != 0) #TODO: build check that verifies these rules """ import netaddr g_in = anm['input'] # add regardless, so allows quick check of node in anm['ospf'] in compilers g_ospf = anm.add_overlay("ospf") if not any(n.igp == "ospf" for n in g_in): log.debug("No OSPF nodes") return g_ospf.add_nodes_from(g_in.nodes("is_router", igp = "ospf"), retain=['asn']) g_ospf.add_nodes_from(g_in.nodes("is_server", igp = "ospf"), retain=['asn']) g_ospf.add_nodes_from(g_in.nodes("is_switch"), retain=['asn']) g_ospf.add_edges_from(g_in.edges(), retain=['edge_id']) ank_utils.copy_attr_from(g_in, g_ospf, "ospf_area", dst_attr="area") ank_utils.copy_edge_attr_from(g_in, g_ospf, "ospf_cost", dst_attr="cost", type=float) ank_utils.aggregate_nodes(g_ospf, g_ospf.nodes("is_switch"), retain="edge_id") exploded_edges = ank_utils.explode_nodes(g_ospf, g_ospf.nodes("is_switch"), retain="edge_id") for edge in exploded_edges: edge.multipoint = True g_ospf.remove_edges_from([link for link in g_ospf.edges( ) if link.src.asn != link.dst.asn]) # remove inter-AS links area_zero_ip = netaddr.IPAddress("0.0.0.0") area_zero_int = 0 area_zero_ids = {area_zero_ip, area_zero_int} default_area = area_zero_int if any(router.area == "0.0.0.0" for router in g_ospf): # string comparison as hasn't yet been cast to IPAddress default_area = area_zero_ip #TODO: use interfaces throughout, rather than edges for router in g_ospf: if not router.area or router.area == "None": router.area = default_area # check if 0.0.0.0 used anywhere, if so then use 0.0.0.0 as format else: try: router.area = int(router.area) except ValueError: try: router.area = netaddr.IPAddress(router.area) except netaddr.core.AddrFormatError: log.warning("Invalid OSPF area %s for %s. Using default" " of %s" % (router.area, router, default_area)) router.area = default_area for router in g_ospf: # and set area on interface for edge in router.edges(): if edge.area: continue # allocated (from other "direction", as undirected) if router.area == edge.dst.area: edge.area = router.area # intra-area continue if router.area in area_zero_ids or edge.dst.area in area_zero_ids: # backbone to other area if router.area in area_zero_ids: # router in backbone, use other area edge.area = edge.dst.area else: # router not in backbone, use its area edge.area = router.area for router in g_ospf: areas = {edge.area for edge in router.edges()} router.areas = list(areas) # edges router participates in if len(areas) in area_zero_ids: router.type = "backbone" # no ospf edges (eg single node in AS) elif len(areas) == 1: # single area: either backbone (all 0) or internal (all nonzero) if len(areas & area_zero_ids): # intersection has at least one element -> router has area zero router.type = "backbone" else: router.type = "internal" else: # multiple areas if len(areas & area_zero_ids): # intersection has at least one element -> router has area zero router.type = "backbone ABR" else: log.warning( "%s spans multiple areas but is not a member of area 0" % router) router.type = "INVALID" if (any(area_zero_int in router.areas for router in g_ospf) and any(area_zero_ip in router.areas for router in g_ospf)): log.warning("Using both area 0 and area 0.0.0.0") for link in g_ospf.edges(): if not link.cost: link.cost = 1 # map areas and costs onto interfaces #TODO: later map them directly rather than with edges - this is part of the transition for edge in g_ospf.edges(): for interface in edge.interfaces(): interface.cost = edge.cost interface.area = edge.area interface.multipoint = edge.multipoint for router in g_ospf: router.loopback_zero.area = router.area router.loopback_zero.cost = 0
def build_phy(anm): """Build physical overlay""" g_in = anm['input'] g_phy = anm['phy'] g_phy.data.enable_routing = g_in.data.enable_routing if g_phy.data.enable_routing is None: g_in.data.enable_routing = True # default if not set g_phy.add_nodes_from(g_in, retain=[ 'label', 'update', 'device_type', 'asn', 'specified_int_names', 'device_subtype', 'platform', 'host', 'syntax' ]) if g_in.data.Creator == "Topology Zoo Toolset": ank_utils.copy_attr_from(g_in, g_phy, "Network") ank_utils.set_node_default(g_phy, Network=None) g_phy.add_edges_from(g_in.edges(type="physical")) # TODO: make this automatic if adding to the physical graph? if g_in.data.Creator == "VIRL": g_phy.data.mgmt_interfaces_enabled = g_in.data.mgmt_interfaces_enabled #TODO: remove this code now allocated externally g_phy.data.mgmt_address_start = g_in.data.mgmt_address_start g_phy.data.mgmt_address_end = g_in.data.mgmt_address_end g_phy.data.mgmt_prefixlen = g_in.data.mgmt_prefixlen g_phy.data.mgmt_prefixlen = g_in.data.mgmt_prefixlen ank_utils.copy_attr_from(g_in, g_phy, "use_cdp") ank_utils.copy_attr_from(g_in, g_phy, "use_onepk") ank_utils.copy_attr_from(g_in, g_phy, "label_full") ank_utils.copy_attr_from(g_in, g_phy, "indices") ank_utils.copy_attr_from(g_in, g_phy, "dont_configure_static_routing") ank_utils.copy_attr_from(g_in, g_phy, "server_username") ank_utils.copy_attr_from(g_in, g_phy, "server_ssh_key") ank_utils.set_node_default(g_phy, use_ipv4=False, use_ipv6=False) g_phy.allocate_interfaces() for node in g_phy: for interface in node: specified_id = interface['input'].get("specified_id") if specified_id: interface.specified_id = specified_id # map across remove_parallel_switch_links(anm)
def build_bgp(anm): """Build iBGP end eBGP overlays""" # eBGP g_in = anm['input'] g_phy = anm['phy'] if not anm['phy'].data.enable_routing: log.info("Routing disabled, not configuring BGP") return build_ebgp(anm) build_ebgp_v4(anm) build_ebgp_v6(anm) """TODO: remove from here once compiler updated""" g_bgp = anm.add_overlay("bgp", directed=True) g_bgp.add_nodes_from(g_in.routers()) ebgp_edges = [edge for edge in g_in.edges() if edge.src.asn != edge.dst.asn] g_bgp.add_edges_from(ebgp_edges, bidirectional=True, type='ebgp') ebgp_switches = [n for n in g_in.switches() if not ank_utils.neigh_equal(g_phy, n, "asn")] g_bgp.add_nodes_from(ebgp_switches, retain=['asn']) log.debug("eBGP switches are %s" % ebgp_switches) g_bgp.add_edges_from((e for e in g_in.edges() if e.src in ebgp_switches or e.dst in ebgp_switches), bidirectional=True, type='ebgp') ank_utils.aggregate_nodes(g_bgp, ebgp_switches) ebgp_switches = list(g_bgp.switches()) # need to recalculate as may have aggregated log.debug("aggregated eBGP switches are %s" % ebgp_switches) exploded_edges = ank_utils.explode_nodes(g_bgp, ebgp_switches) same_asn_edges = [] for edge in exploded_edges: if edge.src.asn == edge.dst.asn: same_asn_edges.append(edge) else: edge.multipoint = True """TODO: remove up to here once compiler updated""" ank_utils.copy_attr_from(g_in, g_bgp, "custom_config_bgp", dst_attr="custom_config") g_bgp.remove_edges_from(same_asn_edges) build_ibgp(anm) ebgp_nodes = [d for d in g_bgp if any( edge.type == 'ebgp' for edge in d.edges())] g_bgp.update(ebgp_nodes, ebgp=True) for ebgp_edge in g_bgp.edges(type = "ebgp"): for interface in ebgp_edge.interfaces(): interface.ebgp = True for edge in g_bgp.edges(type='ibgp'): # TODO: need interface querying/selection. rather than hard-coded ids edge.bind_interface(edge.src, 0) #TODO: need to initialise interface zero to be a loopback rather than physical type for node in g_bgp: for interface in node.interfaces(): interface.multipoint = any(e.multipoint for e in interface.edges()) build_ibgp_v4(anm) build_ibgp_v6(anm)
def build_ibgp(anm): g_in = anm['input'] g_bgp = anm['bgp'] # TODO: build direct to ibgp graph - can construct combined bgp for vis #TODO: normalise input property ank_utils.copy_attr_from(g_in, g_bgp, "ibgp_role") ank_utils.copy_attr_from(g_in, g_bgp, "ibgp_l2_cluster", "hrr_cluster", default=None) ank_utils.copy_attr_from(g_in, g_bgp, "ibgp_l3_cluster", "rr_cluster", default=None) # TODO: add more detailed logging for n in g_bgp: # Tag with label to make logic clearer if n.ibgp_role is None: n.ibgp_role = "Peer" # TODO: if top-level, then don't mark as RRC ibgp_nodes = [n for n in g_bgp if not n.ibgp_role is "Disabled"] # Notify user of non-ibgp nodes non_ibgp_nodes = [n for n in g_bgp if n.ibgp_role is "Disabled"] if 0 < len(non_ibgp_nodes) < 10: log.info("Skipping iBGP for iBGP disabled nodes: %s", non_ibgp_nodes) elif len(non_ibgp_nodes) >= 10: log.info("Skipping iBGP for more than 10 iBGP disabled nodes:" "refer to visualization for resulting topology.") # warn for any nodes that have RR set but no rr_cluster, or HRR set and no # hrr_cluster rr_mismatch = [ n for n in ibgp_nodes if n.ibgp_role == "RR" and n.rr_cluster is None ] if len(rr_mismatch): log.warning( "Some routers are set as RR but have no rr_cluster: %s. Please specify an rr_cluster for peering." % ", ".join(str(n) for n in rr_mismatch)) hrr_mismatch = [ n for n in ibgp_nodes if n.ibgp_role == "HRR" and n.hrr_cluster is None ] if len(hrr_mismatch): log.warning( "Some routers are set as HRR but have no hrr_cluster: %s. Please specify an hrr_cluster for peering." % ", ".join(str(n) for n in hrr_mismatch)) for _, asn_devices in ank_utils.groupby("asn", ibgp_nodes): asn_devices = list(asn_devices) # iBGP peer peers with peers = [n for n in asn_devices if n.ibgp_role == "Peer"] rrs = [n for n in asn_devices if n.ibgp_role == "RR"] hrrs = [n for n in asn_devices if n.ibgp_role == "HRR"] rrcs = [n for n in asn_devices if n.ibgp_role == "RRC"] over_links = [] up_links = [] down_links = [] # 0. RRCs can only belong to either an rr_cluster or a hrr_cluster invalid_rrcs = [ r for r in rrcs if r.rr_cluster is not None and r.hrr_cluster is not None ] if len(invalid_rrcs): message = ", ".join(str(r) for r in invalid_rrcs) log.warning( "RRCs can only have either a rr_cluster or hrr_cluster set. " "The following have both set, and only the rr_cluster will be used: %s", message) # TODO: do we also want to warn for RRCs with no cluster set? Do we also exclude these? # TODO: do we also want to warn for HRRs and RRs with no cluster set? # Do we also exclude these? # 1. Peers: # 1a. Peers connect over to peers over_links += [(s, t) for s in peers for t in peers] # 1b. Peers connect over to RRs over_links += [(s, t) for s in peers for t in rrs] # 2. RRs: # 2a. RRs connect over to Peers over_links += [(s, t) for s in rrs for t in peers] # 2b. RRs connect over to RRs over_links += [(s, t) for s in rrs for t in rrs] # 2c. RRs connect down to RRCs in same rr_cluster down_links += [(s, t) for s in rrs for t in rrcs if s.rr_cluster == t.rr_cluster != None] # 2d. RRs connect down to HRRs in the same rr_cluster down_links += [(s, t) for s in rrs for t in hrrs if s.rr_cluster == t.rr_cluster != None] # 3. HRRs # 3a. HRRs connect up to RRs in the same rr_cluster up_links += [(s, t) for s in hrrs for t in rrs if s.rr_cluster == t.rr_cluster != None] # 3b. HRRs connect down to RRCs in same hrr_cluster (providing RRC has # no rr_cluster set) down_links += [ (s, t) for s in hrrs for t in rrcs if s.hrr_cluster == t.hrr_cluster != None and t.rr_cluster is None ] # 4. RRCs # 4a. RRCs connect up to RRs in the same rr_cluster (regardless if RRC # has hrr_cluster set) up_links += [(s, t) for s in rrcs for t in rrs if s.rr_cluster == t.rr_cluster != None] # 3b. RRCs connect up to HRRs in same hrr_cluster (providing RRC has no # rr_cluster set) up_links += [ (s, t) for s in rrcs for t in hrrs if s.hrr_cluster == t.hrr_cluster != None and s.rr_cluster is None ] # Remove self-links over_links = [(s, t) for s, t in over_links if s != t] up_links = [(s, t) for s, t in up_links if s != t] down_links = [(s, t) for s, t in down_links if s != t] g_bgp.add_edges_from(over_links, type='ibgp', direction='over') g_bgp.add_edges_from(up_links, type='ibgp', direction='up') g_bgp.add_edges_from(down_links, type='ibgp', direction='down')
def build_phy(anm): """Build physical overlay""" g_in = anm["input"] g_phy = anm["phy"] if g_in.data.enable_routing is not None: g_phy.data.enable_routing = g_in.data.enable_routing if g_in.data.mgmt_block: g_phy.data["mgmt_block"] = g_in.data["mgmt_block"] if g_in.data.ignite: g_phy.data["ignite"] = g_in.data["ignite"] if g_in.data.pc_only is not None: g_phy.data["pc_only"] = g_in.data["pc_only"] if g_in.data.vpcid_block: g_phy.data["vpcid_block"] = g_in.data["vpcid_block"] if g_in.data.vxlan_global_config: g_phy.data["vxlan_global_config"] = g_in.data["vxlan_global_config"] if g_phy.data.enable_routing is None: g_in.data.enable_routing = True # default if not set g_phy.add_nodes_from( g_in, retain=[ "name", "label", "update", "device_type", "devsubtype", "asn", "specified_int_names", "x", "y", "device_subtype", "platform", "host", "syntax", "profile", "syslog", "vpc-peer", "vxlan_vni_configured", ], ) if g_in.data.Creator == "Topology Zoo Toolset": ank_utils.copy_attr_from(g_in, g_phy, "Network") ank_utils.set_node_default(g_phy, Network=None) g_phy.add_edges_from(g_in.edges(type="physical"), retain=["link_type"]) # TODO: make this automatic if adding to the physical graph? ank_utils.set_node_default(g_phy, use_ipv4=False, use_ipv6=False) ank_utils.copy_attr_from(g_in, g_phy, "custom_config_global", dst_attr="custom_config") for node in g_phy: if node["input"].custom_config_loopback_zero: lo_zero_config = node["input"].custom_config_loopback_zero node.loopback_zero.custom_config = lo_zero_config custom_config_phy_ints = node["input"].custom_config_phy_ints for interface in node: if custom_config_phy_ints: interface.custom_config = custom_config_phy_ints specified_id = interface["input"].get("specified_id") if specified_id: interface.specified_id = specified_id # map across # TODO: tidy this code up for node in g_phy: for interface in node: remote_edges = interface.edges() if len(remote_edges): interface.description = "to %s" % remote_edges[0].dst.label
def build_bgp(anm): """Build iBGP end eBGP overlays""" # eBGP g_in = anm['input'] g_phy = anm['phy'] build_ebgp(anm) build_ebgp_v4(anm) build_ebgp_v6(anm) """TODO: remove from here once compiler updated""" g_bgp = anm.add_overlay("bgp", directed=True) g_bgp.add_nodes_from(g_in.nodes("is_router")) ebgp_edges = [edge for edge in g_in.edges() if not edge.attr_equal("asn")] g_bgp.add_edges_from(ebgp_edges, bidirectional=True, type='ebgp') #TODO: why don't we include edge_id here ebgp_switches = [ n for n in g_in.nodes("is_switch") if not ank_utils.neigh_equal(g_phy, n, "asn") ] g_bgp.add_nodes_from(ebgp_switches, retain=['asn']) log.debug("eBGP switches are %s" % ebgp_switches) g_bgp.add_edges_from((e for e in g_in.edges() if e.src in ebgp_switches or e.dst in ebgp_switches), bidirectional=True, type='ebgp') ank_utils.aggregate_nodes(g_bgp, ebgp_switches, retain="edge_id") ebgp_switches = list( g_bgp.nodes("is_switch")) # need to recalculate as may have aggregated log.debug("aggregated eBGP switches are %s" % ebgp_switches) exploded_edges = ank_utils.explode_nodes(g_bgp, ebgp_switches, retain="edge_id") for edge in exploded_edges: edge.multipoint = True """TODO: remove up to here once compiler updated""" # now iBGP ank_utils.copy_attr_from(g_in, g_bgp, "ibgp_level") ank_utils.copy_attr_from(g_in, g_bgp, "ibgp_l2_cluster") ank_utils.copy_attr_from(g_in, g_bgp, "ibgp_l3_cluster") for node in g_bgp: # set defaults if node.ibgp_level is None: node.ibgp_level = 1 if node.ibgp_level == "None": # if unicode string from yEd node.ibgp_level = 1 #TODO CHECK FOR IBGP NONE node.ibgp_level = int(node.ibgp_level) # ensure is numeric if not node.ibgp_l2_cluster or node.ibgp_l2_cluster == "None": # ibgp_l2_cluster defaults to region node.ibgp_l2_cluster = node.region or "default_l2_cluster" if not node.ibgp_l3_cluster or node.ibgp_l3_cluster == "None": # ibgp_l3_cluster defaults to ASN node.ibgp_l3_cluster = node.asn for asn, devices in ank_utils.groupby("asn", g_bgp): # group by nodes in phy graph routers = list(g_bgp.node(n) for n in devices if n.is_router) # list of nodes from bgp graph ibgp_levels = {int(r.ibgp_level) for r in routers} max_level = max(ibgp_levels) # all possible edge src/dst pairs ibgp_routers = [r for r in routers if r.ibgp_level > 0] all_pairs = [(s, t) for s in ibgp_routers for t in ibgp_routers if s != t] if max_level == 3: up_links, down_links, over_links = three_tier_ibgp_edges( ibgp_routers) elif max_level == 2: up_links, down_links, over_links = build_two_tier_ibgp( ibgp_routers) elif max_level == 1: up_links = [] down_links = [] over_links = [(s, t) for (s, t) in all_pairs if s.ibgp_l3_cluster == t.ibgp_l3_cluster and s.ibgp_l2_cluster == t.ibgp_l2_cluster] else: # no iBGP up_links = [] down_links = [] over_links = [] if max_level > 0: g_bgp.add_edges_from(up_links, type='ibgp', direction='up') g_bgp.add_edges_from(down_links, type='ibgp', direction='down') g_bgp.add_edges_from(over_links, type='ibgp', direction='over') else: log.debug("No iBGP routers in %s" % asn) # and set label back ibgp_label_to_level = { 0: "None", # Explicitly set role to "None" -> Not in iBGP 3: "RR", 1: "RRC", 2: "HRR", } for node in g_bgp: node.ibgp_role = ibgp_label_to_level[node.ibgp_level] ebgp_nodes = [ d for d in g_bgp if any(edge.type == 'ebgp' for edge in d.edges()) ] g_bgp.update(ebgp_nodes, ebgp=True) for ebgp_edge in g_bgp.edges(type="ebgp"): for interface in ebgp_edge.interfaces(): interface.ebgp = True for edge in g_bgp.edges(type='ibgp'): # TODO: need interface querying/selection. rather than hard-coded ids edge.bind_interface(edge.src, 0) #TODO: need to initialise interface zero to be a loopback rather than physical type for node in g_bgp: for interface in node.interfaces(): interface.multipoint = any(e.multipoint for e in interface.edges()) build_ibgp_v4(anm) build_ibgp_v6(anm)
def build_ibgp(anm): g_in = anm['input'] g_bgp = anm['bgp'] # TODO: build direct to ibgp graph - can construct combined bgp for vis #TODO: normalise input property ank_utils.copy_attr_from(g_in, g_bgp, "ibgp_role") ank_utils.copy_attr_from( g_in, g_bgp, "ibgp_l2_cluster", "hrr_cluster", default=None) ank_utils.copy_attr_from( g_in, g_bgp, "ibgp_l3_cluster", "rr_cluster", default=None) # TODO: add more detailed logging for n in g_bgp: # Tag with label to make logic clearer if n.ibgp_role is None: n.ibgp_role = "Peer" # TODO: if top-level, then don't mark as RRC ibgp_nodes = [n for n in g_bgp if not n.ibgp_role is "Disabled"] # Notify user of non-ibgp nodes non_ibgp_nodes = [n for n in g_bgp if n.ibgp_role is "Disabled"] if 0 < len(non_ibgp_nodes) < 10: log.info("Skipping iBGP for iBGP disabled nodes: %s", non_ibgp_nodes) elif len(non_ibgp_nodes) >= 10: log.info("Skipping iBGP for more than 10 iBGP disabled nodes:", "refer to visualization for resulting topology.") # warn for any nodes that have RR set but no rr_cluster, or HRR set and no # hrr_cluster rr_mismatch = [ n for n in ibgp_nodes if n.ibgp_role == "RR" and n.rr_cluster is None] if len(rr_mismatch): log.warning("Some routers are set as RR but have no rr_cluster: %s. Please specify an rr_cluster for peering." % ", ".join(str(n) for n in rr_mismatch)) hrr_mismatch = [ n for n in ibgp_nodes if n.ibgp_role == "HRR" and n.hrr_cluster is None] if len(hrr_mismatch): log.warning("Some routers are set as HRR but have no hrr_cluster: %s. Please specify an hrr_cluster for peering." % ", ".join(str(n) for n in hrr_mismatch)) for _, asn_devices in ank_utils.groupby("asn", ibgp_nodes): asn_devices = list(asn_devices) # iBGP peer peers with peers = [n for n in asn_devices if n.ibgp_role == "Peer"] rrs = [n for n in asn_devices if n.ibgp_role == "RR"] hrrs = [n for n in asn_devices if n.ibgp_role == "HRR"] rrcs = [n for n in asn_devices if n.ibgp_role == "RRC"] over_links = [] up_links = [] down_links = [] # 0. RRCs can only belong to either an rr_cluster or a hrr_cluster invalid_rrcs = [r for r in rrcs if r.rr_cluster is not None and r.hrr_cluster is not None] if len(invalid_rrcs): message = ", ".join(str(r) for r in invalid_rrcs) log.warning("RRCs can only have either a rr_cluster or hrr_cluster set. " "The following have both set, and only the rr_cluster will be used: %s", message) # TODO: do we also want to warn for RRCs with no cluster set? Do we also exclude these? # TODO: do we also want to warn for HRRs and RRs with no cluster set? # Do we also exclude these? # 1. Peers: # 1a. Peers connect over to peers over_links += [(s, t) for s in peers for t in peers] # 1b. Peers connect over to RRs over_links += [(s, t) for s in peers for t in rrs] # 2. RRs: # 2a. RRs connect over to Peers over_links += [(s, t) for s in rrs for t in peers] # 2b. RRs connect over to RRs over_links += [(s, t) for s in rrs for t in rrs] # 2c. RRs connect down to RRCs in same rr_cluster down_links += [(s, t) for s in rrs for t in rrcs if s.rr_cluster == t.rr_cluster != None] # 2d. RRs connect down to HRRs in the same rr_cluster down_links += [(s, t) for s in rrs for t in hrrs if s.rr_cluster == t.rr_cluster != None] # 3. HRRs # 3a. HRRs connect up to RRs in the same rr_cluster up_links += [(s, t) for s in hrrs for t in rrs if s.rr_cluster == t.rr_cluster != None] # 3b. HRRs connect down to RRCs in same hrr_cluster (providing RRC has # no rr_cluster set) down_links += [(s, t) for s in hrrs for t in rrcs if s.hrr_cluster == t.hrr_cluster != None and t.rr_cluster is None] # 4. RRCs # 4a. RRCs connect up to RRs in the same rr_cluster (regardless if RRC # has hrr_cluster set) up_links += [(s, t) for s in rrcs for t in rrs if s.rr_cluster == t.rr_cluster != None] # 3b. RRCs connect up to HRRs in same hrr_cluster (providing RRC has no # rr_cluster set) up_links += [(s, t) for s in rrcs for t in hrrs if s.hrr_cluster == t.hrr_cluster != None and s.rr_cluster is None] # Remove self-links over_links = [(s, t) for s, t in over_links if s != t] up_links = [(s, t) for s, t in up_links if s != t] down_links = [(s, t) for s, t in down_links if s != t] g_bgp.add_edges_from(over_links, type='ibgp', direction='over') g_bgp.add_edges_from(up_links, type='ibgp', direction='up') g_bgp.add_edges_from(down_links, type='ibgp', direction='down')
def build_bgp(anm): """Build iBGP end eBGP overlays""" # eBGP g_in = anm["input"] g_phy = anm["phy"] g_bgp = anm.add_overlay("bgp", directed=True) g_bgp.add_nodes_from(g_in.nodes("is_router")) ebgp_edges = [edge for edge in g_in.edges() if not edge.attr_equal("asn")] g_bgp.add_edges_from(ebgp_edges, bidirectional=True, type="ebgp") # TODO: why don't we include edge_id here ebgp_switches = [n for n in g_in.nodes("is_switch") if not ank_utils.neigh_equal(g_phy, n, "asn")] g_bgp.add_nodes_from(ebgp_switches, retain=["asn"]) log.debug("eBGP switches are %s" % ebgp_switches) g_bgp.add_edges_from( (e for e in g_in.edges() if e.src in ebgp_switches or e.dst in ebgp_switches), bidirectional=True, type="ebgp" ) ank_utils.aggregate_nodes(g_bgp, ebgp_switches, retain="edge_id") ebgp_switches = list(g_bgp.nodes("is_switch")) # need to recalculate as may have aggregated log.debug("aggregated eBGP switches are %s" % ebgp_switches) exploded_edges = ank_utils.explode_nodes(g_bgp, ebgp_switches, retain="edge_id") for edge in exploded_edges: edge.multipoint = True # now iBGP ank_utils.copy_attr_from(g_in, g_bgp, "ibgp_level") ank_utils.copy_attr_from(g_in, g_bgp, "ibgp_l2_cluster") ank_utils.copy_attr_from(g_in, g_bgp, "ibgp_l3_cluster") for node in g_bgp: # set defaults if node.ibgp_level is None: node.ibgp_level = 1 if node.ibgp_level == "None": # if unicode string from yEd node.ibgp_level = 1 # TODO CHECK FOR IBGP NONE node.ibgp_level = int(node.ibgp_level) # ensure is numeric if not node.ibgp_l2_cluster or node.ibgp_l2_cluster == "None": # ibgp_l2_cluster defaults to region node.ibgp_l2_cluster = node.region or "default_l2_cluster" if not node.ibgp_l3_cluster or node.ibgp_l3_cluster == "None": # ibgp_l3_cluster defaults to ASN node.ibgp_l3_cluster = node.asn for asn, devices in ank_utils.groupby("asn", g_bgp): # group by nodes in phy graph routers = list(g_bgp.node(n) for n in devices if n.is_router) # list of nodes from bgp graph ibgp_levels = {int(r.ibgp_level) for r in routers} max_level = max(ibgp_levels) # all possible edge src/dst pairs ibgp_routers = [r for r in routers if r.ibgp_level > 0] all_pairs = [(s, t) for s in ibgp_routers for t in ibgp_routers if s != t] if max_level == 3: up_links, down_links, over_links = three_tier_ibgp_edges(ibgp_routers) elif max_level == 2: up_links, down_links, over_links = build_two_tier_ibgp(ibgp_routers) elif max_level == 1: up_links = [] down_links = [] over_links = [ (s, t) for (s, t) in all_pairs if s.ibgp_l3_cluster == t.ibgp_l3_cluster and s.ibgp_l2_cluster == t.ibgp_l2_cluster ] else: # no iBGP up_links = [] down_links = [] over_links = [] if max_level > 0: g_bgp.add_edges_from(up_links, type="ibgp", direction="up") g_bgp.add_edges_from(down_links, type="ibgp", direction="down") g_bgp.add_edges_from(over_links, type="ibgp", direction="over") else: log.debug("No iBGP routers in %s" % asn) # and set label back ibgp_label_to_level = {0: "None", 3: "RR", 1: "RRC", 2: "HRR"} # Explicitly set role to "None" -> Not in iBGP for node in g_bgp: node.ibgp_role = ibgp_label_to_level[node.ibgp_level] ebgp_nodes = [d for d in g_bgp if any(edge.type == "ebgp" for edge in d.edges())] g_bgp.update(ebgp_nodes, ebgp=True) for ebgp_edge in g_bgp.edges(type="ebgp"): for interface in ebgp_edge.interfaces(): interface.ebgp = True for edge in g_bgp.edges(type="ibgp"): # TODO: need interface querying/selection. rather than hard-coded ids edge.bind_interface(edge.src, 0) # TODO: need to initialise interface zero to be a loopback rather than physical type for node in g_bgp: for interface in node.interfaces(): interface.multipoint = any(e.multipoint for e in interface.edges())