Commit 14715fd8 authored by Alberts S's avatar Alberts S
Browse files

Create internal return routes

parent aeaa6c27
......@@ -57,3 +57,7 @@ class CapybaraNetty(metaclass=MetaCapybaraNetty):
@staticmethod
def log_format_ip(ip):
return f"{ip:<16s}"
@staticmethod
def log_format_name(name):
return f"{name:<20s}"
......@@ -58,6 +58,7 @@ class Controller(CapybaraNetty):
Pinger(
router_ip=router.ip,
router_ssh_user=self.config["default_router_ssh_user"],
external_interface_name=router.get_default_interface_name(),
)
)
router_coroutines.append(router.add_default_dummy_interface_ip(ip=self.get_dummy_ip()))
......
import asyncio
import ipaddress
import itertools
import networkx as nx
......@@ -60,47 +59,62 @@ class ControllerStatic(CapybaraNetty):
if optimized_ms < self.optimization_threshold_ms:
self.__logger.debug(f"IGNORED OPTIMIZATION: {optimized_ms:<3d}ms {'->'.join(path[::-1])}")
else:
# External targets
for node in path:
G.nodes[node]["improved_path"] = True
G.nodes[node].setdefault("improved_path_info", []).append(
self.get_improvement_obj(
target=target, source="10.0.0.1", path=path[::-1], optimization_ms=optimized_ms
)
self.get_improvement_obj(target=target, path=path[::-1], optimization_ms=optimized_ms)
)
# Internal targets
# On the current node we must have the interface IPs for next nodes in hops
for current_node in path:
if G.nodes[current_node]["type"] == "router":
for next_node in path[path.index(current_node) + 1 :]:
internal_source = str(G.nodes[next_node]["obj"].get_default_dummy_interface_ip().ip)
G.nodes[current_node]["improved_path_info"].append(
self.get_improvement_obj(
target=internal_source, path=path, optimization_ms=optimized_ms
)
)
self.__logger.info(f"VIABLE OPTIMIZATION: {optimized_ms:<3d}ms {'->'.join(path[::-1])}")
return G
@staticmethod
async def add_viable_external_routes(G):
async def add_viable_external_routes(self, G):
for node in G.nodes():
if G.nodes[node]["type"] == "router" and "improved_path_info" in G.nodes[node]:
current_router = G.nodes[node]["obj"]
routes = []
for path_obj in G.nodes[node]["improved_path_info"]:
idx = path_obj["path"].index(node)
path_len = len(path_obj["path"])
current_router = G.nodes[node]["obj"]
next_hop_name = path_obj["path"][idx + 1]
if G.nodes[next_hop_name]["type"] == "router":
next_router = G.nodes[next_hop_name]["obj"]
exit_interface_name = current_router.get_interface_name(next_router.name_simplified)
current_router_interfaces = await current_router.get_interfaces()
exit_interface_ip = ipaddress.ip_interface(
current_router_interfaces[exit_interface_name]["ipAddresses"][0]["address"]
).ip
await current_router.add_routes(
[
try:
next_hop_name = path_obj["path"][idx + 1]
if G.nodes[next_hop_name]["type"] == "router":
next_router = G.nodes[next_hop_name]["obj"]
inbound_interface_name = next_router.get_interface_name(current_router.name_simplified)
exit_interface_name = current_router.get_interface_name(next_router.name_simplified)
next_router_interfaces = await next_router.get_interfaces()
exit_gateway_ip = ipaddress.ip_interface(
next_router_interfaces[inbound_interface_name]["ipAddresses"][0]["address"]
).ip
routes.append(
{
"prefix": f"{path_obj['target']}/32",
"gateway": exit_interface_ip,
"gateway": exit_gateway_ip,
"interface": exit_interface_name,
}
]
)
)
except IndexError:
self.__logger.debug(f"Skipping route for {path_obj['target']} due to IndexError")
await current_router.add_routes(routes)
@staticmethod
def get_improvement_obj(target, source, path, optimization_ms):
return {"target": target, "source": source, "path": path, "optimization_ms": optimization_ms}
def get_improvement_obj(target, path, optimization_ms):
return {"target": target, "path": path, "optimization_ms": optimization_ms}
async def run_daemon(self, pinger_data_fn, routers_fn):
while True:
......
......@@ -7,13 +7,14 @@ from CapybaraNetty import CapybaraNetty
class Pinger(CapybaraNetty):
def __init__(self, router_ip, router_ssh_user, ping_addresses=None):
def __init__(self, router_ip, router_ssh_user, external_interface_name, ping_addresses=None):
super().__init__()
if ping_addresses is None:
ping_addresses = list()
self.router_ip = router_ip
self.router_ssh_user = router_ssh_user
self.ping_addresses = ping_addresses
self.external_interface_name = external_interface_name
self.latest_results = list()
async def ssh_exec(self, command, check, **kwargs):
......@@ -56,7 +57,7 @@ class Pinger(CapybaraNetty):
async def get_ping(self):
if len(self.ping_addresses) > 0:
self.__logger.info(f"{self.log_format_ip(self.router_ip)} Running")
fping_cmd_base = "fping -c 10 -N -p 500 "
fping_cmd_base = f"fping -c 10 -N -p 500 -I {self.external_interface_name} "
fping_cmd_dests = " ".join(self.ping_addresses)
fping_cmd = fping_cmd_base + " " + fping_cmd_dests
......
......@@ -24,6 +24,7 @@ class Router(CapybaraNetty):
self.managed_interfaces = {}
self.managed_routes = {}
self.dummy_interface_name = self.config["router_dummy_interface_name"]
self.debug_assert_routes = self.config["router_debug_assert_routes"]
@classmethod
async def run(cls, *args, **kwargs):
......@@ -69,7 +70,7 @@ class Router(CapybaraNetty):
self.default_interface_name = default_route["nexthops"][0]["interfaceName"]
return self.default_interface_name
except StopIteration:
self.__logger.error(f"{self.log_format_ip(self.ip)} has no default route")
self.__logger.error(f"{self.log_format_name(self.name)} has no default route")
raise
def get_default_interface_ip(self):
......@@ -87,6 +88,8 @@ class Router(CapybaraNetty):
for route in routes:
self.add_managed_route_info(route)
await self.vty.add_routes(routes)
if self.debug_assert_routes:
await self.assert_route_state()
async def del_existing_interfaces(self):
command = f"sudo ip link delete group {self.interface_group_id}"
......@@ -94,7 +97,9 @@ class Router(CapybaraNetty):
await self.ssh_exec(command)
except asyncssh.process.ProcessError as e:
if "No such device" in e.stderr:
self.__logger.warning(f"There were no interfaces in group {self.interface_group_id}")
self.__logger.warning(
f"{self.log_format_name(self.name)} There were no interfaces in group {self.interface_group_id}"
)
else:
raise
self.interfaces = []
......@@ -143,6 +148,17 @@ class Router(CapybaraNetty):
self.interfaces = await self.vty.get_interfaces()
return self.interfaces
# Confirms that the routes we have created actually exist
async def assert_route_state(self):
await self.get_routes()
for k, v in self.managed_routes.items():
try:
self.__logger.debug(f"{self.log_format_name(self.name)} Asserting existence of {v['prefix']}")
assert any(route["prefix"] == v["prefix"] for route in self.routes)
except AssertionError:
self.__logger.error(f"{self.log_format_name(self.name)} Route {v['prefix']} is not setup")
raise
async def ssh_exec(self, command, **kwargs):
return await super(Router, self).ssh_exec(self.ip, self.ssh_user, command=command)
......
......@@ -28,6 +28,7 @@ kiwisolver==1.4.2
MarkupSafe==2.1.1
matplotlib==3.5.1
matplotlib-inline==0.1.3
more-itertools==8.12.0
mypy-extensions==0.4.3
networkx==2.7.1
nodeenv==1.6.0
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment