示例#1
0
文件: naming.py 项目: iainwp/ank_le
def dump_identifiers(network, filename):
    with open( filename, 'w') as f_dump:
# writes out lo_ip for routers, and identifying IP for servers
        for my_as in ank.get_as_graphs(network):
            for router in sorted(network.routers(my_as.asn), key = lambda x: x.fqdn):
                f_dump.write( "%s\t%s\n" % (router, router.lo_ip.ip))
            for server in sorted(network.servers(my_as.asn), key = lambda x: x.fqdn):
                    f_dump.write( "%s\t%s\n" % (server, server_ip(server)))
            f_dump.write("\n")
示例#2
0
def inv_cap_weights(network):
    """Updates link weights based on inverse of link speed."""
    #TODO: rewrite this to be cleaner iteration and setting
    for graph in ank.get_as_graphs(network):
        for (src, dst, data) in graph.edges_iter(data=True):    
            # only update if non default weight       
            if 'speed' in data and 'weight' in data and data['weight'] == 1: 
                # assume largest link is 10gb 
                #TODO: use Cisco guidelines for this
                scale_speed = 100000      
                speed = float(data['speed'])
                weight = int((1/speed)*scale_speed)    
                weight = max(weight, 1)
                if weight is 0:
                    weight = 1
                    graph[src][dst]['weight'] = weight
                    network.set_edge_property(src, dst, 'weight', weight)
    return
示例#3
0
    def configure(self):
        """Configure C-BGP"""
        LOG.info("Configuring C-BGP")
        self.initialise()
        default_weight = 1
        template = lookup.get_template("cbgp/cbgp.mako")
        physical_graph = self.network.graph
        igp_graph = ank.igp_graph(self.network)
        ibgp_graph = ank.get_ibgp_graph(self.network)
        ebgp_graph = ank.get_ebgp_graph(self.network)
        as_graphs = ank.get_as_graphs(self.network)
        ip_as_allocs = ank.get_ip_as_allocs(self.network) # Allocs for ebgp announcements

        physical_topology = defaultdict(dict)
        ibgp_topology = {} 
        igp_topology = {} 
        ebgp_topology = {}
        ebgp_prefixes = {}
        bgp_routers = {}

# Fast lookup of loopbacks - the unique router ID for cBGP
        loopback = dict( (n, self.network.lo_ip(n).ip) for n in physical_graph)

# Physical topology
        for as_graph in as_graphs:
            asn = as_graph.name
            physical_topology[asn]['nodes'] = [loopback[n] for n in as_graph]
            physical_topology[asn]['links'] = [ (loopback[s], loopback[t]) 
                    for (s,t) in unidirectional(as_graph.edges())]

# Interdomain links
        interdomain_links =  [ (loopback[s], loopback[t])
                for (s,t) in unidirectional(ebgp_graph.edges())]

#IGP configuration
        for as_graph in as_graphs:
            asn = as_graph.name
            igp_topology[asn] = {}
# Subgraph of IGP graph for this AS
            as_igp_graph = igp_graph.subgraph(as_graph.nodes())
            igp_topology[asn]['nodes'] = [loopback[n] for n in as_igp_graph]
            igp_topology[asn]['links'] = [ (loopback[s], loopback[t], data.get('weight', default_weight)) 
                    for (s,t,data) in (as_graph.edges(data=True))]

# iBGP configuration
#TODO: if ibgp graph is a clique then use "bgp domain 1 full-mesh" where 1 is asn
# use nx.graph_clique_number(G) and compare to size of G, if same then is a clique
# otherwise create ibgp session by session

#TODO: add support for non full-mesh (need to find documentation on this)
        for as_graph in as_graphs:
            asn = as_graph.name
            for router in as_graph:
                if not router.is_router:
                    continue
                if router not in ibgp_graph:
# likely single node AS
                    continue
                ibgp_topology[router] = []
                for peer in ibgp_graph.neighbors(router):
                    ibgp_topology[router].append(peer)
            bgp_routers[asn] = [n.lo_ip.ip for n in ank.bgp_routers(self.network)
                    if n.asn == asn]

# eBGP configuration
        for node in ebgp_graph.nodes():
            node_id = loopback[node]
            peers = []
            for peer in ebgp_graph.neighbors(node):
                peers.append( (self.network.asn(peer), loopback[peer]))
            ebgp_topology[node_id] = peers
# Prefixes to originate
            adv_subnet = ip_as_allocs[self.network.asn(node)]
            ebgp_prefixes[node_id] = adv_subnet

            #TODO: see if can just do for node in ebgp_graph ie without the .nodes() on end

        # bgp policy
        bgp_policy = {}
        for router in self.network.routers():
            for peer in self.network.g_session.neighbors(router):
                pol_egress = self.network.g_session[router][peer]['egress']
                pol_ingress = self.network.g_session[peer][router]['ingress']
                if len(pol_ingress) or len(pol_egress):
                    try:
                        bgp_policy[router][peer] = {
                                'ingress': pol_ingress,
                                'egress': pol_egress,
                                }
                    except KeyError:
                        bgp_policy[router] = {}
                        bgp_policy[router][peer] = {
                                'ingress': pol_ingress,
                                'egress': pol_egress,
                                }


        # tags dict for mapping from tag to community value, and for prefixes
        tags = self.network.g_session.graph['tags']
        prefixes = self.network.g_session.graph['prefixes']

        with open( cbgp_file(), 'w') as f_cbgp:
                f_cbgp.write( template.render(
                   physical_topology = physical_topology,
                   interdomain_links = interdomain_links,
                   igp_topology = igp_topology,
                   ibgp_topology = ibgp_topology,
                   ebgp_topology = ebgp_topology,
                   ebgp_prefixes = ebgp_prefixes,
                   bgp_routers = bgp_routers,
                   bgp_policy = bgp_policy,
                   tags = tags,
                   prefixes = prefixes,
                   ))
示例#4
0
文件: ip.py 项目: iainwp/ank_le
def allocate_subnets(network, address_block=IPNetwork("10.0.0.0/8")):

    """Allocates subnets and IP addresses to links in the network.

    Args:
        address_block (IPNetwork):  The address block to use.

    Returns:
        ip_as_allocs

    Example usage:

    >>> network = ank.example_multi_as()
    >>> allocate_subnets(network)
    >>> print ank.debug_nodes(network.graph, "lo_ip")
    {'1a.AS1': IPNetwork('10.0.0.32/32'),
     '1b.AS1': IPNetwork('10.0.0.33/32'),
     '1c.AS1': IPNetwork('10.0.0.34/32'),
     '2a.AS2': IPNetwork('10.1.0.64/32'),
     '2b.AS2': IPNetwork('10.1.0.65/32'),
     '2c.AS2': IPNetwork('10.1.0.66/32'),
     '2d.AS2': IPNetwork('10.1.0.67/32'),
     '3a.AS3': IPNetwork('10.2.0.0/32')}
    
    >>> print ank.debug_edges(network.graph, "ip")
    {('1a.AS1', '1b.AS1'): IPAddress('10.0.0.10'),
     ('1a.AS1', '1c.AS1'): IPAddress('10.0.0.22'),
     ('1b.AS1', '1a.AS1'): IPAddress('10.0.0.9'),
     ('1b.AS1', '1c.AS1'): IPAddress('10.0.0.26'),
     ('1b.AS1', '3a.AS3'): IPAddress('10.0.0.17'),
     ('1c.AS1', '1a.AS1'): IPAddress('10.0.0.21'),
     ('1c.AS1', '1b.AS1'): IPAddress('10.0.0.25'),
     ('1c.AS1', '2a.AS2'): IPAddress('10.0.0.29'),
     ('2a.AS2', '1c.AS1'): IPAddress('10.0.0.30'),
     ('2a.AS2', '2b.AS2'): IPAddress('10.1.0.10'),
     ('2a.AS2', '2d.AS2'): IPAddress('10.1.0.26'),
     ('2b.AS2', '2a.AS2'): IPAddress('10.1.0.9'),
     ('2b.AS2', '2c.AS2'): IPAddress('10.1.0.18'),
     ('2c.AS2', '2b.AS2'): IPAddress('10.1.0.17'),
     ('2c.AS2', '2d.AS2'): IPAddress('10.1.0.30'),
     ('2d.AS2', '2a.AS2'): IPAddress('10.1.0.25'),
     ('2d.AS2', '2c.AS2'): IPAddress('10.1.0.29'),
     ('2d.AS2', '3a.AS3'): IPAddress('10.1.0.33'),
     ('3a.AS3', '1b.AS1'): IPAddress('10.0.0.18'),
     ('3a.AS3', '2d.AS2'): IPAddress('10.1.0.34')}

    
    >>> print ank.debug_edges(network.graph, "sn")
    {('1a.AS1', '1b.AS1'): IPNetwork('10.0.0.8/30'),
     ('1a.AS1', '1c.AS1'): IPNetwork('10.0.0.20/30'),
     ('1b.AS1', '1a.AS1'): IPNetwork('10.0.0.8/30'),
     ('1b.AS1', '1c.AS1'): IPNetwork('10.0.0.24/30'),
     ('1b.AS1', '3a.AS3'): IPNetwork('10.0.0.16/30'),
     ('1c.AS1', '1a.AS1'): IPNetwork('10.0.0.20/30'),
     ('1c.AS1', '1b.AS1'): IPNetwork('10.0.0.24/30'),
     ('1c.AS1', '2a.AS2'): IPNetwork('10.0.0.28/30'),
     ('2a.AS2', '1c.AS1'): IPNetwork('10.0.0.28/30'),
     ('2a.AS2', '2b.AS2'): IPNetwork('10.1.0.8/30'),
     ('2a.AS2', '2d.AS2'): IPNetwork('10.1.0.24/30'),
     ('2b.AS2', '2a.AS2'): IPNetwork('10.1.0.8/30'),
     ('2b.AS2', '2c.AS2'): IPNetwork('10.1.0.16/30'),
     ('2c.AS2', '2b.AS2'): IPNetwork('10.1.0.16/30'),
     ('2c.AS2', '2d.AS2'): IPNetwork('10.1.0.28/30'),
     ('2d.AS2', '2a.AS2'): IPNetwork('10.1.0.24/30'),
     ('2d.AS2', '2c.AS2'): IPNetwork('10.1.0.28/30'),
     ('2d.AS2', '3a.AS3'): IPNetwork('10.1.0.32/30'),
     ('3a.AS3', '1b.AS1'): IPNetwork('10.0.0.16/30'),
     ('3a.AS3', '2d.AS2'): IPNetwork('10.1.0.32/30')}
    
    """
    LOG.debug("Allocating subnets")
    # Initialise IP list to be graph edge format
    ip_as_allocs = {}

    # allocates subnets to the edges and loopback in network graph
    # Put into dictionary, indexed by ASN (the name attribute of each as graph)
    # for easy appending of eBGP links
    asgraphs = dict((my_as.asn, my_as) for my_as in ank.get_as_graphs(network))

    # Simple method: break address_block into a /16 for each network
    #TODO: check this is feasible - ie against required host count
    subnet_list = address_block.subnet(16)

    ebgp_edges = ank.ebgp_edges(network)
    visited_ebgp_edges = set()
    for src, dst in sorted(ebgp_edges):
      # Add the dst (external peer) to AS of src node so they are allocated
        # a subnet. (The AS choice is arbitrary)
        if (dst, src) in visited_ebgp_edges:
            continue
        src_as = asgraphs[src.asn]
        src_as.add_edge(src, dst)
# record for DNS purposes
        ank.dns_advertise_link(src, dst)
        visited_ebgp_edges.add( (src, dst))

    for my_as in sorted(asgraphs.values(), key = lambda x: x.asn):
        asn = my_as.asn
        as_subnet =  subnet_list.next()

        as_internal_nodes = [n for n in sorted(my_as.nodes()) if network.asn(n) == asn]

        host_count = my_as.number_of_nodes()

        # record this subnet
        ip_as_allocs[my_as.asn] = as_subnet

        # split into subnets for loopback and ptp
        ptp_count = my_as.number_of_edges()

        # Now subnet network into subnets of the larger of these two
        # TODO tidy up this comment
        # Note ptp subnets required a /30 ie 4 ips
        req_sn_count = max(host_count, 4*ptp_count)

        if req_sn_count == 0:
            # Nothing to allocate for this AS
            continue

        req_pref_len = int(32 - math.ceil(math.log(req_sn_count, 2)) )
        # Subnet as subnet into subnets of this size
        sn_iter = as_subnet.subnet(req_pref_len)
        # And allocate a subnet for each ptp and loopback

        if ptp_count > 0:
            # Don't allocate a ptp subnet if there are no ptp links
            ptp_subnet = sn_iter.next()
        loopback_subnet = sn_iter.next()

        if ptp_count > 0:
            link_subnet = ptp_subnet.subnet(30)

            # Now iterate over edges in this as and apply
            for src, dst in sorted(my_as.edges()):
                # Note we apply this back to the main graph
                # not to the as graph!

                subnet = link_subnet.next()

                #TODO: fix the technique for accessing edges
                # as it breaks with multigraphs, as it creates a new edge
                if network.asn(dst) != asn:
# eBGP link where dst has IP allocated from subnet of this AS
                    network.graph[dst][src]['remote_as_sn_block'] = True

                network.graph[src][dst]['sn'] = subnet
                network.graph[dst][src]['sn'] = subnet
                # allocate an ip to each end
                network.graph[src][dst]['ip'] = subnet[1]
                network.graph[dst][src]['ip'] = subnet[2]

        # Allocate an loopback interface to each router
        #TODO: check if next step is necessary
        loopback_ips = loopback_subnet.subnet(32)
    
        for rtr in sorted(as_internal_nodes):
            lo_ip = loopback_ips.next()
            network.graph.node[rtr]['lo_ip'] = lo_ip

    network.ip_as_allocs = ip_as_allocs
示例#5
0
文件: ip.py 项目: iainwp/ank_le
def alloc_tap_hosts(network, address_block=IPNetwork("172.16.0.0/16")):
    """Allocates TAP IPs for connecting using Netkit

    >>> network = ank.example_multi_as()
    >>> alloc_tap_hosts(network)
    >>> print ank.debug_nodes(network.graph, "tap_ip")
    {'1a.AS1': IPAddress('172.16.1.1'),
     '1b.AS1': IPAddress('172.16.1.2'),
     '1c.AS1': IPAddress('172.16.1.3'),
     '2a.AS2': IPAddress('172.16.2.1'),
     '2b.AS2': IPAddress('172.16.2.2'),
     '2c.AS2': IPAddress('172.16.2.3'),
     '2d.AS2': IPAddress('172.16.2.4'),
     '3a.AS3': IPAddress('172.16.3.1')}
    """
    LOG.debug("Allocating TAP hosts")
    network.tap_sn = address_block

    as_graph = ank.get_as_graphs(network)
    # Try allocating /24 to each subnet as cleaner
    # then check if this is feasible 
    prefix_len = 24
    # Check this will fit into provided network
    #TODO: check what these 2 lines of code are doing
    if prefix_len <= address_block.prefixlen:
        # Try with smaller prefix len
        prefix_len += 1
   
    # Number of bits required for AS subnet (network bits)
    # need to add one on for tap host subnet, eg 172.16.0.0 and 172.16.0.1 hosts
    req_network_bits = int( math.ceil(math.log(len(as_graph) + 1, 2)) )
    upper_bound = prefix_len 
    # Find the subnet with the most hosts in it

    max_req_hosts = max(len(my_as) for my_as in as_graph)
    req_host_bits = int(math.ceil(math.log(max_req_hosts, 2)))
#TODO: there is an off by one error here mking the tap subnets /9 rather than /8
# so end up with 172.16.64.1 and 172.16.128.1 not .1 .2 etc

    # Check subnetting is feasible
    lower_bound = address_block.prefixlen + req_network_bits
    upper_bound = lower_bound + req_host_bits
    if upper_bound > 32:
        #TODO: throw error
        print "Unfeasible tap subnet allocation"
        return
    else:
        prefix_len = lower_bound

    # Neatness: use a Class C, B, A (in order of preference) if feasible
    for x in [24, 16, 8]:
        if lower_bound < x < upper_bound:
            prefix_len = x
        elif lower_bound < upper_bound < x:
# eg both fit inside a class A, B or C
            prefix_len = x

    def set_tap_ips(network, nodes, host_ips):
        # Allocate in order of node name
        for node in sorted(nodes, key=network.label):
            network.graph.node[node]['tap_ip'] = host_ips.next()
        return

    # assign /required subnet size to each as then append pops to list
    if len(as_graph) == 1:
        # Single AS, don't need to subnet the address block
        host_ips = address_block.iter_hosts()
        network.tap_host = host_ips.next()
        _ = host_ips.next() # IP of tap VM
        my_as = as_graph.pop()
        # Allocate directly from address block
        set_tap_ips(network, my_as.nodes(), host_ips)
    else:
        sn_iter = address_block.subnet(prefix_len)
        tap_host_subnet = sn_iter.next()
        #TODO: make consistent with previous section [1] vs .next()
        network.tap_host = tap_host_subnet[1]
        for my_as in as_graph:
            LOG.debug("Setting tap IPs for %s" % my_as.asn)
            host_ips = sn_iter.next().iter_hosts()
            set_tap_ips(network, my_as.nodes(), host_ips)
        
    #TODO: make this a generic function which allocates items from an
    # generator to each node in the specified network
    # rather than IP addresses specifically
    return
示例#6
0
    def configure_bgp(self):
        """Generates BGP specific configuration files"""

        ip_as_allocs = ank.get_ip_as_allocs(self.network)

        LOG.debug("Configuring BGP")
        template = lookup.get_template("quagga/bgp.mako")

        route_maps = {}

        ibgp_graph = ank.get_ibgp_graph(self.network)
        ebgp_graph = ank.get_ebgp_graph(self.network)
        physical_graph = self.network.graph

        for my_as in ank.get_as_graphs(self.network):
            asn = my_as.asn
            LOG.debug("Configuring IGP for AS %s " % asn)
            # get nodes ie intersection
            #H = nx.intersection(my_as, ibgp_graph)
            # get ibgp graph that contains only nodes from this AS

            for router in self.network.routers(asn):
                bgp_groups = {}
                route_maps = []
                ibgp_neighbor_list = []
                ibgp_rr_client_list = []
                route_map_groups = {}

                if router in ibgp_graph:
                        for src, neigh, data in ibgp_graph.edges(router, data=True):
                            route_maps_in = self.network.g_session[neigh][router]['ingress']
                            rm_group_name_in = None
                            if len(route_maps_in):
                                rm_group_name_in = "rm_%s_in" % neigh.folder_name
                                route_map_groups[rm_group_name_in] = [match_tuple 
                                        for route_map in route_maps_in
                                        for match_tuple in route_map.match_tuples]

                            route_maps_out = self.network.g_session[router][neigh]['egress']
                            rm_group_name_out = None
                            if len(route_maps_out):
                                rm_group_name_in = "rm_%s_out" % neigh.folder_name
                                route_map_groups[rm_group_name_out] = [match_tuple 
                                        for route_map in route_maps_out
                                        for match_tuple in route_map.match_tuples]

                            description = data.get("rr_dir") + " to " + ank.fqdn(self.network, neigh)
                            if data.get('rr_dir') == 'down':
                                ibgp_rr_client_list.append(
                                        {
                                            'id':  self.network.lo_ip(neigh).ip,
                                            'description':      description,
                                            'route_maps_in': rm_group_name_in,
                                            'route_maps_out': rm_group_name_out,
                                            })
                            elif (data.get('rr_dir') in set(['up', 'over', 'peer'])
                                    or data.get('rr_dir') is None):
                                ibgp_neighbor_list.append(
                                        {
                                            'id':  self.network.lo_ip(neigh).ip,
                                            'description':      description,
                                            'route_maps_in': rm_group_name_in,
                                            'route_maps_out': rm_group_name_out,
                                            })

                        bgp_groups['internal_peers'] = {
                            'type': 'internal',
                            'neighbors': ibgp_neighbor_list
                            }
                        if len(ibgp_rr_client_list):
                            bgp_groups['internal_rr'] = {
                                    'type': 'internal',
                                    'neighbors': ibgp_rr_client_list,
                                    'cluster': self.network.lo_ip(router).ip,
                                    }

                if router in ebgp_graph:
                    external_peers = []
                    for peer in ebgp_graph.neighbors(router):
                        route_maps_in = self.network.g_session[peer][router]['ingress']
                        rm_group_name_in = None
                        if len(route_maps_in):
                            rm_group_name_in = "rm_%s_in" % peer.folder_name
                            route_map_groups[rm_group_name_in] = [match_tuple 
                                    for route_map in route_maps_in
                                    for match_tuple in route_map.match_tuples]

# Now need to update the sequence numbers for the flattened route maps

                        route_maps_out = self.network.g_session[router][peer]['egress']
                        rm_group_name_out = None
                        if len(route_maps_out):
                            rm_group_name_out = "rm_%s_out" % peer.folder_name
                            route_map_groups[rm_group_name_out] = [match_tuple 
                                    for route_map in route_maps_out
                                    for match_tuple in route_map.match_tuples]

                        peer_ip = physical_graph[peer][router]['ip'] 

                        external_peers.append({
                            'id': peer_ip, 
                            'route_maps_in': rm_group_name_in,
                            'route_maps_out': rm_group_name_out,
                            'peer_as': self.network.asn(peer)})
                    bgp_groups['external_peers'] = {
                            'type': 'external', 
                            'neighbors': external_peers}

# Ensure only one copy of each route map, can't use set due to list inside tuples (which won't hash)
# Use dict indexed by name, and then extract the dict items, dict hashing ensures only one route map per name
                community_lists = {}
                prefix_lists = {}
                node_bgp_data = self.network.g_session.node.get(router)
                if node_bgp_data:
                    community_lists = node_bgp_data.get('tags')
                    prefix_lists = node_bgp_data.get('prefixes')
                policy_options = {
                'community_lists': community_lists,
                'prefix_lists': prefix_lists,
                'route_maps': route_map_groups,
                }
            
                f_handle = open(os.path.join(zebra_dir(self.network, router),
                                                "bgpd.conf"),'wb')

                #TODO: remove community_lists and prefix_lists as they are put into policy_options
                f_handle.write(template.render(
                        hostname = router.device_hostname,
                        asn = self.network.asn(router),
                        password = self.zebra_password,
                        enable_password = self.zebra_password,
                        router_id = self.network.lo_ip(router).ip,
                        community_lists = community_lists,
                        policy_options = policy_options,
                        prefix_lists = prefix_lists,
                        #TODO: see how this differs to router_id
                        identifying_loopback = self.network.lo_ip(router),
                        bgp_groups = bgp_groups,
                        ibgp_neighbor_list = ibgp_neighbor_list,
                        ibgp_rr_client_list = ibgp_rr_client_list,
                        route_maps = route_maps,
                        logfile = "/var/log/zebra/bgpd.log",
                        debug=True,
                        use_debug=True,
                        dump=False,
                        snmp=False,
                        interfaces = self.configure_interfaces(router)
                ))
示例#7
0
    def configure_igp(self):
        """Generates IGP specific configuration files (eg ospfd)"""
        LOG.debug("Configuring IGP")
        template = lookup.get_template("quagga/ospf.mako")
        default_weight = 1

        # configures IGP for each AS
        as_graphs = ank.get_as_graphs(self.network)
        for my_as in as_graphs:
            asn = my_as.asn
            LOG.debug("Configuring IGP for AS %s " % asn)
            if my_as.number_of_edges() == 0:
                # No edges, nothing to configure
                LOG.debug("Skipping IGP for AS%s as no internal links" % asn)
                continue

            for router in self.network.routers(asn):
                #TODO: can probably through most of these straight into the template and use properties there!

                interface_list = []
                network_list = []

                # Add loopback info
                lo_ip = router.lo_ip 
                interface_list.append ( {'id':  "lo", 'weight':  1,
                                        'remote_router': "NA (loopback)",
                                        'remote_int': "Loopback"})
                network_list.append ( { 'cidr': lo_ip.cidr, 'ip': lo_ip.ip,
                                    'netmask': lo_ip.netmask,
                                    'area': 0, 'remote_ip': "Loopback" })

                for link in self.network.links(router, my_as):

                    int_id = self.interface_id(link.id)
                    weight = link.weight or default_weight
                    interface_list.append ({ 'id':  int_id,
                                            'weight': weight,
                                            'remote_router': link.remote_host, } )

                    # fetch and format the ip details
                    subnet = link.subnet
                    local_ip = link.local_ip
                    remote_ip = link.remote_ip
                    network_list.append ( { 'cidr': subnet.cidr, 'ip': local_ip,
                                        'netmask': subnet.netmask,
                                        'remote_ip': remote_ip, 'area': 0, } )

                #TODO: see if need to use router-id for ospfd in quagga
                f_handle = open( os.path.join(zebra_dir(self.network, router),
                        "ospfd.conf"), 'wb')

                f_handle.write(template.render
                               (
                                   hostname = router.device_hostname,
                                   password = self.zebra_password,
                                   enable_password = self.zebra_password,
                                   interface_list = interface_list,
                                   network_list = network_list,
                                   routerID = router,
                                   use_igp = True,
                                   logfile = "/var/log/zebra/ospfd.log",
                                   use_debug = False,
                               ))
示例#8
0
def allocate_dns_servers(network):
    """Allocates DNS according to rules defined above
    
    TODO: allow 3 level (ie no pop caching, clients connect to AS server)
    TODO: make DNS servers standalone rather that co-hosted with router
    
    TODO: note set dns level on dns graph, but ibgp level on physical graph - inconsistent!
    
    """
    dns_graph = nx.DiGraph()
    dns_advertise_graph = nx.DiGraph()
    LOG.debug("DNS currently disabled")

    hierarchical_dns = config.settings['DNS']['hierarchical']
    if hierarchical_dns:
        LOG.info("Configuring hierarchical DNS")
        dns_levels = 4
    else:
        dns_levels = 1
        LOG.debug("Non-hierarchical DNS not yet implemented")
#TODO: do "flat" dns - one root server, add all other devices as children for both resolving and authoritative

        return


    def nodes_by_eccentricity(graph):
        if len(graph) == 1:
            return graph.nodes()
# need to crop the global shortest paths otherwise get 
#NetworkXError: Graph not connected: infinite path length
        eccentricities = nx.eccentricity(graph)
        return sorted(eccentricities.keys(), key = lambda n: eccentricities[n])

    def format_asn(asn):
        """Returns unique format for asn, so don't confuse with property of the same,
        eg if ibgp_l2_cluster = 1 in as2, it could match as1 routers as 1==1
        so set asn_1 so 1 != asn_1"""
        return "asn_%s" % asn

    def get_l2_cluster(node):
        """syntactic sugar to access cluster"""
        return dns_graph.node[node].get("dns_l2_cluster")

    def get_l3_cluster(node):
        """syntactic sugar to access cluster"""
        return dns_graph.node[node].get("dns_l3_cluster")

    def level(u):
        return int(dns_graph.node[u]['level'])

    servers_per_l2_cluster = config.settings['DNS']['Server Count']['l2 cluster'] 
    servers_per_l3_cluster = config.settings['DNS']['Server Count']['l3 cluster'] 
    root_dns_servers = config.settings['DNS']['Server Count']['root'] 
    global_eccentricities = nodes_by_eccentricity(network.graph)


#TODO: add count of each cluster occurence so can round servers down - dont want 3 servers in a one router network!

# Add routers, these form the level 1 clients
    dns_graph.add_nodes_from(network.graph.nodes(), level=1)
    for node, data in network.graph.nodes(data=True):
        #TODO: the cluster should never be manually set, so can remove checks
        if not data.get("dns_l2_cluster"):
            dns_graph.node[node]['dns_l2_cluster'] = data.get("pop") or format_asn(network.asn(node))
        if not data.get("dns_l3_cluster"):
            dns_graph.node[node]['dns_l3_cluster'] = format_asn(network.asn(node))

    for my_as in ank.get_as_graphs(network):
        asn = my_as.asn
        if not nx.is_strongly_connected(my_as):
            LOG.info("AS%s not fully connected, skipping DNS configuration" % asn)
            continue

        l2_clusters = list(set(dns_graph.node[n].get("dns_l2_cluster") for n in my_as))
        for l2_cluster in l2_clusters:
            for index in range(servers_per_l2_cluster):
                label = "l2_%s_dns_%s" % (l2_cluster, index+1)
                if l2_cluster == format_asn(asn):
# Don't put asn into server name twice "AS2_asn_2_l2dns_1" vs "asn_2_l2dns_1"
                    server_name = "%s_l2dns_%s" % (l2_cluster, index+1)
                else:
                    server_name = "AS%s_%s_l2dns_%s" % (asn, l2_cluster, index+1)
#TODO: see what other properties to retain
                node_name = network.add_device(server_name, asn=asn, 
                        device_type='server', label=label)
                dns_graph.add_node(node_name, level=2, dns_l2_cluster=l2_cluster,
                        asn = asn, dns_l3_cluster = format_asn(asn))

        for index in range(servers_per_l3_cluster):
                label = "l3_%s_dns_%s" % (asn, index+1)
                server_name = "AS%s_l3dns_%s" % (asn, index+1)
                node_name = network.add_device(server_name, asn=asn, 
                        device_type='server', label=label)
#TODO: check if need to add l2 here - was coded before, possible mistake?
                dns_graph.add_node(node_name, level=3, 
                        asn = asn, dns_l3_cluster = format_asn(asn))
    
    # and level 4 connections
#TODO: need to determine the right place to put the server - order issue between allocating for root as need an ASN for the device before  know best place - for now use asn = 1, and move if needed
    for index in range(root_dns_servers):
        attach_point = global_eccentricities.pop()
        server_name = "root_dns_%s" % (index+1)
        asn = ank.asn(attach_point)
        LOG.debug("Attaching %s to %s in %s" % (server_name, ank.label(attach_point), asn))
        node_name = network.add_device(server_name, asn=asn, device_type='server')
        network.add_link(node_name, attach_point)
        dns_graph.add_node(node_name, level=4)
        
    # now connect
#TODO: scale to handle multiple levels same as ibgp (see doco at start for details)
    edges_to_add = []
    all_edges = [ (s,t) for s in dns_graph for t in dns_graph if s != t]
    same_l3_cluster_edges = [ (s,t) for (s,t) in all_edges if 
                    get_l3_cluster(s) == get_l3_cluster(t) != None]
    same_l2_cluster_edges = [ (s,t) for (s,t) in same_l3_cluster_edges if 
                    get_l2_cluster(s) == get_l2_cluster(t) != None]
    
# l1 -> l2 same l2 cluster
    edges_to_add += [(s,t, 'up') for (s,t) in same_l2_cluster_edges
            if level(s) == 1 and level(t) == 2]
    # l2 -> l2 ???

# l2 -> l3
    edges_to_add += [(s,t, 'up') for (s,t) in same_l3_cluster_edges
            if level(s) == 2 and level(t) == 3]

# l3 -> l4
    edges_to_add += [(s,t, 'up') for (s,t) in all_edges
            if level(s) == 3 and level(t) == 4]
    
    # format into networkx format
    edges_to_add = ( (s,t, {'dns_dir': dns_dir}) for (s, t, dns_dir) in edges_to_add)
    dns_graph.add_edges_from(edges_to_add)

# and create attach points
# take advantage of Python sorts being stable
# refer http://wiki.python.org/moin/HowTo/Sorting
#TODO: note assumes routers are level 1 - need to also check type is router!
    routers = set(network.routers())
    devices = dns_graph.nodes()
    devices = sorted(devices, key= get_l2_cluster)
    devices = sorted(devices, key= get_l3_cluster)
    devices = sorted(devices, key= ank.asn)
    for asn, asn_devices in itertools.groupby(devices, key = ank.asn):
# if no asn set, then root server, which has already been allocated
        if asn:
            # asn is set, look at l3 groups
            for l3_cluster, l3_cluster_devices in itertools.groupby(asn_devices, key = get_l3_cluster):
                if not l3_cluster:
                    #TODO: see why getting empty cluster
                    continue
                l3_cluster_devices = set(l3_cluster_devices)
                l3_cluster_servers = set(n for n in l3_cluster_devices if level(n) == 3)
                l3_cluster_routers = set(n for n in l3_cluster_devices if n in routers)

                l3_cluster_physical_graph = network.graph.subgraph(l3_cluster_routers)
                l3_cluster_eccentricities = nodes_by_eccentricity(l3_cluster_physical_graph)
# Cycle in event more servers to attach than routers
                l3_cluster_eccentricities = itertools.cycle(l3_cluster_eccentricities)
                for server in l3_cluster_servers:
                    attach_point = l3_cluster_eccentricities.next()
                    LOG.debug("Attaching %s to %s in %s" % (ank.label(server), 
                        ank.label(attach_point), asn))
                    network.add_link(server, attach_point)

                l1l2_devices = l3_cluster_devices - set(l3_cluster_servers)
# resort after set operations for groupby to work correctly
                l1l2_devices = sorted(l1l2_devices, key= get_l2_cluster)
                for l2_cluster, l2_cluster_devices in itertools.groupby(l1l2_devices, key = get_l2_cluster):
                    l2_cluster_devices = set(l2_cluster_devices)
                    l2_cluster_servers = set(n for n in l2_cluster_devices if level(n) == 2)
                    l2_cluster_routers = set(n for n in l2_cluster_devices if level(n) == 1 and n in routers)
                    l2_cluster_physical_graph = network.graph.subgraph(l2_cluster_routers)
                    l2_cluster_eccentricities = nodes_by_eccentricity(l2_cluster_physical_graph)
                    # Cycle in event more servers to attach than routers
                    l2_cluster_eccentricities = itertools.cycle(l2_cluster_eccentricities)
                    for server in l2_cluster_servers:
                        attach_point = l2_cluster_eccentricities.next()
                        LOG.debug("Attaching %s to %s in %s" % (ank.label(server), 
                            ank.label(attach_point), asn))
                        network.add_link(server, attach_point)


#TODO: authoritative might need to be a graph also
# setup domains

    for server in dns_servers(network):
        children = dns_auth_children(server)
        if len(children):
# does auth, set domain
            network.g_dns.node[server]['domain'] = "AS%s" % server.asn

# TODO: handle different levels
# in 3 level model, l3 servers advertise for AS
    for my_as in ank.get_as_graphs(network):
        devices = [ n for n in my_as]
        as_l3_servers = (n for n in my_as if level(n) == 3)
        edges = itertools.product(devices, as_l3_servers)
        dns_advertise_graph.add_edges_from(edges)

    network.g_dns = dns_graph
    network.g_dns_auth = dns_advertise_graph
示例#9
0
文件: bgp.py 项目: sk2/ank_legacy_v2
def configure_ibgp_rr(network):
    """Configures route-reflection properties based on work in (NEED CITE).

    Note: this currently needs ibgp_level to be set globally for route-reflection to work.
    Future work will implement on a per-AS basis.
    """
    LOG.debug("Configuring iBGP route reflectors")
    # Add all nodes from physical graph
    # TODO: if no
    network.g_session.add_nodes_from(network.graph)

    def level(u):
        return int(network.graph.node[u]["ibgp_level"])

    def format_asn(asn):
        """Returns unique format for asn, so don't confuse with property of the same,
        eg if ibgp_l2_cluster = 1 in as2, it could match as1 routers as 1==1
        so set asn_1 so 1 != asn_1"""
        return "asn_%s" % asn

    default_ibgp_level = 1

    # TODO: make "asn" eg "asn_1" as could conflict if asn=1 and ibgp_l2_cluster = 1 elsewhere and match the same
    for my_as in ank.get_as_graphs(network):
        # TODO: for neatness, look at redefining the above functions inside here setting my_as as network
        asn = my_as.name
        nodes_without_level_set = [n for n in my_as if not network.graph.node[n].get("ibgp_level")]
        if len(nodes_without_level_set):
            LOG.debug(
                "Setting default ibgp_level of %s for nodes %s"
                % (default_ibgp_level, ", ".join(str(n) for n in nodes_without_level_set))
            )
            for node in nodes_without_level_set:
                network.graph.node[node]["ibgp_level"] = default_ibgp_level

        max_ibgp_level = max(level(n) for n in my_as)
        LOG.debug("Max ibgp level for %s is %s" % (my_as.asn, max_ibgp_level))
        if max_ibgp_level >= 2:
            for node, data in my_as.nodes(data=True):
                if not data.get("ibgp_l2_cluster"):
                    # due to boolean evaluation will set in order from left to right
                    network.graph.node[node]["ibgp_l2_cluster"] = data.get("pop") or format_asn(asn)

                if max_ibgp_level == 3 and not data.get("ibgp_l3_cluster"):
                    # due to boolean evaluation will set in order from left to right
                    network.graph.node[node]["ibgp_l3_cluster"] = format_asn(asn)
        # Now connect
        edges_to_add = []
        # List of edges for easier iteration (rather than doing each time)
        as_edges = [(s, t) for s in my_as for t in my_as if s != t]
        if max_ibgp_level > 1:
            same_l2_cluster_edges = [
                (s, t)
                for (s, t) in as_edges
                if network.graph.node[s]["ibgp_l2_cluster"] == network.graph.node[t]["ibgp_l2_cluster"]
            ]
        if max_ibgp_level > 2:
            same_l3_cluster_edges = [
                (s, t)
                for (s, t) in as_edges
                if network.graph.node[s]["ibgp_l3_cluster"] == network.graph.node[t]["ibgp_l3_cluster"]
            ]

        if max_ibgp_level == 1:
            # 1           asn                 None
            edges_to_add += [(s, t, "peer") for (s, t) in as_edges]
        else:
            edges_to_add += [(s, t, "up") for (s, t) in same_l2_cluster_edges if level(s) == 1 and level(t) == 2]
            edges_to_add += [(s, t, "down") for (s, t) in same_l2_cluster_edges if level(s) == 2 and level(t) == 1]

        if max_ibgp_level == 2:
            edges_to_add += [(s, t, "peer") for (s, t) in as_edges if level(s) == level(t) == 2]
        elif max_ibgp_level == 3:
            edges_to_add += [(s, t, "peer") for (s, t) in same_l2_cluster_edges if level(s) == level(t) == 2]
            edges_to_add += [(s, t, "up") for (s, t) in same_l3_cluster_edges if level(s) == 2 and level(t) == 3]
            edges_to_add += [(s, t, "down") for (s, t) in same_l3_cluster_edges if level(s) == 3 and level(t) == 2]
            edges_to_add += [(s, t, "peer") for (s, t) in same_l3_cluster_edges if level(s) == level(t) == 3]

        # format into networkx format
        edges_to_add = [(s, t, {"rr_dir": rr_dir}) for (s, t, rr_dir) in edges_to_add]
        # LOG.debug("iBGP edges %s" % pprint.pformat(edges_to_add))
        LOG.debug("Adding iBGP edges")
        network.g_session.add_edges_from(edges_to_add)
        LOG.debug("Added iBGP edges")

    for node, data in network.graph.nodes(data=True):
        # is route_reflector if level > 1
        network.graph.node[node]["route_reflector"] = int(data.get("ibgp_level")) > 1
示例#10
0
def summarydoc(network):
    """ Plot the network """
    ank_main_dir = config.ank_main_dir

    html_template = lookup.get_template("autonetkit/summary_html.mako")
    ank_css_template = lookup.get_template("autonetkit/style_css.mako")
    
    ebgp_graph = ank.get_ebgp_graph(network)
    ibgp_graph = ank.get_ibgp_graph(network)

# Network wide stats
    network_stats = {}
    network_stats['device_count'] = len(list(network.devices()))
    network_stats['router_count'] = len(list(network.routers()))
    network_stats['server_count'] = len(list(network.servers()))
    network_stats['edge_count'] = network.graph.number_of_edges()
    as_graphs = ank.get_as_graphs(network)
    network_stats['as_count'] = len(as_graphs)

    as_stats = {}

    for my_as in as_graphs:
        #print single_as.nodes(data=True)
# Get ASN of first node
        asn = my_as.asn
        #print asn
        node_list = {}
        loopbacks = []
        virtual_nodes = {}
        for router in network.routers(asn):
            node_label = network.fqdn(router)
            loopbacks.append( (node_label, network.lo_ip(router).ip))
            node_list[node_label] = {}
            interface_list = []
            ibgp_list = []
            ebgp_list = []
            for _, dst, data in network.graph.edges(router, data=True):
                interface_list.append( (ank.fqdn(network, dst), data['sn']))
            node_list[node_label]['interface_list'] = interface_list
            for _, dst, data in ebgp_graph.edges(router, data=True):
                ebgp_list.append( (ank.fqdn(network, dst), network.lo_ip(dst).ip))
            node_list[node_label]['ebgp_list'] = ebgp_list
            for _, dst, data in ibgp_graph.edges(router, data=True):
                ibgp_list.append( (ank.fqdn(network, dst), network.lo_ip(dst).ip))
            node_list[node_label]['ibgp_list'] = ibgp_list

        for virtual_node in network.virtual_nodes(asn):
            links = []
            for link in network.links(virtual_node):
                links.append( (link.local_ip, link.remote_ip, link.dst))
            virtual_nodes[virtual_node] = {
                'links': links,
                }

        as_stats[my_as.name] = {
                'asn': asn,
                'loopbacks': loopbacks,
                'node_list': node_list,
                'virtual_nodes': virtual_nodes,
                }

    plot_dir = config.plot_dir
    if not os.path.isdir(plot_dir):
        os.mkdir(plot_dir)

    timestamp = time.strftime("%Y/%m/%d %H:%M:%S", time.localtime())
    
    css_filename = os.path.join(plot_dir, "ank_style.css")
    with open( css_filename, 'w') as f_css:
            f_css.write( ank_css_template.render())

    # put html file in main plot directory
    html_filename = os.path.join(plot_dir, "summary.html")
    #print html_filename
    with open( html_filename, 'w') as f_html:
            f_html.write( html_template.render(
                network_stats = network_stats,
                as_stats = as_stats,
                timestamp=timestamp,
                css_filename = "./ank_style.css",
                    )
                    )