Esempio n. 1
0
    def get_dependent_edges(self, src_nodes, dst_node):
        # first, find all nodes that are only connected to two neighbors - any path coming from one neighbor goes to
        # the other one and means that it could also be modeled by a direct edge between the two neighbors
        candidate_nodes = list()
        for node in self.nodes:
            if node not in src_nodes and node != dst_node and self.in_degree(node) == 2 and self.out_degree(node) == 2:
                candidate_nodes.append(node)

        dependent_edges = list()

        while candidate_nodes:
            curr_nodes = [candidate_nodes.pop()]
            curr_edges = set()
            while curr_nodes:
                node = curr_nodes.pop()
                neighbors = self.neighbors(node)

                for neighbor in neighbors:
                    curr_edges.add(Link.get_name(neighbor, node))

                    if neighbor in candidate_nodes:
                        candidate_nodes.remove(neighbor)
                        curr_nodes.append(neighbor)

            dependent_edges.append(list(curr_edges))
        return dependent_edges
Esempio n. 2
0
    def get_query(sources):
        sources = [PolicySource(src_router) for src_router in sources]
        destinations = PolicyDestination("r12", "FastEthernet1/1", "11.0.12.0/24")
        num_paths = 3
        links = [Link("l{id}".format(id=i), edge, LinkState.UP) for i, edge in enumerate(ResponseTest.get_all_edges())]
        netenv = NetworkEnvironment(links)

        return Query(PolicyType.LoadBalancingSimple, sources, destinations, num_paths, netenv, negate=False)
Esempio n. 3
0
 def __str__(self):
     output = '%d nodes, %d edges:\n' % (len(self.nodes), len(self.edges))
     for edge in sorted(self.edges, key=lambda e: e[0]):
         state = self.edges[edge]['state']
         output += '{edge}: {state}'.format(edge=Link.get_name(edge[0], edge[1]),
                                            state='up' if state == LinkState.UP else 'down')
         if 'cost' in self.edges[edge]:
             cost = self.edges[edge]['cost']
             output += ' with cost %d' % (cost, )
         output += '\n'
     return output
Esempio n. 4
0
    def parse_generic_counterexample(message):
        assert message.startswith("Counterexample")

        failed_links = set()
        source_routers = set()

        edges = re.findall('link\((.+?),(.+?)\)', message)
        for router1, router2 in edges:
            failed_links.add(Link.get_name(router1, router2))

        return failed_links, source_routers
Esempio n. 5
0
    def get_multi_flow_counter_example():
        query = ResponseTest.get_query(sources=["r15", "r6"])

        response = ("Flow: ingress:r15 vrf:default 0.0.0.0->11.12.7.2 HOPOPT packetLength:0 state:NEW\n"
                    "  environment:BASE\n"
                    "Environment{testrigName=tempSnapshot, edgeBlacklist=[<r11:FastEthernet0/1, r16:FastEthernet1/1>, "
                    "<r12:FastEthernet0/0, r13:FastEthernet1/0>, <r16:FastEthernet0/0, r17:FastEthernet1/0>, "
                    "<r16:FastEthernet0/1, r21:FastEthernet1/1>, <r17:FastEthernet0/1, r22:FastEthernet1/1>, "
                    "<r18:FastEthernet0/1, r23:FastEthernet1/1>, <r19:FastEthernet0/0, r20:FastEthernet1/0>, "
                    "<r2:FastEthernet0/0, r3:FastEthernet1/0>, <r2:FastEthernet1/0, r1:FastEthernet0/0>, "
                    "<r21:FastEthernet0/0, r22:FastEthernet1/0>, <r23:FastEthernet0/0, r24:FastEthernet1/0>, "
                    "<r23:FastEthernet1/0, r22:FastEthernet0/0>, <r25:FastEthernet1/0, r24:FastEthernet0/0>, "
                    "<r3:FastEthernet0/0, r4:FastEthernet1/0>, <r4:FastEthernet0/1, r9:FastEthernet1/1>, "
                    "<r6:FastEthernet0/0, r7:FastEthernet1/0>, <r6:FastEthernet1/1, r1:FastEthernet0/1>, "
                    "<r9:FastEthernet0/1, r14:FastEthernet1/1>], interfaceBlacklist=null, nodeBlacklist=null, "
                    "bgpTables=null, routingTables=null, externalBgpAnnouncements=[]}\n"
                    "    Hop 1: r15:FastEthernet1/1 -> r10:FastEthernet0/1\n"
                    "    Hop 2: r10:FastEthernet1/0 -> r9:FastEthernet0/0\n"
                    "    Hop 3: r9:FastEthernet1/0 -> r8:FastEthernet0/0\n"
                    "    Hop 4: r8:FastEthernet1/0 -> r7:FastEthernet0/0\n"
                    "    Hop 5: r7:FastEthernet0/1 -> r12:FastEthernet1/1\n"
                    "    ACCEPTED\n\n"
                    "Flow: ingress:r6 vrf:default 0.0.0.0->11.12.7.2 HOPOPT packetLength:0 state:NEW\n"
                    "  environment:BASE\n"
                    "Environment{testrigName=tempSnapshotId, edgeBlacklist=["
                    "<r11:FastEthernet0/1, r16:FastEthernet1/1>, <r12:FastEthernet0/0, r13:FastEthernet1/0>, "
                    "<r16:FastEthernet0/0, r17:FastEthernet1/0>, <r16:FastEthernet0/1, r21:FastEthernet1/1>, "
                    "<r17:FastEthernet0/1, r22:FastEthernet1/1>, <r18:FastEthernet0/1, r23:FastEthernet1/1>, "
                    "<r19:FastEthernet0/0, r20:FastEthernet1/0>, <r2:FastEthernet0/0, r3:FastEthernet1/0>, "
                    "<r2:FastEthernet1/0, r1:FastEthernet0/0>, <r21:FastEthernet0/0, r22:FastEthernet1/0>, "
                    "<r23:FastEthernet0/0, r24:FastEthernet1/0>, <r23:FastEthernet1/0, r22:FastEthernet0/0>, "
                    "<r25:FastEthernet1/0, r24:FastEthernet0/0>, <r3:FastEthernet0/0, r4:FastEthernet1/0>, "
                    "<r4:FastEthernet0/1, r9:FastEthernet1/1>, <r6:FastEthernet0/0, r7:FastEthernet1/0>, "
                    "<r6:FastEthernet1/1, r1:FastEthernet0/1>, <r9:FastEthernet0/1, r14:FastEthernet1/1>], "
                    "interfaceBlacklist=null, nodeBlacklist=null, "
                    "bgpTables=null, routingTables=null, externalBgpAnnouncements=[]}\n"
                    "    Hop 1: r6:FastEthernet0/1 -> r11:FastEthernet1/1\n"
                    "    Hop 2: r11:FastEthernet0/0 -> r12:FastEthernet1/0\n"
                    "    ACCEPTED")

        failed_edges = [("r12", "r13"), ("r16", "r17"), ("r16", "r21"), ("r17", "r22"), ("r18", "r23"),
                        ("r19", "r20"), ("r2", "r3"), ("r2", "r1"), ("r21", "r22"), ("r23", "r24"),
                        ("r23", "r22"), ("r25", "r24"), ("r3", "r4"), ("r4", "r9"), ("r6", "r7"),
                        ("r6", "r1"), ("r9", "r14"), ("r16", "r11"), ]
        failed_links = set([Link.get_name(r1, r2) for r1, r2 in failed_edges])

        src_routers = {"r15", "r6"}

        return query, response, failed_links, src_routers
Esempio n. 6
0
    def get_random_links():
        links = list()
        symbolic_link_names = list()
        for i in range(1, 15):
            r1 = "r{id}".format(id=i)
            r2 = "r{id}".format(id=i * 9)
            state = random.choice(
                [LinkState.UP, LinkState.DOWN, LinkState.SYMBOLIC])
            link = Link("l{id}".format(id=i), (r1, r2), state=state)
            links.append(link)

            if state == LinkState.SYMBOLIC:
                symbolic_link_names.append(link.name)

        max_failures = random.randint(0, len(symbolic_link_names))

        return links, symbolic_link_names, max_failures
Esempio n. 7
0
def build_network(backend, scenario_path, max_failures, waypoints_min, waypoints_fraction):
    topology_files = backend.get_topology()
    network = BackendTopologyBuilder.build_topology(topology_files, scenario_path)

    # get waypoints
    all_routers = network.nodes()
    num_waypoints = max(waypoints_min, int(len(all_routers) / waypoints_fraction))
    waypoints = random.sample(all_routers, num_waypoints)

    links = list()
    all_edges = network.get_undirected_edges()
    for i, edge in enumerate(all_edges):
        links.append(Link("l{id}".format(id=i), edge, LinkState.SYMBOLIC))

    # create the network environment
    netenv = NetworkEnvironment(links, k_failures=max_failures)

    return network, netenv, waypoints
Esempio n. 8
0
    def parse_flow_counterexample(message):
        assert message.startswith("Flow:")

        failed_links = set()
        source_routers = set()

        first = True
        for item in re.split('\n\n', message):
            ingress = re.search('ingress:(.+?) vrf:', item)
            if ingress and ingress.group(1):
                source_routers.add(ingress.group(1))

                if first:
                    first = True
                    tmp_blacklist = re.search('edgeBlacklist=\[(.*?)\]', item)
                    if tmp_blacklist:
                        blacklist = tmp_blacklist.group(1)
                        edges = re.findall('<(.+?):(.+?),\s(.+?):(.+?)>', blacklist)

                        for router1, intf1, router2, intf2 in edges:
                            failed_links.add(Link.get_name(router1, router2))

        return failed_links, source_routers
Esempio n. 9
0
 def test_get_dependent_edges(self):
     router_pairs = [
         ("r1", "r2",
          [[Link.get_name("r1", "r4"),
            Link.get_name("r3", "r4")]]),
         ("r1", "r3",
          [[Link.get_name("r1", "r4"),
            Link.get_name("r3", "r4")],
           [Link.get_name("r1", "r2"),
            Link.get_name("r2", "r3")]]),
         ("r2", "r4", []),
     ]
     for r1, r2, true_dep_edges in router_pairs:
         tmp_dependent_edges = self.topology.get_dependent_edges([r1], r2)
         dependent_edges = [
             sorted(dep_edges) for dep_edges in tmp_dependent_edges
         ]
         self.assertCountEqual(true_dep_edges,
                               dependent_edges,
                               msg="dependent edges from {} to {}".format(
                                   r1, r2))
Esempio n. 10
0
 def get_links(self):
     if not self.links:
         for i, edge in enumerate(self.get_undirected_edges()):
             link_id = 'l%d' % i
             self.links.append(Link(link_id, edge))
     return self.links
Esempio n. 11
0
    def get_next_env(self, fwd_graphs, provided_env=None):
        # first check if there are any samples left, if not, return None
        if not self.more_envs():
            self.logger.debug("No more states left for sampling")
            return None

        # reset all policies on the edges
        for r1, r2 in self.graph.edges:
            self.graph.edges[r1, r2]['policies'] = set()

        # from the policy db get all destinations and the sources for which we have a policy
        source_counts = self.policy_db.get_source_counts(
            status=PolicyStatus.UNKNOWN)

        # create a pseudo policy identifier - at the moment, we don't actually care which policy it represents, but
        # just that it represents different policies.
        policy_id = 0

        # augment the weight graph with the policy information
        for subnet, counts in source_counts.items():

            assert subnet in fwd_graphs, "no forwarding graph for {subnet}".format(
                subnet=subnet)
            fwd_graph = fwd_graphs[subnet]

            for source, counts in counts.items():
                all_paths = nx.all_simple_paths(fwd_graph, source.router,
                                                "sink")

                for count in range(counts):
                    policy_id += 1

                    for path in all_paths:
                        for i in range(0, len(path) - 2):
                            self.graph.edges[path[i],
                                             path[i + 1]]["policies"].add(
                                                 policy_id)

        if self.debug:
            output = "Weighted Graph:\n"
            for r1, r2, policies in self.graph.edges.data("policies"):
                # output += "\t{link} - {policies}\n".format(link=Link.get_name(r1, r2), policies=policies)
                output += "\t{link} - num policies: {policies}\n".format(
                    link=Link.get_name(r1, r2), policies=len(policies))
            self.logger.debug(output)

        tries = 0
        excluded_links = set()
        while True:
            tries += 1

            output = ""
            failed_links = list()

            # order the links by the number of policies they "carry"
            num_edges = len(self.graph.edges)
            covered_policies = set()
            i = 0
            while len(failed_links) < self.netenv.k_failures and i < num_edges:
                all_edges = sorted(
                    self.graph.edges.data("policies"),
                    key=lambda x: len(x[2].difference(covered_policies)),
                    reverse=True)

                r1, r2, policies = all_edges[i]
                link = Link.get_name(r1, r2)

                if link not in excluded_links:
                    i = 0
                    failed_links.append(Link.get_name(r1, r2))
                    covered_policies |= policies
                    output += "\t{link} - {weight}\n".format(
                        link=Link.get_name(r1, r2), weight=len(policies))
                else:
                    i += 1

            self.logger.debug("Link choice:\n{links}".format(links=output))

            concrete_env = ConcreteEnvironment.from_failed_links(
                self.links, failed_links)

            # check if we have already used that specific environment
            if self.use_env(concrete_env):
                self.last_sample = concrete_env
                return concrete_env
            elif tries > 100 or not failed_links:
                self.logger.debug(
                    "Couldn't find an unused environment and hence, stop here."
                )
                return None
            else:
                # exclude some links
                excluded_links.add(random.choice(failed_links))
Esempio n. 12
0
    def get_next_env(self, fwd_graphs, provided_env=None):
        # first check if there are any samples left, if not, return None
        if not self.more_envs():
            self.logger.debug("No more states left for sampling")
            return None

        # reset all policies on the edges
        for r1, r2 in self.graph.edges:
            self.graph.edges[r1, r2]['policies'] = set()

        # from the policy db get all destinations and the sources for which we have a policy
        source_counts = self.policy_db.get_source_counts(
            status=PolicyStatus.UNKNOWN)

        # create a pseudo policy identifier - at the moment, we don't actually care which policy it represents, but
        # just that it represents different policies.
        policy_id = 0

        # augment the weight graph with the policy information
        for subnet, counts in source_counts.items():

            assert subnet in fwd_graphs, "no forwarding graph for {subnet}".format(
                subnet=subnet)
            fwd_graph = fwd_graphs[subnet]

            for source, counts in counts.items():
                all_paths = nx.all_simple_paths(fwd_graph, source.router,
                                                "sink")

                for count in range(counts):
                    policy_id += 1

                    for path in all_paths:
                        for i in range(0, len(path) - 2):
                            self.graph.edges[path[i],
                                             path[i + 1]]["policies"].add(
                                                 policy_id)

        # Debug
        if self.debug:
            output = "Weighted Graph:\n"
            for r1, r2, policies in self.graph.edges.data("policies"):
                # output += "\t{link} - {policies}\n".format(link=Link.get_name(r1, r2), policies=policies)
                output += "\t{link} - num policies: {policies}\n".format(
                    link=Link.get_name(r1, r2), policies=len(policies))
            self.logger.debug(output)

        # Pick the environment - Try at most 100 times
        tries = 0
        while True:
            tries += 1

            output = ""
            failed_links = list()

            # order the links by the number of policies they "carry"
            covered_policies = set()
            for i in range(0, self.netenv.k_failures):
                all_edges = list(self.graph.edges.data("policies"))

                total_weight = 0
                weights = list()
                for r1, r2, policies in all_edges:
                    num_uncovered_policies = len(
                        policies.difference(covered_policies))

                    total_weight += num_uncovered_policies
                    weights.append(float(num_uncovered_policies))

                if total_weight != 0:
                    normalized_weights = [w / total_weight for w in weights]
                    edge_id = np.random.choice(len(all_edges),
                                               1,
                                               replace=False,
                                               p=normalized_weights)[0]
                else:
                    edge_id = np.random.choice(len(all_edges),
                                               1,
                                               replace=False)[0]

                r1, r2, policies = all_edges[edge_id]

                failed_links.append(Link.get_name(r1, r2))
                output += "\t{link} - {weight}\n".format(link=Link.get_name(
                    r1, r2),
                                                         weight=len(policies))

                covered_policies |= policies

            self.logger.debug("Link choice:\n{links}".format(links=output))

            concrete_env = ConcreteEnvironment.from_failed_links(
                self.links, failed_links)

            # check if we have already used that specific environment
            if self.use_env(concrete_env):
                return concrete_env
            elif tries > 100:
                self.logger.debug(
                    "Couldn't find an unused environment and hence, stop here."
                )
                return None
Esempio n. 13
0
    def get_generic_single_counter_example():
        query = ResponseTest.get_query(sources=["r6"])

        response = ("Counterexample Found:\n"
                    "==========================================\n"
                    "Packet:\n"
                    "----------------------\n"
                    "dstIp: 11.12.7.2\n"
                    "\n"
                    "Environment Messages:\n"
                    "----------------------\n"
                    "Final Forwarding:\n"
                    "----------------------\n"
                    "r10,FastEthernet1/0 --> r9,FastEthernet0/0 (OSPF)\n"
                    "r11,FastEthernet0/0 --> r12,FastEthernet1/0 (OSPF)\n"
                    "r13,FastEthernet1/1 --> r8,FastEthernet0/1 (OSPF)\n"
                    "r14,FastEthernet1/0 --> r13,FastEthernet0/0 (OSPF)\n"
                    "r15,FastEthernet1/0 --> r14,FastEthernet0/0 (OSPF)\n"
                    "r15,FastEthernet1/1 --> r10,FastEthernet0/1 (OSPF)\n"
                    "r17,FastEthernet1/1 --> r12,FastEthernet0/1 (OSPF)\n"
                    "r18,FastEthernet1/0 --> r17,FastEthernet0/0 (OSPF)\n"
                    "r19,FastEthernet1/0 --> r18,FastEthernet0/0 (OSPF)\n"
                    "r2,FastEthernet0/1 --> r7,FastEthernet1/1 (OSPF)\n"
                    "r20,FastEthernet1/1 --> r15,FastEthernet0/1 (OSPF)\n"
                    "r24,FastEthernet1/1 --> r19,FastEthernet0/1 (OSPF)\n"
                    "r25,FastEthernet1/1 --> r20,FastEthernet0/1 (OSPF)\n"
                    "r3,FastEthernet0/1 --> r8,FastEthernet1/1 (OSPF)\n"
                    "r4,FastEthernet0/0 --> r5,FastEthernet1/0 (OSPF)\n"
                    "r5,FastEthernet0/1 --> r10,FastEthernet1/1 (OSPF)\n"
                    "r6,FastEthernet0/1 --> r11,FastEthernet1/1 (OSPF)\n"
                    "r7,FastEthernet0/1 --> r12,FastEthernet1/1 (CONNECTED)\n"
                    "r8,FastEthernet1/0 --> r7,FastEthernet0/0 (OSPF)\n"
                    "r9,FastEthernet1/0 --> r8,FastEthernet0/0 (OSPF)\n"
                    "\n"
                    "Failures:\n"
                    "----------------------\n"
                    "link(r1,r2)\n"
                    "link(r1,r6)\n"
                    "link(r11,r16)\n"
                    "link(r12,r13)\n"
                    "link(r14,r9)\n"
                    "link(r16,r17)\n"
                    "link(r16,r21)\n"
                    "link(r17,r22)\n"
                    "link(r18,r23)\n"
                    "link(r19,r20)\n"
                    "link(r2,r3)\n"
                    "link(r21,r22)\n"
                    "link(r22,r23)\n"
                    "link(r23,r24)\n"
                    "link(r24,r25)\n"
                    "link(r3,r4)\n"
                    "link(r4,r9)\n"
                    "link(r6,r7)\n"
                    "==========================================")

        failed_edges = [("r1", "r2"), ("r1", "r6"), ("r11", "r16"), ("r12", "r13"), ("r14", "r9"), ("r16", "r17"),
                        ("r16", "r21"), ("r17", "r22"), ("r18", "r23"), ("r19", "r20"), ("r2", "r3"), ("r21", "r22"),
                        ("r22", "r23"), ("r23", "r24"), ("r24", "r25"), ("r3", "r4"), ("r4", "r9"), ("r6", "r7")]
        failed_links = set([Link.get_name(r1, r2) for r1, r2 in failed_edges])

        src_routers = set()

        return query, response, failed_links, src_routers
Esempio n. 14
0
    def get_next_env(self, fwd_graphs, provided_env=None):
        # first check if there are any samples left, if not, return None
        if not self.more_envs():
            self.logger.debug("No more states left for sampling")
            return None

        # make a copy of the weight graph
        graph = self.graph.copy()

        # from the policy db get all destinations and the sources for which we have a policy
        source_counts = self.policy_db.get_source_counts(
            status=PolicyStatus.UNKNOWN)

        # augment the weight graph with the policy information
        for subnet, counts in source_counts.items():

            assert subnet in fwd_graphs, "no forwarding graph for {subnet}".format(
                subnet=subnet)
            fwd_graph = fwd_graphs[subnet]

            for source, count in counts.items():
                all_paths = nx.all_simple_paths(fwd_graph, source.router,
                                                "sink")

                for path in all_paths:
                    for i in range(0, len(path) - 2):
                        graph.edges[path[i], path[i + 1]]["weight"] += count

        # order the links by their weight and create the corresponding probability distribution
        all_edges = sorted(graph.edges.data("weight"),
                           key=lambda x: x[2],
                           reverse=True)

        if self.debug:
            output = "Weighted Graph:\n"
            for r1, r2, weight in all_edges:
                output += "\t{link} - {weight}\n".format(link=Link.get_name(
                    r1, r2),
                                                         weight=weight)
            self.logger.debug(output)

        total_weight = sum(x[2] for x in all_edges)
        if total_weight < 1:
            weights = None
        else:
            weights = [float(x[2]) / total_weight for x in all_edges]

        tries = 0
        while True:
            tries += 1

            # find a combination of edges by sampling k edges using the weights
            try:
                edge_candidates = np.random.choice(len(all_edges),
                                                   self.netenv.k_failures,
                                                   replace=False,
                                                   p=weights)
            except ValueError as e:
                self.logger.error(
                    "There was an error in picking the samples: {error}".
                    format(error=e))
                edge_candidates = np.random.choice(len(all_edges),
                                                   self.netenv.k_failures)

            # create concrete environment from the edge candidates
            output = "Link choice:\n"

            failed_links = list()
            for edge_id in edge_candidates:
                r1, r2, weight = all_edges[edge_id]
                failed_links.append(Link.get_name(r1, r2))

                output += "\t{link} - {weight}\n".format(link=Link.get_name(
                    r1, r2),
                                                         weight=weight)

            self.logger.debug(output)

            concrete_env = ConcreteEnvironment.from_failed_links(
                self.links, failed_links)

            # check if we have already used that specific environment
            if self.use_env(concrete_env):
                return concrete_env
            elif tries > 100:
                return self.get_next_unused_env()
Esempio n. 15
0
    def get_next_env(self, fwd_graphs, provided_env=None):
        # first check if there are any samples left, if not, return None
        if not self.more_envs():
            self.logger.debug("No more states left for sampling")
            return None

        # reset all policies on the edges
        for r1, r2 in self.graph.edges:
            self.graph.edges[r1, r2]['policies'] = set()

        # from the policy db get all destinations and the sources for which we have a policy
        source_counts = self.policy_db.get_source_counts(
            status=PolicyStatus.UNKNOWN)

        # create a pseudo policy identifier - at the moment, we don't actually care which policy it represents, but
        # just that it represents different policies.
        policy_id = 0

        # augment the weight graph with the policy information
        for subnet, counts in source_counts.items():

            assert subnet in fwd_graphs, "no forwarding graph for {subnet}".format(
                subnet=subnet)
            fwd_graph = fwd_graphs[subnet]

            for source, counts in counts.items():
                all_paths = nx.all_simple_paths(fwd_graph, source.router,
                                                "sink")

                for count in range(counts):
                    policy_id += 1

                    for path in all_paths:
                        for i in range(0, len(path) - 2):
                            self.graph.edges[path[i],
                                             path[i + 1]]["policies"].add(
                                                 policy_id)

        # debug output
        if self.debug:
            output = "Weighted Graph:\n"
            for r1, r2, policies in self.graph.edges.data("policies"):
                # output += "\t{link} - {policies}\n".format(link=Link.get_name(r1, r2), policies=policies)
                output += "\t{link} - num policies: {policies}\n".format(
                    link=Link.get_name(r1, r2), policies=len(policies))
            self.logger.debug(output)

        # pick links to fail according to the policies they carry - if we pick an environment that we have used before,
        # we try to merge the previous environment (which lead us to choose the already used one) and the already
        # used one to produce a new one.
        tries = 0
        while True:
            tries += 1

            output = ""
            failed_links = list()

            # order the links by the number of policies they "carry"
            covered_policies = set()
            while len(failed_links) < self.netenv.k_failures:
                all_edges = sorted(
                    self.graph.edges.data("policies"),
                    key=lambda x: len(x[2].difference(covered_policies)),
                    reverse=True)

                r1, r2, policies = all_edges[0]
                link = Link.get_name(r1, r2)
                failed_links.append(link)
                covered_policies |= policies
                output += "\t{link} - {weight}\n".format(link=Link.get_name(
                    r1, r2),
                                                         weight=len(policies))

            self.logger.debug("Link choice:\n{links}".format(links=output))

            concrete_env = ConcreteEnvironment.from_failed_links(
                self.links, failed_links)

            # check if we have already used that specific environment
            if self.use_env(concrete_env):
                self.last_sample = concrete_env
                return concrete_env
            elif tries > 100:
                self.logger.debug(
                    "Couldn't find an unused environment and hence, stop here."
                )
                return None
            else:
                self.logger.debug(
                    "We have already used that environment :( Will try to find a new one..."
                )
                links1 = set(
                    link.name
                    for link in self.last_sample.get_links(LinkState.DOWN))
                links2 = set(failed_links)

                # this only works if k is strictly larger than 1 - Hence if k is 0 or 1, there is no other option than
                # to stop here as we cannot merge anything.
                if self.netenv.k_failures <= 1:
                    return None

                failed_links = list()
                failed_links.append(random.choice(list(links1)))
                failed_links.append(random.choice(list(links2)))

                if self.netenv.k_failures - len(failed_links) > 0:
                    union = list(links1.union(links2))
                    additional_links = random.sample(
                        union, self.netenv.k_failures - len(failed_links))
                    failed_links.extend(additional_links)

                output = ""
                for link in failed_links:
                    output += "\t{link}\n".format(link=link)
                self.logger.debug("Link choice:\n{links}".format(links=output))

                concrete_env = ConcreteEnvironment.from_failed_links(
                    self.links, failed_links)

                if self.use_env(concrete_env):
                    self.last_sample = concrete_env
                    return concrete_env
Esempio n. 16
0
    def get_loadbalancing_policies(self,
                                   policies,
                                   forwarding_graphs,
                                   dominator_graphs,
                                   node_local_reachability=False,
                                   simple=True,
                                   edge_disjoint=False,
                                   node_disjoint=False):

        start_time = time.time()
        for subnet, graph in dominator_graphs.items():
            all_destinations, destinations, dst_routers = self.get_destination_interfaces_for_subnet(
                subnet, forwarding_graphs[subnet])

            # TODO how to deal with Loadbalancing to same prefix, but last hop is different router?!
            # only use router specific subnets (e.g., subnets that are connected at a single interface, such as loopback
            # and host subnets). This removes all subnet that just exist between two routers (usually /31)
            if len(all_destinations) > 1:
                continue

            for node in graph.nodes():
                # prevent policies within the same router
                if node == "sink" or (not node_local_reachability
                                      and node in dst_routers):
                    continue

                if len(destinations[node]) == 1:
                    policy_destination = destinations[node][0]
                    dst_router = policy_destination.router
                else:
                    self.logger.error(
                        "There should only be a single destination for {node} this subnet: {subnet}."
                        .format(node=node, subnet=subnet))
                    continue

                policy_source = PolicySource(node)

                all_paths = list(
                    nx.all_simple_paths(forwarding_graphs[subnet], node,
                                        "sink"))

                # only continue if there is more than one path available
                if len(all_paths) > 1:
                    # simple loadbalancing, more than a single path
                    if simple:
                        policies.append((PolicyType.LoadBalancingSimple,
                                         policy_destination, len(all_paths),
                                         policy_source))

                    # edge disjoint loadbalancing
                    if edge_disjoint:
                        edge_disjoint_paths = list()
                        edge_union = set()
                        for path in all_paths:
                            edges = set([
                                Link.get_name(path[i], path[i + 1])
                                for i in range(len(path) - 1)
                            ])

                            if not edge_union.intersection(edges):
                                edge_union.union(edges)
                                edge_disjoint_paths.append(path)

                        if len(edge_disjoint_paths) > 1:
                            self.logger.debug(
                                "There are {num_paths} edge-disjoint paths from {src} to {dst}."
                                .format(num_paths=len(edge_disjoint_paths),
                                        src=node,
                                        dst=dst_router))

                            # TODO add policies

                    # node disjoint loadbalancing
                    if node_disjoint:
                        node_disjoint_paths = list()
                        node_union = set()
                        for path in all_paths:
                            # need to remove the source and destination as all the paths will have them in common
                            nodes = set(path[1:-1])

                            if not node_union.intersection(nodes):
                                node_union.union(nodes)
                                node_disjoint_paths.append(path)

                        if len(node_disjoint_paths) > 1:
                            self.logger.debug(
                                "There are {num_paths} node-disjoint paths from {src} to {dst}."
                                .format(num_paths=len(node_disjoint_paths),
                                        src=node,
                                        dst=dst_router))

                            # TODO add policies

        self.logger.debug(
            "Getting the loadbalancing policies from the forwarding graphs took {time:.4f}s."
            .format(time=time.time() - start_time, ))

        return policies