def make_connected(network): neighbors = convert_to_neighbors(network) clusters = _get_clusters_sets(neighbors) def get_unique_id(neighbors, i=0): if f'interconnect-{i}' not in neighbors: return f'interconnect-{i}' else: return get_unique_id(neighbors, i + 1) def get_center_node(neighbors, cluster): max_neighbors = 0 center_node = None for sid, neighs in neighbors.items(): if sid in cluster and len(neighs) >= max_neighbors: max_neighbors = len(neighs) center_node = sid return center_node if len(clusters) > 1: central = get_unique_id(neighbors) # connect all clusters via central node for cluster in clusters: center = get_center_node(neighbors, cluster) network['links'].append({ 'source': central, 'target': center, 'type': 'vpn' })
def get_paths_to_gateways(network, gateways): nodes = list(convert_to_neighbors(network).keys()) dijkstra = Dijkstra(network) paths = [] # remove gateways from nodes list for gateway in gateways: nodes.remove(gateway) for node in nodes: distance_min = math.inf gateway_min = None for gateway in gateways: d = dijkstra.find_shortest_distance(gateway, node) if distance_min == math.inf or d <= distance_min: distance_min = d gateway_min = gateway if gateway_min is not None: paths.append((node, gateway)) return paths
def _get_remote_mapping(cur_state, new_state, remotes, cur_state_rmap): def partition_into_subgraph_nodes(neighbor_map, nodes, rmap, remotes): random.shuffle(nodes) # remote_id => [<node_ids>] partitions = {} # keep running nodes on the same partition for node_id, remote in rmap.items(): remote_id = remotes.index(remote) partitions.setdefault(remote_id, []).append(node_id) if node_id not in nodes: eprint(f'Node {node_id} not in previous state!') stop_all_terminals() exit(1) nodes.remove(node_id) for remote_id in range(len(remotes)): if len(nodes) == 0: break if remote_id not in partitions: partitions[remote_id] = [nodes.pop()] # find neighbor node of cluster def grow_cluster(cluster, nodes): for node in nodes: for cluster_node in cluster: if node in neighbor_map[cluster_node]: cluster.append(node) nodes.remove(node) return # cannot extend cluster via neighbor => use left over node cluster.append(nodes.pop()) while len(nodes) > 0: # get smallest cluster (remote) key key = min(partitions.keys(), key=lambda k: len(partitions[k])) grow_cluster(partitions[key], nodes) return partitions # get node distribution balance def get_variance(partition): median = 0 for remote_id, cluster in partition.items(): median += len(cluster) median /= len(partition) q = 0 for remote_id, cluster in partition.items(): q = (len(cluster) - median)**2 return math.sqrt(q / len(partition)) def partition_to_map(partition, remotes): node_to_remote_map = {} for remote_id, node_ids in partition.items(): for node_id in node_ids: node_to_remote_map[node_id] = remotes[remote_id] return node_to_remote_map ''' # debug output def debug_partition(partition, remotes): print('partitioning:') for remote_id, cluster in partition.items(): print(' {}: {} nodes'.format(remotes[remote_id].get('address', 'local'), len(cluster))) interconnects = 0 node_to_remote_map = partition_to_map(partition, remotes) for link in new_state.get('links', []): if node_to_remote_map[str(link['source'])] is not node_to_remote_map[str(link['target'])]: interconnects += 1 print(f' l2tp links: {interconnects}') ''' # try multiple random (connected) partitions and select the best neighbor_map = convert_to_neighbors(cur_state, new_state) tries = 20 lowest_variance = math.inf best_partition = [] # shortcut: if no mapping on multiple remotes is needed if len(remotes) == 1 and len(cur_state_rmap) == 0: return partition_to_map({0: neighbor_map.keys()}, remotes) for _ in range(tries): partition = partition_into_subgraph_nodes(neighbor_map, list(neighbor_map.keys()), cur_state_rmap, remotes) if partition: variance = get_variance(partition) if variance < lowest_variance: lowest_variance = variance best_partition = partition if len(best_partition) == 0: eprint('No network partition found.') stop_all_terminals() exit(1) #if verbosity in ['verbose', 'normal']: # debug_partition(best_partition, remotes) # node_id => remote return partition_to_map(best_partition, remotes)
def __init__(self, network): self.dists_cache = {} self.prevs_cache = {} self.nodes = convert_to_neighbors(network)
def get_random_nodes(network, count): nodes = list(convert_to_neighbors(network).keys()) return random.sample(nodes, count)
def get_random_paths(network=None, count=10, seed=None): nodes = list(convert_to_neighbors(network).keys()) return _get_random_paths(nodes=nodes, count=count, seed=seed)