From 13b41901cad36e306c7484c6f6758bfbc9922edf Mon Sep 17 00:00:00 2001 From: Jacob Boddey Date: Thu, 4 May 2023 12:45:32 +0100 Subject: [PATCH 1/5] Build dependencies first --- .../modules/dhcp-1/conf/module_config.json | 1 + .../modules/dhcp-2/conf/module_config.json | 1 + .../modules/dns/conf/module_config.json | 1 + .../modules/gateway/conf/module_config.json | 1 + .../modules/ntp/conf/module_config.json | 1 + .../modules/ovs/conf/module_config.json | 1 + .../modules/radius/conf/module_config.json | 1 + .../modules/template/conf/module_config.json | 1 + net_orc/python/src/network_orchestrator.py | 120 ++++++++++-------- 9 files changed, 74 insertions(+), 54 deletions(-) diff --git a/net_orc/network/modules/dhcp-1/conf/module_config.json b/net_orc/network/modules/dhcp-1/conf/module_config.json index 56d9aa271..4a41eee3f 100644 --- a/net_orc/network/modules/dhcp-1/conf/module_config.json +++ b/net_orc/network/modules/dhcp-1/conf/module_config.json @@ -14,6 +14,7 @@ "port": 5001 }, "docker": { + "depends_on": "base", "mounts": [ { "source": "runtime/network", diff --git a/net_orc/network/modules/dhcp-2/conf/module_config.json b/net_orc/network/modules/dhcp-2/conf/module_config.json index 2a978ca8c..bd719604d 100644 --- a/net_orc/network/modules/dhcp-2/conf/module_config.json +++ b/net_orc/network/modules/dhcp-2/conf/module_config.json @@ -14,6 +14,7 @@ "port": 5001 }, "docker": { + "depends_on": "base", "mounts": [ { "source": "runtime/network", diff --git a/net_orc/network/modules/dns/conf/module_config.json b/net_orc/network/modules/dns/conf/module_config.json index 73f890d28..cad1c02ef 100644 --- a/net_orc/network/modules/dns/conf/module_config.json +++ b/net_orc/network/modules/dns/conf/module_config.json @@ -11,6 +11,7 @@ "ip_index": 4 }, "docker": { + "depends_on": "base", "mounts": [ { "source": "runtime/network", diff --git a/net_orc/network/modules/gateway/conf/module_config.json b/net_orc/network/modules/gateway/conf/module_config.json index 35bd34392..5b39339ce 100644 --- a/net_orc/network/modules/gateway/conf/module_config.json +++ b/net_orc/network/modules/gateway/conf/module_config.json @@ -11,6 +11,7 @@ "ip_index": 1 }, "docker": { + "depends_on": "base", "mounts": [ { "source": "runtime/network", diff --git a/net_orc/network/modules/ntp/conf/module_config.json b/net_orc/network/modules/ntp/conf/module_config.json index 781521263..e3dbdc8f1 100644 --- a/net_orc/network/modules/ntp/conf/module_config.json +++ b/net_orc/network/modules/ntp/conf/module_config.json @@ -11,6 +11,7 @@ "ip_index": 5 }, "docker": { + "depends_on": "base", "mounts": [ { "source": "runtime/network", diff --git a/net_orc/network/modules/ovs/conf/module_config.json b/net_orc/network/modules/ovs/conf/module_config.json index f6a1eff50..8a440d0ae 100644 --- a/net_orc/network/modules/ovs/conf/module_config.json +++ b/net_orc/network/modules/ovs/conf/module_config.json @@ -12,6 +12,7 @@ "host": true }, "docker": { + "depends_on": "base", "mounts": [ { "source": "runtime/network", diff --git a/net_orc/network/modules/radius/conf/module_config.json b/net_orc/network/modules/radius/conf/module_config.json index 153d951df..ce8fbd52f 100644 --- a/net_orc/network/modules/radius/conf/module_config.json +++ b/net_orc/network/modules/radius/conf/module_config.json @@ -11,6 +11,7 @@ "ip_index": 7 }, "docker": { + "depends_on": "base", "mounts": [ { "source": "runtime/network", diff --git a/net_orc/network/modules/template/conf/module_config.json b/net_orc/network/modules/template/conf/module_config.json index bcea3808e..c767c9ad6 100644 --- a/net_orc/network/modules/template/conf/module_config.json +++ b/net_orc/network/modules/template/conf/module_config.json @@ -15,6 +15,7 @@ }, "docker": { "enable_container": false, + "depends_on": "base", "mounts": [ { "source": "runtime/network", diff --git a/net_orc/python/src/network_orchestrator.py b/net_orc/python/src/network_orchestrator.py index 63391a24f..451216fe2 100644 --- a/net_orc/python/src/network_orchestrator.py +++ b/net_orc/python/src/network_orchestrator.py @@ -224,60 +224,74 @@ def load_network_modules(self): for module_dir in os.listdir(net_modules_dir): - net_module = NetworkModule() - - # Load basic module information - - net_module_json = json.load(open(os.path.join( - self._path, net_modules_dir, module_dir, NETWORK_MODULE_METADATA), encoding='UTF-8')) - - net_module.name = net_module_json['config']['meta']['name'] - net_module.display_name = net_module_json['config']['meta']['display_name'] - net_module.description = net_module_json['config']['meta']['description'] - net_module.dir = os.path.join( - self._path, net_modules_dir, module_dir) - net_module.dir_name = module_dir - net_module.build_file = module_dir + ".Dockerfile" - net_module.container_name = "tr-ct-" + net_module.dir_name - net_module.image_name = "test-run/" + net_module.dir_name - - # Attach folder mounts to network module - if "docker" in net_module_json['config']: - if "mounts" in net_module_json['config']['docker']: - for mount_point in net_module_json['config']['docker']['mounts']: - net_module.mounts.append(Mount( - target=mount_point['target'], - source=os.path.join( - os.getcwd(), mount_point['source']), - type='bind' - )) - - # Determine if this is a container or just an image/template - if "enable_container" in net_module_json['config']['docker']: - net_module.enable_container = net_module_json['config']['docker']['enable_container'] - - # Load network service networking configuration - if net_module.enable_container: - - net_module.net_config.enable_wan = net_module_json['config']['network']['enable_wan'] - net_module.net_config.ip_index = net_module_json['config']['network']['ip_index'] - - net_module.net_config.host = False if not "host" in net_module_json[ - 'config']['network'] else net_module_json['config']['network']['host'] - - net_module.net_config.ipv4_address = self.network_config.ipv4_network[ - net_module.net_config.ip_index] - net_module.net_config.ipv4_network = self.network_config.ipv4_network - - net_module.net_config.ipv6_address = self.network_config.ipv6_network[ - net_module.net_config.ip_index] - net_module.net_config.ipv6_network = self.network_config.ipv6_network + if self._get_network_module(module_dir) is None: + loaded_module = self._load_network_module(module_dir) + loaded_modules += loaded_module.dir_name + " " - loaded_modules += net_module.dir_name + " " + LOGGER.info(loaded_modules) - self._net_modules.append(net_module) + def _load_network_module(self, module_dir): + + LOGGER.info("Loading module " + module_dir) + + net_modules_dir = os.path.join(self._path, NETWORK_MODULES_DIR) - LOGGER.info(loaded_modules) + net_module = NetworkModule() + + # Load basic module information + net_module_json = json.load(open(os.path.join( + self._path, net_modules_dir, module_dir, NETWORK_MODULE_METADATA), encoding='UTF-8')) + + net_module.name = net_module_json['config']['meta']['name'] + net_module.display_name = net_module_json['config']['meta']['display_name'] + net_module.description = net_module_json['config']['meta']['description'] + net_module.dir = os.path.join( + self._path, net_modules_dir, module_dir) + net_module.dir_name = module_dir + net_module.build_file = module_dir + ".Dockerfile" + net_module.container_name = "tr-ct-" + net_module.dir_name + net_module.image_name = "test-run/" + net_module.dir_name + + # Attach folder mounts to network module + if "docker" in net_module_json['config']: + + if "mounts" in net_module_json['config']['docker']: + for mount_point in net_module_json['config']['docker']['mounts']: + net_module.mounts.append(Mount( + target=mount_point['target'], + source=os.path.join( + os.getcwd(), mount_point['source']), + type='bind' + )) + + if "depends_on" in net_module_json['config']['docker']: + depends_on_module = net_module_json['config']['docker']['depends_on'] + if self._get_network_module(depends_on_module) is None: + self._load_network_module(depends_on_module) + + # Determine if this is a container or just an image/template + if "enable_container" in net_module_json['config']['docker']: + net_module.enable_container = net_module_json['config']['docker']['enable_container'] + + # Load network service networking configuration + if net_module.enable_container: + + net_module.net_config.enable_wan = net_module_json['config']['network']['enable_wan'] + net_module.net_config.ip_index = net_module_json['config']['network']['ip_index'] + + net_module.net_config.host = False if not "host" in net_module_json[ + 'config']['network'] else net_module_json['config']['network']['host'] + + net_module.net_config.ipv4_address = self.network_config.ipv4_network[ + net_module.net_config.ip_index] + net_module.net_config.ipv4_network = self.network_config.ipv4_network + + net_module.net_config.ipv6_address = self.network_config.ipv6_network[ + net_module.net_config.ip_index] + net_module.net_config.ipv6_network = self.network_config.ipv6_network + + self._net_modules.append(net_module) + return net_module def build_network_modules(self): LOGGER.info("Building network modules...") @@ -296,7 +310,7 @@ def _build_module(self, net_module): def _get_network_module(self, name): for net_module in self._net_modules: - if name == net_module.display_name: + if name == net_module.display_name or name == net_module.name or name == net_module.dir_name: return net_module return None @@ -515,7 +529,6 @@ def restore_net(self): LOGGER.info("Network is restored") - class NetworkModule: def __init__(self): @@ -539,7 +552,6 @@ def __init__(self): # The networking configuration for a network module - class NetworkModuleNetConfig: def __init__(self): From b3a9073dfce52973e954653406ae44992b8297c6 Mon Sep 17 00:00:00 2001 From: Jacob Boddey Date: Thu, 4 May 2023 12:47:47 +0100 Subject: [PATCH 2/5] Remove debug message --- net_orc/python/src/network_orchestrator.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/net_orc/python/src/network_orchestrator.py b/net_orc/python/src/network_orchestrator.py index 451216fe2..46d26c71f 100644 --- a/net_orc/python/src/network_orchestrator.py +++ b/net_orc/python/src/network_orchestrator.py @@ -232,8 +232,6 @@ def load_network_modules(self): def _load_network_module(self, module_dir): - LOGGER.info("Loading module " + module_dir) - net_modules_dir = os.path.join(self._path, NETWORK_MODULES_DIR) net_module = NetworkModule() From a0385b474f136d736e1f4fdfa98fb67d010be885 Mon Sep 17 00:00:00 2001 From: jhughesbiot Date: Tue, 9 May 2023 09:03:14 -0600 Subject: [PATCH 3/5] Add depend on option to test modules --- .../modules/baseline/conf/module_config.json | 1 + test_orc/modules/dns/conf/module_config.json | 1 + test_orc/python/src/test_orchestrator.py | 89 +++++++++++-------- 3 files changed, 54 insertions(+), 37 deletions(-) diff --git a/test_orc/modules/baseline/conf/module_config.json b/test_orc/modules/baseline/conf/module_config.json index ba337267a..4c0cd08d8 100644 --- a/test_orc/modules/baseline/conf/module_config.json +++ b/test_orc/modules/baseline/conf/module_config.json @@ -7,6 +7,7 @@ }, "network": false, "docker": { + "depends_on": "base", "enable_container": true, "timeout": 30 }, diff --git a/test_orc/modules/dns/conf/module_config.json b/test_orc/modules/dns/conf/module_config.json index d21f6bca6..b8ff36c97 100644 --- a/test_orc/modules/dns/conf/module_config.json +++ b/test_orc/modules/dns/conf/module_config.json @@ -7,6 +7,7 @@ }, "network": false, "docker": { + "depends_on": "base", "enable_container": true, "timeout": 30 }, diff --git a/test_orc/python/src/test_orchestrator.py b/test_orc/python/src/test_orchestrator.py index 85c6fb631..9eda1393a 100644 --- a/test_orc/python/src/test_orchestrator.py +++ b/test_orc/python/src/test_orchestrator.py @@ -113,6 +113,12 @@ def _get_module_status(self, module): return container.status return None + def _get_test_module(self, name): + for test_module in self._test_modules: + if name == test_module.display_name or name == test_module.name or name == test_module.dir_name: + return test_module + return None + def _get_module_container(self, module): container = None try: @@ -127,49 +133,58 @@ def _get_module_container(self, module): return container def _load_test_modules(self): - """Import module configuration from module_config.json.""" - - modules_dir = os.path.join(self._path, TEST_MODULES_DIR) - - LOGGER.debug("Loading test modules from /" + modules_dir) + """Load network modules from module_config.json.""" + LOGGER.debug("Loading test modules from /" + TEST_MODULES_DIR) + loaded_modules = "Loaded the following test modules: " + test_modules_dir = os.path.join(self._path, TEST_MODULES_DIR) + + for module_dir in os.listdir(test_modules_dir): - for module_dir in os.listdir(modules_dir): - - LOGGER.debug("Loading module from: " + module_dir) + if self._get_test_module(module_dir) is None: + loaded_module = self._load_test_module(module_dir) + loaded_modules += loaded_module.dir_name + " " - # Load basic module information - module = TestModule() - with open(os.path.join( - self._path, - modules_dir, - module_dir, - MODULE_CONFIG), - encoding='UTF-8') as module_config_file: - module_json = json.load(module_config_file) - - module.name = module_json['config']['meta']['name'] - module.display_name = module_json['config']['meta']['display_name'] - module.description = module_json['config']['meta']['description'] - module.dir = os.path.join(self._path, modules_dir, module_dir) - module.dir_name = module_dir - module.build_file = module_dir + ".Dockerfile" - module.container_name = "tr-ct-" + module.dir_name + "-test" - module.image_name = "test-run/" + module.dir_name + "-test" - - if 'timeout' in module_json['config']['docker']: - module.timeout = module_json['config']['docker']['timeout'] - - # Determine if this is a container or just an image/template - if "enable_container" in module_json['config']['docker']: - module.enable_container = module_json['config']['docker']['enable_container'] + LOGGER.info(loaded_modules) - self._test_modules.append(module) + def _load_test_module(self,module_dir): + """Import module configuration from module_config.json.""" - if module.enable_container: - loaded_modules += module.dir_name + " " + modules_dir = os.path.join(self._path, TEST_MODULES_DIR) - LOGGER.info(loaded_modules) + # Load basic module information + module = TestModule() + with open(os.path.join( + self._path, + modules_dir, + module_dir, + MODULE_CONFIG), + encoding='UTF-8') as module_config_file: + module_json = json.load(module_config_file) + + module.name = module_json['config']['meta']['name'] + module.display_name = module_json['config']['meta']['display_name'] + module.description = module_json['config']['meta']['description'] + module.dir = os.path.join(self._path, modules_dir, module_dir) + module.dir_name = module_dir + module.build_file = module_dir + ".Dockerfile" + module.container_name = "tr-ct-" + module.dir_name + "-test" + module.image_name = "test-run/" + module.dir_name + "-test" + + if 'timeout' in module_json['config']['docker']: + module.timeout = module_json['config']['docker']['timeout'] + + # Determine if this is a container or just an image/template + if "enable_container" in module_json['config']['docker']: + module.enable_container = module_json['config']['docker']['enable_container'] + + if "depends_on" in module_json['config']['docker']: + depends_on_module = module_json['config']['docker']['depends_on'] + if self._get_test_module(depends_on_module) is None: + self._load_test_module(depends_on_module) + + self._test_modules.append(module) + return module def build_test_modules(self): """Build all test modules.""" From a07105832704d773aa57097e973d30c728ae765d Mon Sep 17 00:00:00 2001 From: J Boddey Date: Tue, 16 May 2023 19:53:02 +0100 Subject: [PATCH 4/5] Re-add single interface option --- net_orc/python/src/network_orchestrator.py | 45 ++++++++++++++++++++-- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/net_orc/python/src/network_orchestrator.py b/net_orc/python/src/network_orchestrator.py index 46d26c71f..f7ed62672 100644 --- a/net_orc/python/src/network_orchestrator.py +++ b/net_orc/python/src/network_orchestrator.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 +import getpass import ipaddress import json import os @@ -31,10 +32,10 @@ class NetworkOrchestrator: """Manage and controls a virtual testing network.""" - def __init__(self, config_file=CONFIG_FILE, validate=True, async_monitor=False): + def __init__(self, config_file=CONFIG_FILE, validate=True, async_monitor=False, single_intf = False): self._int_intf = None self._dev_intf = None - + self._single_intf = single_intf self.listener = None self._net_modules = [] @@ -178,6 +179,38 @@ def _create_private_net(self): check_duplicate=True, driver="macvlan" ) + + def _ci_pre_network_create(self): + """ Stores network properties to restore network after + network creation and flushes internet interface + """ + + self._ethmac = subprocess.check_output( + f"cat /sys/class/net/{self._int_intf}/address", shell=True).decode("utf-8").strip() + self._gateway = subprocess.check_output( + "ip route | head -n 1 | awk '{print $3}'", shell=True).decode("utf-8").strip() + self._ipv4 = subprocess.check_output( + f"ip a show {self._int_intf} | grep \"inet \" | awk '{{print $2}}'", shell=True).decode("utf-8").strip() + self._ipv6 = subprocess.check_output( + f"ip a show {self._int_intf} | grep inet6 | awk '{{print $2}}'", shell=True).decode("utf-8").strip() + self._brd = subprocess.check_output( + f"ip a show {self._int_intf} | grep \"inet \" | awk '{{print $4}}'", shell=True).decode("utf-8").strip() + + def _ci_post_network_create(self): + """ Restore network connection in CI environment """ + LOGGER.info("post cr") + util.run_command(f"ip address del {self._ipv4} dev {self._int_intf}") + util.run_command(f"ip -6 address del {self._ipv6} dev {self._int_intf}") + util.run_command(f"ip link set dev {self._int_intf} address 00:B0:D0:63:C2:26") + util.run_command(f"ip addr flush dev {self._int_intf}") + util.run_command(f"ip addr add dev {self._int_intf} 0.0.0.0") + util.run_command(f"ip addr add dev {INTERNET_BRIDGE} {self._ipv4} broadcast {self._brd}") + util.run_command(f"ip -6 addr add {self._ipv6} dev {INTERNET_BRIDGE} ") + util.run_command(f"systemd-resolve --interface {INTERNET_BRIDGE} --set-dns 8.8.8.8") + util.run_command(f"ip link set dev {INTERNET_BRIDGE} up") + util.run_command(f"dhclient {INTERNET_BRIDGE}") + util.run_command(f"ip route del default via 10.1.0.1") + util.run_command(f"ip route add default via {self._gateway} src {self._ipv4[:-3]} metric 100 dev {INTERNET_BRIDGE}") def create_net(self): LOGGER.info("Creating baseline network") @@ -186,6 +219,9 @@ def create_net(self): LOGGER.error("Configured interfaces are not ready for use. " + "Ensure both interfaces are connected.") sys.exit(1) + + if self._single_intf: + self._ci_pre_network_create() # Create data plane util.run_command("ovs-vsctl add-br " + DEVICE_BRIDGE) @@ -209,6 +245,9 @@ def create_net(self): # Set ports up util.run_command("ip link set dev " + DEVICE_BRIDGE + " up") util.run_command("ip link set dev " + INTERNET_BRIDGE + " up") + + if self._single_intf: + self._ci_post_network_create() self._create_private_net() @@ -337,7 +376,7 @@ def _start_network_service(self, net_module): privileged=True, detach=True, mounts=net_module.mounts, - environment={"HOST_USER": os.getlogin()} + environment={"HOST_USER": getpass.getuser()} ) except docker.errors.ContainerError as error: LOGGER.error("Container run error") From 07a41d1725211e0691bbf08ea25ebea0f61fd4c4 Mon Sep 17 00:00:00 2001 From: J Boddey Date: Tue, 16 May 2023 20:05:21 +0100 Subject: [PATCH 5/5] Import subprocess --- net_orc/python/src/network_orchestrator.py | 1 + 1 file changed, 1 insertion(+) diff --git a/net_orc/python/src/network_orchestrator.py b/net_orc/python/src/network_orchestrator.py index f7ed62672..6930f22be 100644 --- a/net_orc/python/src/network_orchestrator.py +++ b/net_orc/python/src/network_orchestrator.py @@ -4,6 +4,7 @@ import ipaddress import json import os +import subprocess import sys import time import threading