Example #1
0
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
Example #2
0
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)
Example #3
0
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)
Example #4
0
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
Example #5
0
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)
Example #6
0
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?
Example #7
0
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)
Example #8
0
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?
Example #9
0
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)
Example #10
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_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
Example #11
0
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
Example #12
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', '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
Example #13
0
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
Example #14
0
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)
Example #15
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")

    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
Example #16
0
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
Example #17
0
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
Example #18
0
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
Example #19
0
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
Example #20
0
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
Example #21
0
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
Example #22
0
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
Example #23
0
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
Example #24
0
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
Example #25
0
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
Example #26
0
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
Example #27
0
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
Example #28
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', '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)
Example #29
0
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
Example #30
0
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
Example #31
0
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)
Example #32
0
        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")
Example #33
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)

    #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
Example #34
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
Example #35
0
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
Example #36
0
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
Example #37
0
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
Example #38
0
    # 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)
Example #39
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)

    #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
Example #40
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)
Example #41
0
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)
Example #42
0
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')
Example #43
0
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
Example #44
0
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)
Example #45
0
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')
Example #46
0
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())