From 478a54de1d717e0550ac53da022be42144fa94d3 Mon Sep 17 00:00:00 2001 From: Peng LEI Date: Sun, 4 Aug 2024 10:23:09 +0800 Subject: [PATCH 1/3] chore: IHost interface --- src/containernet/containernet_host.py | 33 +++++++++++++++++ ...zed_network.py => containernet_network.py} | 37 +++++++++++-------- src/containernet/requirements.txt | 3 ++ src/interfaces/host.py | 16 +++++++- src/interfaces/network.py | 1 + src/run_test.py | 2 +- src/start.py | 2 +- 7 files changed, 76 insertions(+), 18 deletions(-) create mode 100644 src/containernet/containernet_host.py rename src/containernet/{containerized_network.py => containernet_network.py} (92%) create mode 100644 src/containernet/requirements.txt diff --git a/src/containernet/containernet_host.py b/src/containernet/containernet_host.py new file mode 100644 index 0000000..7ba5759 --- /dev/null +++ b/src/containernet/containernet_host.py @@ -0,0 +1,33 @@ +# adapter pattern +from interfaces.host import IHost + +class ContainernetHostAdapter(IHost): + def __init__(self, containernet_host): + self.containernet_host = containernet_host + + def cmd(self, command): + return self.containernet_host.cmd(command) + + def name(self) -> str: + """Get the name of the host. + """ + return self.containernet_host.name + + def IP(self) -> str: + """Get the IP address of the host. + """ + return self.containernet_host.IP() + + def deleteIntfs(self): + """Delete all interfaces. + """ + return self.containernet_host.deleteIntfs() + + def cleanup(self): + """Cleanup the host. + """ + return self.containernet_host.cleanup() + + def get_host(self): + return self.containernet_host + \ No newline at end of file diff --git a/src/containernet/containerized_network.py b/src/containernet/containernet_network.py similarity index 92% rename from src/containernet/containerized_network.py rename to src/containernet/containernet_network.py index f914f44..ed0b9ce 100644 --- a/src/containernet/containerized_network.py +++ b/src/containernet/containernet_network.py @@ -4,6 +4,7 @@ from mininet.util import ipStr, netParse from mininet.link import TCLink from containernet.topology import (ITopology, MatrixType) +from containernet.containernet_host import ContainernetHostAdapter from interfaces.network import INetwork from .config import NodeConfig @@ -32,6 +33,7 @@ def __init__(self, ** params) -> None: super().__init__(**params) self.containernet = Containernet() + self.hosts = [] # NodeConfig: Docker node related self.node_img = node_config.node_img self.node_vols = node_config.node_vols @@ -72,7 +74,7 @@ def __init__(self, self._init_containernet() def get_hosts(self): - return self.containernet.hosts + return self.hosts def start(self): logging.info("Oasis starts the ContainerizedNetwork.") @@ -130,6 +132,8 @@ def _setup_docker_nodes(self): port_bindings=port_bindings, publish_all_ports=True ) + self.hosts = [ContainernetHostAdapter(host) \ + for host in self.containernet.hosts] logging.info( "setup_docker_nodes, num. of nodes is %s.", self.num_of_hosts) return True @@ -150,9 +154,9 @@ def _setup_topology(self): right_ip = ipStr(link_ip + 2) + f'/{link_prefix}' logging.info( "addLink: %s(%s) <--> %s(%s)", - self.containernet.hosts[i].name, + self.hosts[i].name, left_ip, - self.containernet.hosts[j].name, + self.hosts[j].name, right_ip ) self.__addLink(i, j, @@ -160,12 +164,12 @@ def _setup_topology(self): params2={'ip': right_ip} ) self.pair_to_link_ip[( - self.containernet.hosts[i], - self.containernet.hosts[j])] = ipStr(link_ip + 2) + self.hosts[i], + self.hosts[j])] = ipStr(link_ip + 2) self.pair_to_link_ip[( - self.containernet.hosts[j], - self.containernet.hosts[i])] = ipStr(link_ip + 1) - for host in self.containernet.hosts: + self.hosts[j], + self.hosts[i])] = ipStr(link_ip + 1) + for host in self.hosts: host.cmd("echo 1 > /proc/sys/net/ipv4/ip_forward") host.cmd('sysctl -p') logging.info( @@ -195,7 +199,8 @@ class 50001 is big. Consider r2q change. if self.net_jitter_mat is not None: params['jitter'] = self.net_jitter_mat[id1][id2] link = self.containernet.addLink( - self.containernet.hosts[id1], self.containernet.hosts[id2], + self.hosts[id1].get_host(), + self.hosts[id2].get_host(), port1, port2, cls=TCLink, **params) return link @@ -217,8 +222,10 @@ def _setup_ip_routes(self): ''' Setup the routing by ip route. ''' + # need convert the node to "IHost" type: ContainernetHostAdapter(host) for route in self.net_routes: - route = [self.containernet.nameToNode[f'h{i}'] for i in route] + # route = [self.containernet.nameToNode[f'h{i}'] for i in route] + route = [self.hosts[i] for i in route] self._add_route(route) def _setup_olsr_routes(self): @@ -326,13 +333,13 @@ def _reset_network(self, num, diff): # remove all links for i in range(num - 1): logging.info("removeLink: %s-%s", - self.containernet.hosts[i].name, - self.containernet.hosts[i+1].name) + self.hosts[i].name, + self.hosts[i+1].name) self.containernet.removeLink( - node1=self.containernet.hosts[i].name, - node2=self.containernet.hosts[i+1].name) + node1=self.hosts[i].name, + node2=self.hosts[i+1].name) # remove all routes. - for host in self.containernet.hosts: + for host in self.hosts: host.cmd('ip route flush table main') host.deleteIntfs() host.cleanup() diff --git a/src/containernet/requirements.txt b/src/containernet/requirements.txt new file mode 100644 index 0000000..77cac57 --- /dev/null +++ b/src/containernet/requirements.txt @@ -0,0 +1,3 @@ +# python3.6 +pyyaml +dataclasses diff --git a/src/interfaces/host.py b/src/interfaces/host.py index f68e863..f6a8c5d 100644 --- a/src/interfaces/host.py +++ b/src/interfaces/host.py @@ -6,6 +6,20 @@ def __init__(self): pass @abstractmethod - def cmd(self, input: str) -> str: + def cmd(self, command: str) -> str: """Execute a command on the host. """ + def name(self) -> str: + """Get the name of the host. + """ + def IP(self) -> str: + """Get the IP address of the host. + """ + def deleteIntfs(self): + """Delete all interfaces. + """ + def cleanup(self): + """Cleanup the host. + """ + def get_host(self): + pass diff --git a/src/interfaces/network.py b/src/interfaces/network.py index e6ba63d..a977c61 100644 --- a/src/interfaces/network.py +++ b/src/interfaces/network.py @@ -1,4 +1,5 @@ import logging +from typing import List from abc import ABC, abstractmethod from containernet.topology import (ITopology) from testsuites.test import ITestSuite diff --git a/src/run_test.py b/src/run_test.py index 777c3d5..1564a56 100755 --- a/src/run_test.py +++ b/src/run_test.py @@ -7,7 +7,7 @@ # from mininet.cli import CLI from mininet.log import setLogLevel from containernet.linear_topology import LinearTopology -from containernet.containerized_network import ContainerizedNetwork +from containernet.containernet_network import ContainerizedNetwork from containernet.config import ( IConfig, NodeConfig, TopologyConfig, supported_config_keys) from testsuites.test import (TestType, TestConfig) diff --git a/src/start.py b/src/start.py index 1355578..5733a5a 100755 --- a/src/start.py +++ b/src/start.py @@ -71,6 +71,6 @@ def build_nested_env(config_file, containernet_name, workspace): nested_config_file, nested_containernet, cur_workspace) nested_env.start() nested_env.execute( - f"python3 src/run_test.py {cur_workspace} " + f"python3.6 src/run_test.py {cur_workspace} " f"{cur_config_yaml_file_path}") nested_env.stop() From e44f1d4efa81dccd886128af8adf778c1d4e10e2 Mon Sep 17 00:00:00 2001 From: Peng LEI Date: Sun, 4 Aug 2024 10:48:32 +0800 Subject: [PATCH 2/3] chore: IRouting interface --- src/containernet/containernet_network.py | 89 ++++-------------------- src/interfaces/network.py | 9 ++- src/interfaces/routing.py | 6 ++ src/routing/olsr_routing.py | 9 +++ src/routing/openr_routing.py | 9 +++ src/routing/static_routing.py | 65 +++++++++++++++++ src/run_test.py | 4 +- 7 files changed, 111 insertions(+), 80 deletions(-) create mode 100644 src/interfaces/routing.py create mode 100644 src/routing/olsr_routing.py create mode 100644 src/routing/openr_routing.py create mode 100644 src/routing/static_routing.py diff --git a/src/containernet/containernet_network.py b/src/containernet/containernet_network.py index ed0b9ce..cc0787e 100644 --- a/src/containernet/containernet_network.py +++ b/src/containernet/containernet_network.py @@ -6,6 +6,7 @@ from containernet.topology import (ITopology, MatrixType) from containernet.containernet_host import ContainernetHostAdapter from interfaces.network import INetwork +from interfaces.routing import IRoutingStrategy from .config import NodeConfig @@ -30,9 +31,11 @@ class ContainerizedNetwork (INetwork): def __init__(self, node_config: NodeConfig = None, net_topology: ITopology = None, + routing_strategy: IRoutingStrategy = None, ** params) -> None: super().__init__(**params) self.containernet = Containernet() + self.routing_strategy = routing_strategy self.hosts = [] # NodeConfig: Docker node related self.node_img = node_config.node_img @@ -76,6 +79,9 @@ def __init__(self, def get_hosts(self): return self.hosts + def get_num_of_host(self): + return self.num_of_hosts + def start(self): logging.info("Oasis starts the ContainerizedNetwork.") self.containernet.build() @@ -105,10 +111,13 @@ def reload(self, top: ITopology): else: self._shrink_network(diff) + def get_link_table(self): + return self.pair_to_link_ip + def _init_containernet(self): self._setup_docker_nodes() self._setup_topology() - self._setup_routes() + self.routing_strategy.setup_routes(self) def _setup_docker_nodes(self): """ @@ -204,80 +213,6 @@ class 50001 is big. Consider r2q change. port1, port2, cls=TCLink, **params) return link - def _setup_routes(self): - if self.node_route == 'ip' and self.topology_type != 'linear': - logging.error( - "The ip routing config %s is not supported for %s.", - self.node_route, self.topology_type) - # setup routes - if self.node_route == 'ip': - self._setup_ip_routes() - elif self.node_route == 'olsr': - self._setup_olsr_routes() - else: - logging.error( - "The routing config %s is not supported.", self.node_route) - - def _setup_ip_routes(self): - ''' - Setup the routing by ip route. - ''' - # need convert the node to "IHost" type: ContainernetHostAdapter(host) - for route in self.net_routes: - # route = [self.containernet.nameToNode[f'h{i}'] for i in route] - route = [self.hosts[i] for i in route] - self._add_route(route) - - def _setup_olsr_routes(self): - ''' - setup the OLSR routing - ''' - - @ staticmethod - def _add_ip_gateway(host, gateway_ip, dst_ip): - host.cmd(f'ip r a {dst_ip} via {gateway_ip}') - - def _add_route(self, route): - for i in range(len(route) - 1): - for j in range(i + 1, len(route)): - host = route[i] - gateway = route[i + 1] - dst_prev = route[j - 1] - dst = route[j] - if j < len(route) - 1: - dst_next = route[j + 1] - - # gateway ip is the ip of the second (right) interface - # of the link (route_i, route_{i+1}) - gateway_ip = self.pair_to_link_ip[(host, gateway)] - - # dst ip is the ip of the second (right) interface in the link - # (route_{j-1}, route_j) - dst_ip = self.pair_to_link_ip[(dst_prev, dst)] - if j < len(route) - 1: - dst_ip_right = self.pair_to_link_ip[(dst_next, dst)] - self._add_ip_gateway(host, gateway_ip, dst_ip) - if j < len(route) - 1: - self._add_ip_gateway(host, gateway_ip, dst_ip_right) - - for i in range(1, len(route)): - for j in range(0, i): - host = route[i] - gateway = route[i - 1] - dst_prev = route[j + 1] - dst = route[j] - - if j >= 1: - dst_next = route[j - 1] - - gateway_ip = self.pair_to_link_ip[(host, gateway)] - dst_ip = self.pair_to_link_ip[(dst_prev, dst)] - if j >= 1: - dst_ip_left = self.pair_to_link_ip[(dst_next, dst)] - self._add_ip_gateway(host, gateway_ip, dst_ip) - if j >= 1: - self._add_ip_gateway(host, gateway_ip, dst_ip_left) - def _check_node_vols(self): if not os.path.exists('/usr/bin/perf') or \ not os.path.isfile('/usr/bin/perf'): @@ -308,7 +243,7 @@ def _expand_network(self, diff): ) self.num_of_hosts += diff self._setup_topology() - self._setup_routes() + self.routing_strategy.setup_routes(self) logging.info( "Expand the network. number of nodes increased by %s", diff) @@ -325,7 +260,7 @@ def _shrink_network(self, diff): self.containernet.removeDocker(f'{self.node_name_prefix}{i}') self.num_of_hosts -= diff self._setup_topology() - self._setup_routes() + self.routing_strategy.setup_routes(self) return True def _reset_network(self, num, diff): diff --git a/src/interfaces/network.py b/src/interfaces/network.py index a977c61..d140296 100644 --- a/src/interfaces/network.py +++ b/src/interfaces/network.py @@ -1,5 +1,4 @@ import logging -from typing import List from abc import ABC, abstractmethod from containernet.topology import (ITopology) from testsuites.test import ITestSuite @@ -21,6 +20,14 @@ def stop(self): def get_hosts(self): pass + @abstractmethod + def get_num_of_host(self): + pass + + @abstractmethod + def get_link_table(self): + pass + @abstractmethod def reload(self, top: ITopology): pass diff --git a/src/interfaces/routing.py b/src/interfaces/routing.py new file mode 100644 index 0000000..c9e6222 --- /dev/null +++ b/src/interfaces/routing.py @@ -0,0 +1,6 @@ +from abc import ABC, abstractmethod + +class IRoutingStrategy(ABC): + @abstractmethod + def setup_routes(self, network): + pass diff --git a/src/routing/olsr_routing.py b/src/routing/olsr_routing.py new file mode 100644 index 0000000..3af91e6 --- /dev/null +++ b/src/routing/olsr_routing.py @@ -0,0 +1,9 @@ +from interfaces.routing import IRoutingStrategy + +class OLSRRouting(IRoutingStrategy): + """Summary: + Configure routing for the network with OLSR. + """ + def setup_routes(self, network: 'INetwork'): # type: ignore + pass + \ No newline at end of file diff --git a/src/routing/openr_routing.py b/src/routing/openr_routing.py new file mode 100644 index 0000000..bd40356 --- /dev/null +++ b/src/routing/openr_routing.py @@ -0,0 +1,9 @@ +from interfaces.routing import IRoutingStrategy + +class OpenrRouting(IRoutingStrategy): + """Summary: + Configure routing for the network with open-r. + """ + def setup_routes(self, network: 'INetwork'): # type: ignore + pass + \ No newline at end of file diff --git a/src/routing/static_routing.py b/src/routing/static_routing.py new file mode 100644 index 0000000..acbe54d --- /dev/null +++ b/src/routing/static_routing.py @@ -0,0 +1,65 @@ +from interfaces.routing import IRoutingStrategy + +class StaticRouting(IRoutingStrategy): + """Summary: + Configure static routing for the network. + """ + def __init__(self): + self.pair_to_link_ip = {} + self.net_routes = [] + + def setup_routes(self, network: 'INetwork'): + ''' + Setup the routing by ip route. + ''' + hosts = network.get_hosts() + self.pair_to_link_ip = network.get_link_table() + self.net_routes = [range(network.get_num_of_host())] + for route in self.net_routes: + route = [hosts[i] for i in route] + self._add_route(route) + + @ staticmethod + def _add_ip_gateway(host, gateway_ip, dst_ip): + host.cmd(f'ip r a {dst_ip} via {gateway_ip}') + + def _add_route(self, route): + for i in range(len(route) - 1): + for j in range(i + 1, len(route)): + host = route[i] + gateway = route[i + 1] + dst_prev = route[j - 1] + dst = route[j] + if j < len(route) - 1: + dst_next = route[j + 1] + + # gateway ip is the ip of the second (right) interface + # of the link (route_i, route_{i+1}) + gateway_ip = self.pair_to_link_ip[(host, gateway)] + + # dst ip is the ip of the second (right) interface in the link + # (route_{j-1}, route_j) + dst_ip = self.pair_to_link_ip[(dst_prev, dst)] + if j < len(route) - 1: + dst_ip_right = self.pair_to_link_ip[(dst_next, dst)] + self._add_ip_gateway(host, gateway_ip, dst_ip) + if j < len(route) - 1: + self._add_ip_gateway(host, gateway_ip, dst_ip_right) + + for i in range(1, len(route)): + for j in range(0, i): + host = route[i] + gateway = route[i - 1] + dst_prev = route[j + 1] + dst = route[j] + + if j >= 1: + dst_next = route[j - 1] + + gateway_ip = self.pair_to_link_ip[(host, gateway)] + dst_ip = self.pair_to_link_ip[(dst_prev, dst)] + if j >= 1: + dst_ip_left = self.pair_to_link_ip[(dst_next, dst)] + self._add_ip_gateway(host, gateway_ip, dst_ip) + if j >= 1: + self._add_ip_gateway(host, gateway_ip, dst_ip_left) diff --git a/src/run_test.py b/src/run_test.py index 1564a56..3074c14 100755 --- a/src/run_test.py +++ b/src/run_test.py @@ -13,7 +13,7 @@ from testsuites.test import (TestType, TestConfig) from testsuites.test_iperf import IperfTest from testsuites.test_ping import PingTest - +from routing.static_routing import StaticRouting def load_test(test_yaml_file: str): """ @@ -95,7 +95,7 @@ def build_network(node_config: NodeConfig, top_config: TopologyConfig): ContainerizedNetwork: the container network object """ net_top = build_topology(top_config) - return ContainerizedNetwork(node_config, net_top) + return ContainerizedNetwork(node_config, net_top, StaticRouting()) if __name__ == '__main__': From c2fbcd3488ca774f450793704c483f3f6c8627c4 Mon Sep 17 00:00:00 2001 From: Peng LEI Date: Sun, 4 Aug 2024 10:51:30 +0800 Subject: [PATCH 3/3] fix: pylint fix --- src/testbed/testbed_host.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/testbed/testbed_host.py b/src/testbed/testbed_host.py index c4a57ae..3b5c036 100644 --- a/src/testbed/testbed_host.py +++ b/src/testbed/testbed_host.py @@ -3,6 +3,9 @@ class TestbedHost(IHost): - def cmd(self, input: str) -> str: + def cmd(self, command: str) -> str: """Execute a command on the host. """ + + def get_host(self): + return self