Exemplo n.º 1
0
def count_local_interference(graphname, connectivity_graph, channel_group,
                             wlan_modules):
    """ Counts which channels interfere for a given channel-group

    Keyword arguments:
    graphname --
    connectivity_graph -- A NetworkX Graph which represents the basic connectivity
    channel_group -- A List of tuples which represent links in a channel-group
    wlan_modules -- List of nodenames of graphname which are modules, the rest (all nodes - modules) are device nodes

    Requires the module-nodes of graphname to have the following attributes:
    "seen_channels"  which is a list of foreign wlans like [1,6,11,11,36]
    "channel" which is None if no channel is assigned to that node or the channel which has been assigned to that connection

    Returns two collections_enhanced.Counter with the number of internal interference counts for each channel and external interference
    """

    internal_channel_counter = collections_enhanced.Counter()
    external_channel_counter = collections_enhanced.Counter()
    modules = set()

    for (module_a, module_b) in channel_group:
        modules.add(module_a)
        modules.add(module_b)

    logger.debug("  Interferences for channel-group: " + str(channel_group))
    logger.debug("      Modules are: " + str(modules))

    for module in modules:

        neighbors = connectivity_graph.neighbors(module)

        for neighbor in neighbors:

            logger.debug("      Neigh for module: " + str(module) + ": " +
                         str(neighbor))

            if neighbor in wlan_modules:

                # Internal Interference
                channel = graphname.node[neighbor]["channel"]
                if channel:
                    internal_channel_counter[channel] += 1
                    logger.debug("          For " + str(module) + " : " +
                                 str(neighbor) + " @Channel:" + str(channel))
                else:
                    logger.debug("          Ignoring neigh: " + str(neighbor) +
                                 "(no channel)")

            # External Interference
            for external_channel in graphname.node[module]["seen_channels"]:
                external_channel_counter[external_channel] += 1

    return internal_channel_counter, external_channel_counter
Exemplo n.º 2
0
def get_basic_graph_from_wlc(hostname, username, password,
                             assignable_channels):
    """Get data from the WLC and return a networkx basic connectivity graph"""
    logger.info("Getting Data from WLC...")

    # First check if we get a connection to the wlc
    if not os.system("ping -c 1 " + hostname + " > /dev/null") == 0:
        print(hostname + " is down!")
        exit(1)

    # Create the dict of dicts for scan results
    # Example entry: scan_results[<index>][<name>]["lan_mac"] = <value>
    #                scan_results[<index>][<name>]["channel"] = <value>
    #                ...
    intra_wlan_discovery = dict()
    intra_wlan_discovery_index = 0
    for line in get_table_data("/Status/WLAN-Management/Intra-WLAN-Discovery",
                               hostname, username, password):
        entry_dict = dict()
        entry_dict["source_mac"] = line[0]
        entry_dict["wlan_dest_mac"] = line[1]
        entry_dict["channel"] = line[2]
        entry_dict["signal_strength"] = line[3]
        entry_dict["noise_level"] = line[4]
        entry_dict["age"] = line[5]
        intra_wlan_discovery[intra_wlan_discovery_index] = entry_dict
        intra_wlan_discovery_index += 1

    # First create the interfaces-graph
    # That means we create a node for each WLAN-interface of a node
    # and connect each of those of a node with a link with quality 0(best)(so the MST takes this edge always)
    # Therefore we use the Status/WLAN-Management/AP-Status/Active-Radios/ Table of the WLC
    # This gives us the interfaces of each node (identified by the MACs (LAN/WLAN)
    # Create the dict of dicts for active radios
    # Example entry: active_radios[<index>][<name>]["lan_mac"] = <value>
    #                active_radios[<index>][<name>]["bssid_mac"] = <value>
    #                ...
    active_wlan_interfaces = set()
    global active_radios
    active_radios = dict()
    active_radios_index = 0
    for line in get_table_data(
            "/Status/WLAN-Management/AP-Status/Active-Radios", hostname,
            username, password):
        entry_dict = dict()
        entry_dict["lan_mac"] = line[0]
        entry_dict["ifc"] = line[1]
        entry_dict["ip"] = line[2]
        entry_dict["name"] = line[3]
        if not line[3]:
            logger.error("Name for AP: " + str(line[0]) + " not set!")
            exit(1)
        entry_dict["location"] = line[4]
        entry_dict["wlan_mac"] = line[5]
        entry_dict["radio_band"] = line[6]
        entry_dict["channel"] = line[7]
        entry_dict["modem_load_min"] = line[8]
        entry_dict["modem_load_max"] = line[9]
        entry_dict["modem_load_avg"] = line[10]
        entry_dict["client_count"] = line[11]
        entry_dict["background_scan"] = line[12]
        entry_dict["card_id"] = line[13]
        entry_dict["fw_version"] = line[14]
        entry_dict["card_serial_nr"] = line[15]
        entry_dict["operating"] = line[16]
        entry_dict["transmit_power"] = line[17]
        entry_dict["eirp"] = line[18]
        entry_dict["exc_eirp"] = line[19]
        entry_dict["internal"] = line[20]
        entry_dict["nr_radios"] = line[21]
        entry_dict["module"] = line[22]
        entry_dict["serial_nr"] = line[23]
        entry_dict["version"] = line[24]
        entry_dict["card_state"] = line[25]
        entry_dict["field_optimization"] = line[26]
        entry_dict["ap-connections"] = line[27]
        entry_dict["groups"] = line[28]
        active_wlan_interfaces.add(line[5])
        active_radios[active_radios_index] = entry_dict
        active_radios_index += 1

    # Todo: Foreign table/ Seen-channels, but christoph has to implement this table first (probably wont happen anytime soon)
    # Separate our connections from foreigners
    #our_connections = dict()
    #foreign_connections = dict()
    #for index in scan_results.keys():
    #    if scan_results[index]["seen_bssid"] in active_radios_ap_bssid_mac:
    #        our_connections[index] = scan_results[index]
    #    else:
    #        foreign_connections[index] = scan_results[index]

    # Safety check
    if len(intra_wlan_discovery) == 0:
        logger.error(
            "Our connection list is empty. The APs don't see each other")
        exit(1)

    # Todo: Foreign table/ Seen-channels, but christoph has to implement this table first (probably wont happen anytime soon)
    #if len(foreign_connections) == 0:
    #    logger.warning("Foreign connection list is empty. The APs don't show foreign networks.")
    #    logger.warning("         This is unlikely, except there are really no other wireless lan networks around")

    # Remove the inactive connections from scan results
    # Only consider those connections, which have a corresponding partner in the active radios table, since only those connections are really active
    for index in intra_wlan_discovery.keys():
        if not intra_wlan_discovery[index][
                "source_mac"] in active_wlan_interfaces or not intra_wlan_discovery[
                    index]["wlan_dest_mac"] in active_wlan_interfaces:
            logger.info(
                "Ignoring link {0},{1}, since at least one is not in active Radios."
                .format(str(intra_wlan_discovery[index]["source_mac"]),
                        str(intra_wlan_discovery[index]["wlan_dest_mac"])))
            del intra_wlan_discovery[index]

    # Create set of nodes, which are modules
    wlan_modules = set()
    for index in active_radios.keys():
        wlan_modules.add(active_radios[index]["wlan_mac"])

    # Create set of nodes, which are not modules (=>actual devices)
    lan_nodes = set()
    for index in active_radios.keys():
        lan_nodes.add(active_radios[index]["lan_mac"])

    basic_graph = nx.DiGraph()

    #
    # Generate the basic graph from data
    #
    # Set default values for nodes
    for node in lan_nodes:
        # Add the node
        basic_graph.add_node(node)

        # Initialize used channels with 0
        used_channels = collections_enhanced.Counter()
        for channel in assignable_channels:
            used_channels[channel] = 0
        basic_graph.node[node]["used-channels"] = used_channels

        # Set number of modules initially to 0
        basic_graph.node[node]["modules"] = 0

    # Set default values for modules
    for module in wlan_modules:

        # Add the module-node
        basic_graph.add_node(module)

        # Set the default channel
        basic_graph.node[module]["channel"] = None

        # Initialize actually seen counters for wlan modules with 0
        actually_seen_channels = collections_enhanced.Counter()
        for channel in assignable_channels:
            actually_seen_channels[channel] = 0
        basic_graph.node[module]["seen_channels"] = actually_seen_channels

        # Initialize nr of modules for wlan module
        basic_graph.node[module]["modules"] = 1

    # Fill/Add node-module edges with data from wlc
    for index in active_radios.keys():
        lan_mac = active_radios[index]["lan_mac"]
        name = active_radios[index]["name"]
        wlan_mac = active_radios[index]["wlan_mac"]

        # Add the edge
        basic_graph.add_edge(lan_mac, wlan_mac)

        # Module-edge data
        basic_graph.edge[lan_mac][wlan_mac]["real-connection"] = False

        # Write all data also into graph
        basic_graph.node[wlan_mac]["module-of"] = lan_mac
        basic_graph.node[wlan_mac]["module-of-name"] = name
        for key in active_radios[index].keys():
            basic_graph.edge[lan_mac][wlan_mac][key] = active_radios[index][
                key]

        # Also count here the number of modules each node has
        basic_graph.node[lan_mac]["modules"] += 1

    # Add all possible module-module links
    for index in intra_wlan_discovery.keys():
        source_mac = intra_wlan_discovery[index]["source_mac"]
        wlan_dest_mac = intra_wlan_discovery[index]["wlan_dest_mac"]
        #channel = autowds_topology_scan_results[index]["channel"]
        signal_strength = int(intra_wlan_discovery[index]["signal_strength"])
        #noise_level = int(autowds_topology_scan_results[index]["noise_level"])
        #age = autowds_topology_scan_results[index]["age"]

        basic_graph.add_edge(source_mac, wlan_dest_mac)

        # Set the score of this edge
        # The following is done, since Alfred Arnold mentioned that the Signal-strength value in LCOS is already the SNR
        snr = signal_strength
        basic_graph.edge[source_mac][wlan_dest_mac]["snr"] = snr

        # Initialize each edge with empty channel
        basic_graph.edge[source_mac][wlan_dest_mac]["channel"] = None
        basic_graph.edge[source_mac][wlan_dest_mac]["real-connection"] = True

    # Todo: Foreign table/ Seen-channels, but christoph has to implement this table first (probably wont happen anytime soon)
    # Fill interference list
    #for index in foreign_connections.keys():
    #    channel = foreign_connections[index]["channel"]
    #    wlan_mac = foreign_connections[index]["wlan_mac"]
    #    # Add to interference list of this wlan module-node
    #    # Only consider the channels we have in Assignable channels, since the others are useless
    #    if channel in assignable_channels:
    #        basic_graph.node[wlan_mac]["seen_channels"][channel] += 1

    return basic_graph, wlan_modules, lan_nodes
Exemplo n.º 3
0
def calculate_ca(graphname, basic_con_graph, allowed_channel_list):
    """ Assigns channel fro the allowed_channel_list to the edges of the graphname graph

    Keyword arguments:
    graphname -- undirected weighted NetworkX graph
    basic_con_graph -- undirected NetworkX graph - the underlying connectivity graph
    returns a colored/channel assigned networkx graph
    """

    logger.info("Calculating channel assignment for graph...")

    wlan_modules = get_modules_of_graph(basic_con_graph)

    overall_channel_counter = collections_enhanced.Counter()
    for channel in allowed_channel_list:
        overall_channel_counter[channel] = 0

    # Initialize the edge- and node-channels
    for edge in graphname.edges():
        if is_real_edge(graphname, edge[0], edge[1]):
            graphname.edge[edge[0]][edge[1]]["channel"] = None
    for node in graphname.nodes():
        if node in wlan_modules:
            graphname.node[node]["channel"] = None
            graphname.node[node]["seen_channels"] = []

    # Iterate over all Edges
    for edge in graphname.edges():

        # Only assign channel to real connections
        if is_fake_edge(graphname, edge[0], edge[1]):
            continue

        # Skip this channel group if it already has a channel assigned
        if has_channel_assigned(graphname, edge[0], edge[1]):
            continue

        # Get the channel group for this edge
        channel_group = get_connected_channels_for_edge(
            graphname, edge[0], edge[1], wlan_modules)

        logger.debug("Coloring Channel-Group: " + str(channel_group))

        # For each module in channel group, count the channel usages
        internal_interference, external_interference = count_local_interference(
            graphname, basic_con_graph, channel_group, wlan_modules)
        logger.debug("  Internal-Interference: " + str(internal_interference))
        logger.debug("  External-Interference: " + str(external_interference))

        election_counter = collections_enhanced.Counter()
        for channel in allowed_channel_list:
            election_counter[channel] = 0

        # Respect internal interference
        for channel in internal_interference:
            if channel in election_counter:
                election_counter[channel] += internal_interference[channel]

        # Respect external interference
        for channel in external_interference:
            if channel in election_counter:
                election_counter[channel] += external_interference[channel]

        logger.debug("  Election-Counter: " + str(election_counter))

        # Select the channel that would cause the least local interference
        best_channels = election_counter.least_common_all().keys()
        if len(best_channels) == 1:
            best_channel = best_channels[0]
        else:

            # Tie occurred, which channel has been overall used the least?
            second_election = collections_enhanced.Counter()
            for channel in best_channels:
                second_election[channel] = overall_channel_counter[channel]

            logger.debug("  Overall-Channel-Counter: " +
                         str(overall_channel_counter))
            logger.debug("  Second-Election-Counter because of tie: " +
                         str(second_election))

            best_channels = second_election.least_common_all().keys()
            if len(best_channels) == 1:
                best_channel = best_channels[0]
            else:

                logger.debug("  Random-Pick because of tie")

                best_channel = random.choice(best_channels)

        logger.debug("  Using channel " + str(best_channel) +
                     " for channel-group")

        # Assign the best channel to the channel group
        assign_channel_to_channel_group(best_channel, channel_group, graphname,
                                        overall_channel_counter)

    return graphname
Exemplo n.º 4
0
def calculate_survival_links(mst_original, basic_con_graph):
    """ Finds the best backup links for a given mst graph

    Keyword arguments:
    mst -- undirected weighted NetworkX Maximal spanning tree we created in step 1
    basic_con_graph -- undirected NetworkX graph - the underlying connectivity graph
    returns a 2-edge-connected MST NetworkX graph
    """

    logger.info("Calculating Survival links for Graph...")

    wlan_modules = get_modules_of_graph(basic_con_graph)

    mst = copy.deepcopy(mst_original)

    mst_edges = copy.copy(mst.edges())
    for edge in mst_edges:

        # Simulate each edge failing
        if edge[0] in wlan_modules and edge[1] in wlan_modules:

            mst.remove_edge(edge[0], edge[1])
            # Check if there still is a path to get to edge[1] even if this edge has failed
            # If we have still a path to edge[1], then we are done
            # else select second highest rated edge to get there
            if not nx.has_path(mst, edge[0], edge[1]):
                logger.debug("Edge " + str(edge) +
                             " has no backup, searching one")
                edge_list = collections_enhanced.Counter()

                connected_components = nx.connected_components(mst)
                if len(connected_components) != 2:
                    logger.error("Could not split graph into two groups")
                    return 1

                # Create Group A and B
                if edge[0] in connected_components[0]:
                    group0 = connected_components[0]
                    group1 = connected_components[1]
                else:
                    group0 = connected_components[1]
                    group1 = connected_components[0]

                # Find edges connecting Group A and B
                connecting_edges = list()
                for cedge in basic_con_graph.edges():
                    if cedge[0] in group0 and cedge[1] in group1 or cedge[
                            0] in group1 and cedge[1] in group0:
                        if not ((cedge[0] == edge[0] and cedge[1] == edge[1])
                                or
                                (cedge[1] == edge[0] and cedge[0] == edge[1])):
                            connecting_edges.append(cedge)

                # Calculate scores for those survival edges
                for con_edge in connecting_edges:
                    edge_list[(con_edge[0],
                               con_edge[1])] = calculate_score_for_edge(
                                   mst, basic_con_graph, con_edge[0],
                                   con_edge[1], wlan_modules)

                # If there is no connection that reconnects the two groups, then we can't do anything about it
                if len(connecting_edges) == 0:
                    logger.warning("Could not find backup for edge " +
                                   str(edge))
                    continue

                # Add edges with higest score that connects those two groups to the graph
                (bestedge_node_a,
                 bestedge_node_b), highest_score = edge_list.most_common(1)[0]

                logger.debug("The backup for edge " + str(edge) + " is: ('" +
                             str(bestedge_node_a) + "', '" +
                             str(bestedge_node_b) + "')")

                mst.add_edge(bestedge_node_a, bestedge_node_b)
                mst.edge[bestedge_node_a][bestedge_node_b][
                    "backup-link"] = True
                for key in basic_con_graph.edge[bestedge_node_a][
                        bestedge_node_b].keys():
                    mst.edge[bestedge_node_a][bestedge_node_b][
                        key] = basic_con_graph.edge[bestedge_node_a][
                            bestedge_node_b][key]
            else:
                logger.debug("Edge " + str(edge) +
                             " already has backup, moving to next edge")

            # Add the removed edge again
            mst.add_edge(edge[0], edge[1])
            for key in basic_con_graph.edge[edge[0]][edge[1]].keys():
                mst.edge[edge[0]][edge[1]][key] = basic_con_graph.edge[
                    edge[0]][edge[1]][key]

    return mst
Exemplo n.º 5
0
def calculate_st(graphname):
    """ Find the maximal spanning tree

    Keyword arguments:
    graphname -- undirected NetworkX Graph where
                    edges have attributes:
                        "snr" - Integer which indicates the Signal to Noise ratio for this edge.
                    nodes have attributes
                        "isModule" - True if node is a Module or False if not
    returns an undirected MST NetworkX Graph
    """

    logger.info("Calculating MST on Graph...")

    wlan_modules = get_modules_of_graph(graphname)

    mst = nx.Graph()
    visited_nodes = set()
    edge_list = collections_enhanced.Counter(
    )  # Edge list contains triple with (source,target,edge-weigth)
    all_nodes = set()

    # Copy nodes from graphname to mst and fill the set: all_nodes
    for node in graphname.nodes():
        all_nodes.add(node)
        mst.add_node(node)
        for key in graphname.node[node].keys():
            mst.node[node][key] = graphname.node[node][key]

    # Devices are all those nodes which are not modules
    not_wlan_modules = [
        node for node in graphname.nodes() if node not in wlan_modules
    ]

    # Select random Device to start with
    start_node = random.choice(list(not_wlan_modules))

    # Add the edges originating from start node to edge_list
    visited_nodes.add(start_node)
    for neighbor in graphname.neighbors(start_node):
        edge_list[(start_node, neighbor)] = calculate_score_for_edge(
            mst, graphname, start_node, neighbor, wlan_modules)

    # Main loop
    while True:
        # Remove all edges which do not see new nodes (keep only productive edges)
        removed_edge = True
        while removed_edge:
            removed_edge = False
            for (a, b) in edge_list:
                if a in visited_nodes and b in visited_nodes:
                    removed_edge = True
                    del edge_list[(a, b)]
                    break

        # If the edge_list is empty after removing unproductive edges, check if we visited all nodes
        if len(edge_list) == 0:
            if len(visited_nodes) != graphname.number_of_nodes():
                logger.error("Could not connect all nodes.")
                logger.error(
                    "Graph / APs are separated, maybe wait some time until they can at least theoretically span a network."
                )
                exit(1)
            else:
                break

        # Find the best new edge and add it to the mst
        # Therefore just take the edge with the highest score
        # If there is a tie, select the edge with the best snr
        bestedges = edge_list.most_common_all()
        if len(bestedges) > 1:
            snrscore = collections_enhanced.Counter()
            for edge in bestedges:
                snrscore[edge] = graphname.edge[edge[0]][edge[1]]["snr"]
            bestedge = snrscore.most_common(1)[0]
            bestedge_node_a = bestedge[0][0]
            bestedge_node_b = bestedge[0][1]
            highest_score = bestedge[1]
        else:
            (bestedge_node_a,
             bestedge_node_b), highest_score = edge_list.most_common(1)[0]

        # Mark node as visited
        visited_nodes.add(bestedge_node_b)

        # Add the edge to mst
        mst.add_edge(bestedge_node_a, bestedge_node_b)
        for key in graphname.edge[bestedge_node_a][bestedge_node_b].keys():
            mst.edge[bestedge_node_a][bestedge_node_b][key] = graphname.edge[
                bestedge_node_a][bestedge_node_b][key]

        # Add its edges to the edge_list
        for neighbor in graphname.neighbors(bestedge_node_b):
            if neighbor not in visited_nodes:
                edge_list[(bestedge_node_b,
                           neighbor)] = calculate_score_for_edge(
                               mst, graphname, bestedge_node_b, neighbor,
                               wlan_modules)

        # Remove the edge from edgelist, to speed up things since we dont need it any longer (since we are using it)
        del edge_list[(bestedge_node_a, bestedge_node_b)]

        # Update the scores, because scores might change, since we added new edge => score of others could get decreased
        # Todo: this could be made more efficient, by just updating those edges, where sth has changed instead of all, find out if this would be a performance killer first
        for (a, b) in edge_list:
            edge_list[(a,
                       b)] = calculate_score_for_edge(mst, graphname, a, b,
                                                      wlan_modules)

    return mst