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