Commit 7f843a1f authored by Alberts S's avatar Alberts S
Browse files

Add interface creation and tunnel setup

parent 29da4b11
......@@ -34,14 +34,15 @@ class CapybaraNetty(metaclass=MetaCapybaraNetty):
@staticmethod
async def ssh_exec(ip, username, command):
async with asyncssh.connect(host=ip, username=username, known_hosts=None, connect_timeout=10) as conn:
result = await conn.run(command)
result = await conn.run(command, check=True)
return result.stdout.strip("\n").split("\n"), result.stderr.strip("\n").split("\n")
@staticmethod
async def ssh_is_alive(ip, username):
try:
async with asyncssh.connect(host=ip, username=username, known_hosts=None, connect_timeout=3) as conn:
await conn.run("true")
await conn.run("true", check=True)
return True
except (asyncssh.misc.PermissionDenied, asyncio.exceptions.TimeoutError) as e:
return False
import asyncio
import ipaddress
import itertools
import yaml
......@@ -17,12 +18,26 @@ class Controller(CapybaraNetty):
self.routers = list()
self.pingers = list()
self.visualizer = Visualizer()
self.tunnel_network_base = ipaddress.ip_network(self.config["tunnel_network_base"]).subnets(new_prefix=30)
self.dummy_network_base = ipaddress.ip_network(self.config["dummy_network_base"]).subnets(new_prefix=32)
@classmethod
async def run(cls):
self = cls()
await self.main()
def get_tunnel_ips(self):
subnet = next(self.tunnel_network_base)
addresses = list(subnet.hosts())
interfaces = [ipaddress.ip_interface(f"{address}/{subnet.prefixlen}") for address in addresses]
return interfaces
def get_dummy_ip(self):
subnet = next(self.dummy_network_base)
addresses = list(subnet.hosts())
interfaces = [ipaddress.ip_interface(f"{address}/{subnet.prefixlen}") for address in addresses]
return interfaces[0]
async def main(self):
router_coroutines = []
for host in self.hosts:
......@@ -32,6 +47,7 @@ class Controller(CapybaraNetty):
router_coroutines.append(router.main())
self.routers = await asyncio.gather(*router_coroutines)
router_coroutines = []
for router in self.routers:
self.pingers.append(
......@@ -40,17 +56,20 @@ class Controller(CapybaraNetty):
router_ssh_user=self.config["default_router_ssh_user"],
)
)
"""
await router.add_routes(
[
{
"prefix" :"8.8.8.8/32",
"gateway":router.get_default_gateway(),
"interface": router.get_default_interface()
}
]
)
"""
router_coroutines.append(router.add_default_dummy_interface_ip(ip=self.get_dummy_ip()))
await asyncio.gather(*router_coroutines)
await asyncio.gather(self.set_router_interface_mesh())
"""
await router.add_routes(
[
{
"prefix" :"8.8.8.8/32",
"gateway":router.get_default_gateway(),
"interface": router.get_default_interface()
}
]
)
"""
self.load_pingers()
concurrent_tasks = [
......@@ -62,6 +81,32 @@ class Controller(CapybaraNetty):
await asyncio.gather(*concurrent_tasks)
async def set_router_interface_mesh(self):
coroutines_interfaces = []
coroutines_ips = []
for router in list(itertools.combinations(self.routers, 2)):
hosts = self.get_tunnel_ips()
a = router[0]
b = router[1]
coroutines_interfaces.extend(
[
a.add_gre_interface(
interface_name=b.name_simplified, local_ip=a.default_interface_ip.ip, remote_ip=b.ip
),
b.add_gre_interface(
interface_name=a.name_simplified, local_ip=b.default_interface_ip.ip, remote_ip=a.ip
),
]
)
coroutines_ips.extend(
[
a.add_interface_ip(interface_name=b.name_simplified, ip=hosts[0]),
b.add_interface_ip(interface_name=a.name_simplified, ip=hosts[1]),
]
)
await asyncio.gather(*coroutines_interfaces)
await asyncio.gather(*coroutines_ips)
# Load IP addresses to be pinged in pingers
def load_pingers(self):
# Get intra host pairs
......
......@@ -34,4 +34,5 @@ class Inventory(CapybaraNetty):
except KeyError as e:
self.__logger.warning(f"Skipped {chefNode} due to {type(e).__name__}")
self.__logger.info(f"Loaded {len(hosts)} hosts")
return hosts
import ipaddress
import asyncssh
from CapybaraNetty import CapybaraNetty
from Vty import Vty
class Router(CapybaraNetty):
def __init__(self, ip, name, ssh_user):
def __init__(self, ip: ipaddress.ip_address, name, ssh_user):
super().__init__()
self.ip = ip
self.ssh_user = ssh_user
self.name = name
self.name_simplified = name.split("-")[0]
self.vty = Vty(host_ip=ip, host_user=ssh_user)
self.routes = []
self.default_interface = None
self.default_interface_name = None
self.default_interface_ip = None
self.default_gateway = None
self.interface_prefix = self.config["router_interface_prefix"]
self.interfaces = []
self.interface_group_id = self.config["router_interface_group_id"]
self.dummy_interface_name = self.config["router_dummy_interface_name"]
@classmethod
async def run(cls, *args, **kwargs):
......@@ -28,9 +38,14 @@ class Router(CapybaraNetty):
return self
await self.get_routes()
await self.get_config_routes()
self.get_default_interface()
await self.get_interfaces()
self.get_default_interface_name()
self.get_default_interface_ip()
self.get_default_gateway()
await self.del_all_config_routes()
await self.del_existing_interfaces()
await self.add_dummy_interface(interface_name=self.dummy_interface_name)
return self
async def get_routes(self):
......@@ -45,10 +60,16 @@ class Router(CapybaraNetty):
await self.get_config_routes()
await self.vty.del_config_routes(self.config_routes)
def get_default_interface(self):
def get_default_interface_name(self):
default_route = next(route for route in self.routes if route["prefix"] == "0.0.0.0/0")
self.default_interface = default_route["nexthops"][0]["interfaceName"]
return self.default_interface
self.default_interface_name = default_route["nexthops"][0]["interfaceName"]
return self.default_interface_name
def get_default_interface_ip(self):
self.default_interface_ip = ipaddress.ip_interface(
self.interfaces[self.get_default_interface_name()]["ipAddresses"][0]["address"]
)
return self.default_interface_ip
def get_default_gateway(self):
default_route = next(route for route in self.routes if route["prefix"] == "0.0.0.0/0")
......@@ -57,3 +78,43 @@ class Router(CapybaraNetty):
async def add_routes(self, routes):
await self.vty.add_routes(routes)
async def del_existing_interfaces(self):
command = f"sudo ip link delete group {self.interface_group_id}"
try:
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}")
else:
raise
self.interfaces = []
async def add_gre_interface(
self, interface_name, local_ip: ipaddress.ip_address, remote_ip: ipaddress.ip_address, ttl=255
):
interface_name = self.get_interface_name(interface_name)
command = f"sudo ip link add name {interface_name} type gre local {local_ip} remote {remote_ip} ttl {ttl} dev {self.default_interface_name} && sudo ip link set {interface_name} group {self.interface_group_id}"
await self.ssh_exec(command)
async def add_dummy_interface(self, interface_name):
interface_name = self.get_interface_name(interface_name)
command = f"sudo ip link add name {interface_name} type dummy && sudo ip link set {interface_name} group {self.interface_group_id}"
await self.ssh_exec(command)
async def add_interface_ip(self, interface_name, ip: ipaddress.ip_interface):
interface_name = self.get_interface_name(interface_name)
command = f"sudo ip addr add {str(ip)} dev {interface_name} && sudo ip link set {interface_name} up"
await self.ssh_exec(command)
async def add_default_dummy_interface_ip(self, ip: ipaddress.ip_interface):
await self.add_interface_ip(self.dummy_interface_name, ip)
def get_interface_name(self, name):
return self.interface_prefix + name
async def get_interfaces(self):
self.interfaces = await self.vty.get_interfaces()
async def ssh_exec(self, command, **kwargs):
return await super(Router, self).ssh_exec(self.ip, self.ssh_user, command=command)
......@@ -89,5 +89,13 @@ class Vty(CapybaraNetty):
self.build_vty_exec(no_route_cmds),
)
async def get_interfaces(self):
interface_json, stderr = await self.ssh_exec(
self.build_vty_exec(["do show interface json"]),
)
# It returns list of strings which combined makes up JSON - need to join it up
interfaces = json.loads("".join(interface_json))
return interfaces
async def ssh_exec(self, command, **kwargs):
return await super(Vty, self).ssh_exec(self.host_ip, self.host_user, command=command)
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