Example #1
0
def graph_to_ank(network, graph, asn=None, include_ext_nodes=True):
    """ Converts a GML graph from the Zoo into AutoNetkit compatible graph"""
    # Default label
    if not 'Network' in graph.graph:
        graph.graph['Network'] = "Network"

    # if no asn set, use next available
    #TODO: put this into a function which returns a graph for combining at the
    # end
    LOG.debug("passed in asn of %s " % asn)
    if graph.is_multigraph():
        graph = nx.DiGraph(graph)
        if 'Network' in graph.graph:
            LOG.info(("Converting {0} to single-edge graph").format(
                graph.graph['Network']))
        else:
            LOG.info("Converting to single-edge graph")
    # ANK uses directed graphs
    graph = graph.to_directed()
    #TODO: see if ASN set in the graph
    asn_list = ank.nodes_by_as(network).keys()

    LOG.debug("current ASN list %s " % asn_list)

    # And append any ASNs manually specified
    manual_asn = {}
    for node, data in graph.nodes(data=True):
        if ('type' in data and data['type'].startswith("AS") and
            data['type'][2:].isdigit()):
            # This node has a valid manually specified ASN
            node_asn = int(data['type'][2:])
            manual_asn[node] = node_asn

    # Unique
    manual_asn_unique = list(set(manual_asn.keys()))
    for node_asn in manual_asn_unique:
        if node_asn in asn_list:
            LOG.warn("Manually specified ASN %i already in use" % asn)
    # Record these as in use
    asn_list += manual_asn_unique
    LOG.debug("asn list after adding manual uniques %s " % asn_list)

    # Allocate asns
    # Find next free asn
    def next_unallocated_asn():
        LOG.debug("nua fn asn list %s " % asn_list)
        if len(asn_list) > 0:
            nua = max(asn_list)
            while nua in asn_list:
                try:
                    nua += 1
                except TypeError as e:
                    LOG.warn("Unable to set Next Unallocated ASN %i to ASN list"
                             " %s " % (nua, asn_list))

                asn_list.append(nua)
                return nua
        else:
            # No asn in use
            nua = 1
            asn_list.append(nua)
            return nua

    #TODO: clean up this logic
    if asn and asn in asn_list:
        # User specified asn already in use
        LOG.warn("ASN %s already in use" % asn)
    elif asn:
        # Record as being used
        asn_list.append(int(asn))
        # Record name for DNS
        network.as_names[asn] = graph.graph['Network']
    else:
        # No asn set, use next available
        asn = next_unallocated_asn()
        # and record
        network.as_names[asn] = graph.graph['Network']

    #TODO: check that writing using names doesn't overwrite the  same folder
    # eg may have 5 AARNET nodes, but all overwrite same folder in nk lab
    # If multiple external nodes with same name, merge into single node
    # TODO: make this behaviour able to be turned on/off
    # eg either merge, keep, make unique, or remove, depending on how
    # external names combined
    #TODO: use include_external_nodes
    external_nodes = [node for node in graph.nodes()
                        if ('Internal' in
                            graph.node[node] and
                            graph.node[node]['Internal'] == 0)]
    # Group external nodes by their name
    ext_node_dict = defaultdict(list)
    for node in external_nodes:
        if not include_ext_nodes:
            graph.remove_node(node)
        else:
            label = graph.node[node]['label']
            ext_node_dict[label].append(node)
            # Now merge nodes
            for label, nodes in ext_node_dict.items():
                if len(nodes) > 1:
                    # Multiple nodes for this label, merge
                    # Choose the first (arbitary) node to merge others into
                    primary_node = nodes.pop()
                    for node in nodes:
                        # merge remaining nodes into primary node
                        # get edges from this node
                        #TODO: look at using edges_iter here
                        for src, dst, data in graph.edges(node,
                                                          data=True):
                            # add link from primary
                            graph.add_edge(primary_node, dst, data)
                            # and reverse link
                            graph.add_edge(dst, primary_node, data)
                            graph.remove_node(node)

    for node in graph:
        if node in manual_asn:
            # Node has ASN manually specified (as previously determined)
            graph.node[node]['asn'] = manual_asn[node]
        elif ('Internal' in graph.node[node]):
            if graph.node[node]['Internal'] == 1:
                graph.node[node]['asn'] = asn
            elif graph.node[node]['Internal'] == 0:
                graph.node[node]['asn'] = next_unallocated_asn()
        else:
            # No internal/external set, assume all internal nodes
            graph.node[node]['asn'] = asn

    # Check labels are unique
    # Store nodes by their label, duplicates are labels with more than one node
    nodes_by_label = defaultdict(list)
    all_labels = set()
    for node, data in graph.nodes_iter(data=True):
        nodes_by_label[data['label']].append(node)
        all_labels.add(data['label'])
    duplicates = ( (label, nodes) for (label, nodes) in nodes_by_label.items()
                  if len(nodes) > 1)
    for (label, nodes) in duplicates:
        if label == '':
            label = "Untitled"
        for index, node in enumerate(nodes):
            node_label = "%s_%s" % (label, index)
            if node_label in all_labels:
                # TODO: throw error
                print "Node label %s already used " % node_label
            else:
                all_labels.add(node_label)
                graph.node[node]['label'] = node_label
            #TODO: log to debug the changed label name

    #TODO: check no blank labels
    #TODO: put this into general algorithms module
       #TODO: replace with helper methods to return fqdn and folder names
    return graph