diff --git a/framework/python/src/net_orc/network_validator.py b/framework/python/src/net_orc/network_validator.py index a4c51eb2d..f82787af5 100644 --- a/framework/python/src/net_orc/network_validator.py +++ b/framework/python/src/net_orc/network_validator.py @@ -193,7 +193,7 @@ def _get_os_user(self): LOGGER.error('An OS error occurred while retrieving the login name.') except Exception as error: # Catch any other unexpected exceptions - LOGGER.error('An exception occurred:', error) + LOGGER.error('An exception occurred:', error) return user def _get_user(self): @@ -203,15 +203,15 @@ def _get_user(self): except (KeyError, ImportError, ModuleNotFoundError, OSError) as e: # Handle specific exceptions individually if isinstance(e, KeyError): - LOGGER.error("USER environment variable not set or unavailable.") + LOGGER.error('USER environment variable not set or unavailable.') elif isinstance(e, ImportError): - LOGGER.error("Unable to import the getpass module.") + LOGGER.error('Unable to import the getpass module.') elif isinstance(e, ModuleNotFoundError): - LOGGER.error("The getpass module was not found.") + LOGGER.error('The getpass module was not found.') elif isinstance(e, OSError): - LOGGER.error("An OS error occurred while retrieving the username.") + LOGGER.error('An OS error occurred while retrieving the username.') else: - LOGGER.error("An exception occurred:", e) + LOGGER.error('An exception occurred:', e) return user def _get_device_status(self, module): diff --git a/framework/python/src/test_orc/test_orchestrator.py b/framework/python/src/test_orc/test_orchestrator.py index 4bc9fc003..fef4e5bb5 100644 --- a/framework/python/src/test_orc/test_orchestrator.py +++ b/framework/python/src/test_orc/test_orchestrator.py @@ -13,16 +13,14 @@ # limitations under the License. """Provides high level management of the test orchestrator.""" -import getpass import os import json import time import shutil import docker from docker.types import Mount -from common import logger +from common import logger, util from test_orc.module import TestModule -from common import util LOG_NAME = "test_orc" LOGGER = logger.get_logger("test_orc") @@ -61,7 +59,7 @@ def start(self): # Setup the output directory self._host_user = util.get_host_user() os.makedirs(RUNTIME_DIR, exist_ok=True) - util.run_command(f'chown -R {self._host_user} {RUNTIME_DIR}') + util.run_command(f"chown -R {self._host_user} {RUNTIME_DIR}") self._load_test_modules() self.build_test_modules() @@ -102,7 +100,7 @@ def _generate_results(self, device): results[module.name] = module_results except (FileNotFoundError, PermissionError, json.JSONDecodeError) as results_error: - LOGGER.error("Error occured whilst obbtaining results for module " + module.name) + LOGGER.error(f"Error occured whilst obbtaining results for module {module.name}") LOGGER.debug(results_error) out_file = os.path.join( @@ -110,7 +108,7 @@ def _generate_results(self, device): "runtime/test/" + device.mac_addr.replace(":", "") + "/results.json") with open(out_file, "w", encoding="utf-8") as f: json.dump(results, f, indent=2) - util.run_command(f'chown -R {self._host_user} {out_file}') + util.run_command(f"chown -R {self._host_user} {out_file}") return results def test_in_progress(self): @@ -140,18 +138,19 @@ def _run_test_module(self, module, device): container_runtime_dir = os.path.join( self._root_path, "runtime/test/" + device.mac_addr.replace(":", "") + "/" + module.name) - network_runtime_dir = os.path.join(self._root_path, "runtime/network") os.makedirs(container_runtime_dir) + network_runtime_dir = os.path.join(self._root_path, "runtime/network") + device_startup_capture = os.path.join( self._root_path, "runtime/test/" + device.mac_addr.replace(":", "") + "/startup.pcap") - util.run_command(f'chown -R {self._host_user} {device_startup_capture}') + util.run_command(f"chown -R {self._host_user} {device_startup_capture}") device_monitor_capture = os.path.join( self._root_path, "runtime/test/" + device.mac_addr.replace(":", "") + "/monitor.pcap") - util.run_command(f'chown -R {self._host_user} {device_monitor_capture}') + util.run_command(f"chown -R {self._host_user} {device_monitor_capture}") client = docker.from_env() diff --git a/modules/network/dhcp-1/python/src/grpc_server/dhcp_config.py b/modules/network/dhcp-1/python/src/grpc_server/dhcp_config.py index 444faa87c..6f003014c 100644 --- a/modules/network/dhcp-1/python/src/grpc_server/dhcp_config.py +++ b/modules/network/dhcp-1/python/src/grpc_server/dhcp_config.py @@ -18,9 +18,7 @@ LOG_NAME = 'dhcp_config' LOGGER = None - CONFIG_FILE = '/etc/dhcp/dhcpd.conf' - DEFAULT_LEASE_TIME_KEY = 'default-lease-time' @@ -186,13 +184,18 @@ def __str__(self): config += '\tprimary;' if self.primary else 'secondary;' config += '\n\t{ADDRESS_KEY} {ADDRESS};' if self.address is not None else '' config += '\n\t{PORT_KEY} {PORT};' if self.port is not None else '' - config += '\n\t{PEER_ADDRESS_KEY} {PEER_ADDRESS};' if self.peer_address is not None else '' - config += '\n\t{PEER_PORT_KEY} {PEER_PORT};' if self.peer_port is not None else '' - config += '\n\t{MAX_RESPONSE_DELAY_KEY} {MAX_RESPONSE_DELAY};' if self.max_response_delay is not None else '' - config += '\n\t{MAX_UNACKED_UPDATES_KEY} {MAX_UNACKED_UPDATES};' if self.max_unacked_updates is not None else '' + config += ('\n\t{PEER_ADDRESS_KEY} {PEER_ADDRESS};' + if self.peer_address is not None else '') + config += ('\n\t{PEER_PORT_KEY} {PEER_PORT};' + if self.peer_port is not None else '') + config += ('\n\t{MAX_RESPONSE_DELAY_KEY} {MAX_RESPONSE_DELAY};' + if self.max_response_delay is not None else '') + config += ('\n\t{MAX_UNACKED_UPDATES_KEY} {MAX_UNACKED_UPDATES};' + if self.max_unacked_updates is not None else '') config += '\n\t{MCLT_KEY} {MCLT};' if self.mclt is not None else '' config += '\n\t{SPLIT_KEY} {SPLIT};' if self.split is not None else '' - config += '\n\t{LOAD_BALANCE_MAX_SECONDS_KEY} {LOAD_BALANCE_MAX_SECONDS};' if self.load_balance_max_seconds is not None else '' + config += ('\n\t{LOAD_BALANCE_MAX_SECONDS_KEY} {LOAD_BALANCE_MAX_SECONDS};' + if self.load_balance_max_seconds is not None else '') config += '\n\r}}' config = config.format( @@ -220,9 +223,9 @@ def __str__(self): if not self.enabled: lines = config.strip().split('\n') - for i in range(len(lines)-1): + for i in range(len(lines) - 1): lines[i] = '#' + lines[i] - lines[-1] = '#' + lines[-1].strip() # Handle the last line separately + lines[-1] = '#' + lines[-1].strip() # Handle the last line separately config = '\n'.join(lines) return config @@ -302,15 +305,20 @@ def __init__(self, subnet): def __str__(self): config = 'subnet {SUBNET_OPTION} netmask {SUBNET_MASK_OPTION} {{' - config += '\n\t{NTP_OPTION_KEY} {NTP_OPTION};' if self._ntp_servers is not None else '' - config += '\n\t{SUBNET_MASK_OPTION_KEY} {SUBNET_MASK_OPTION};' if self._subnet_mask is not None else '' - config += '\n\t{BROADCAST_OPTION_KEY} {BROADCAST_OPTION};' if self._broadcast is not None else '' - config += '\n\t{ROUTER_OPTION_KEY} {ROUTER_OPTION};' if self._routers is not None else '' - config += '\n\t{DNS_OPTION_KEY} {DNS_OPTION};' if self._dns_servers is not None else '' - config += '\n\t{INTERFACE_KEY} {INTERFACE_OPTION};' if self._interface is not None else '' + config += ('\n\t{NTP_OPTION_KEY} {NTP_OPTION};' + if self._ntp_servers is not None else '') + config += ('\n\t{SUBNET_MASK_OPTION_KEY} {SUBNET_MASK_OPTION};' + if self._subnet_mask is not None else '') + config += ('\n\t{BROADCAST_OPTION_KEY} {BROADCAST_OPTION};' + if self._broadcast is not None else '') + config += ('\n\t{ROUTER_OPTION_KEY} {ROUTER_OPTION};' + if self._routers is not None else '') + config += ('\n\t{DNS_OPTION_KEY} {DNS_OPTION};' + if self._dns_servers is not None else '') + config += ('\n\t{INTERFACE_KEY} {INTERFACE_OPTION};' + if self._interface is not None else '') config += '\n\t{AUTHORITATIVE_KEY};' if self._authoritative else '' - config = config.format(length='multi-line', SUBNET_OPTION=self._subnet, NTP_OPTION_KEY=NTP_OPTION_KEY, @@ -407,8 +415,11 @@ def __init__(self, pool): def __str__(self): config = 'pool {{' - config += '\n\t\t{FAILOVER_KEY} "{FAILOVER}";' if self.failover_peer is not None else '' - config += '\n\t\t{RANGE_KEY} {RANGE_START} {RANGE_END};' if self.range_start is not None and self.range_end is not None else '' + config += ('\n\t\t{FAILOVER_KEY} "{FAILOVER}";' + if self.failover_peer is not None else '') + config += ('\n\t\t{RANGE_KEY} {RANGE_START} {RANGE_END};' + if self.range_start is not None and self.range_end is not None + else '') config += '\n\t}}' config = config.format( diff --git a/modules/network/dhcp-1/python/src/grpc_server/dhcp_config_test.py b/modules/network/dhcp-1/python/src/grpc_server/dhcp_config_test.py index 2cc78403a..a34ff4e31 100644 --- a/modules/network/dhcp-1/python/src/grpc_server/dhcp_config_test.py +++ b/modules/network/dhcp-1/python/src/grpc_server/dhcp_config_test.py @@ -17,87 +17,89 @@ import os CONFIG_FILE = 'conf/dhcpd.conf' - DHCP_CONFIG = None def get_config_file_path(): - dhcp_config = DHCPConfig() - current_dir = os.path.dirname(os.path.abspath(__file__)) - module_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(current_dir)))) - conf_file = os.path.join(module_dir,CONFIG_FILE) - return conf_file + current_dir = os.path.dirname(os.path.abspath(__file__)) + module_dir = os.path.dirname( + os.path.dirname(os.path.dirname(os.path.abspath(current_dir)))) + conf_file = os.path.join(module_dir, CONFIG_FILE) + return conf_file + def get_config(): - dhcp_config = DHCPConfig() - dhcp_config.resolve_config(get_config_file_path()) - return dhcp_config + dhcp_config = DHCPConfig() + dhcp_config.resolve_config(get_config_file_path()) + return dhcp_config + class DHCPConfigTest(unittest.TestCase): - @classmethod - def setUpClass(cls): - # Resolve the config - global DHCP_CONFIG - DHCP_CONFIG = get_config() - - def test_resolve_config(self): - print('Test Resolve Config:\n' + str(DHCP_CONFIG)) - - # Resolve the raw config file - with open(get_config_file_path(),'r') as f: - lines = f.readlines() - - # Get the resolved config as a - conf_parts = str(DHCP_CONFIG).split('\n') - - # dhcpd conf is not picky about spacing so we just - # need to check contents of each line for matching - # to make sure evertying matches - for i in range(len(lines)): - self.assertEqual(lines[i].strip(),conf_parts[i].strip()) - - def test_disable_failover(self): - DHCP_CONFIG.disable_failover() - print('Test Disable Config:\n' + str(DHCP_CONFIG)) - config_lines = str(DHCP_CONFIG._peer).split('\n') - for line in config_lines: - self.assertTrue(line.startswith('#')) - - def test_enable_failover(self): - DHCP_CONFIG.enable_failover() - print('Test Enable Config:\n' + str(DHCP_CONFIG)) - config_lines = str(DHCP_CONFIG._peer).split('\n') - for line in config_lines: - self.assertFalse(line.startswith('#')) - - def test_add_reserved_host(self): - DHCP_CONFIG.add_reserved_host('test','00:11:22:33:44:55','192.168.10.5') - host = DHCP_CONFIG.get_reserved_host('00:11:22:33:44:55') - self.assertIsNotNone(host) - print('AddHostConfig:\n' + str(DHCP_CONFIG)) - - def test_delete_reserved_host(self): - DHCP_CONFIG.delete_reserved_host('00:11:22:33:44:55') - host = DHCP_CONFIG.get_reserved_host('00:11:22:33:44:55') - self.assertIsNone(host) - print('DeleteHostConfig:\n' + str(DHCP_CONFIG)) - - def test_resolve_config_with_hosts(self): - DHCP_CONFIG.add_reserved_host('test','00:11:22:33:44:55','192.168.10.5') - config_with_hosts = DHCPConfig() - config_with_hosts.make(str(DHCP_CONFIG)) - host = config_with_hosts.get_reserved_host('00:11:22:33:44:55') - self.assertIsNotNone(host) - print("ResolveConfigWithHosts:\n" + str(config_with_hosts)) + @classmethod + def setUpClass(cls): + # Resolve the config + global DHCP_CONFIG + DHCP_CONFIG = get_config() + + def test_resolve_config(self): + print('Test Resolve Config:\n' + str(DHCP_CONFIG)) + + # Resolve the raw config file + with open(get_config_file_path(), 'r', encoding='UTF-8') as f: + lines = f.readlines() + + # Get the resolved config as a + conf_parts = str(DHCP_CONFIG).split('\n') + + # dhcpd conf is not picky about spacing so we just + # need to check contents of each line for matching + # to make sure evertying matches + for i in range(len(lines)): + self.assertEqual(lines[i].strip(), conf_parts[i].strip()) + + def test_disable_failover(self): + DHCP_CONFIG.disable_failover() + print('Test Disable Config:\n' + str(DHCP_CONFIG)) + config_lines = str(DHCP_CONFIG._peer).split('\n') + for line in config_lines: + self.assertTrue(line.startswith('#')) + + def test_enable_failover(self): + DHCP_CONFIG.enable_failover() + print('Test Enable Config:\n' + str(DHCP_CONFIG)) + config_lines = str(DHCP_CONFIG._peer).split('\n') + for line in config_lines: + self.assertFalse(line.startswith('#')) + + def test_add_reserved_host(self): + DHCP_CONFIG.add_reserved_host('test', '00:11:22:33:44:55', '192.168.10.5') + host = DHCP_CONFIG.get_reserved_host('00:11:22:33:44:55') + self.assertIsNotNone(host) + print('AddHostConfig:\n' + str(DHCP_CONFIG)) + + def test_delete_reserved_host(self): + DHCP_CONFIG.delete_reserved_host('00:11:22:33:44:55') + host = DHCP_CONFIG.get_reserved_host('00:11:22:33:44:55') + self.assertIsNone(host) + print('DeleteHostConfig:\n' + str(DHCP_CONFIG)) + + def test_resolve_config_with_hosts(self): + DHCP_CONFIG.add_reserved_host('test', '00:11:22:33:44:55', '192.168.10.5') + config_with_hosts = DHCPConfig() + config_with_hosts.make(str(DHCP_CONFIG)) + host = config_with_hosts.get_reserved_host('00:11:22:33:44:55') + self.assertIsNotNone(host) + print('ResolveConfigWithHosts:\n' + str(config_with_hosts)) + if __name__ == '__main__': - suite = unittest.TestSuite() - suite.addTest(DHCPConfigTest('test_resolve_config')) - suite.addTest(DHCPConfigTest('test_disable_failover')) - suite.addTest(DHCPConfigTest('test_enable_failover')) - suite.addTest(DHCPConfigTest('test_add_reserved_host')) - suite.addTest(DHCPConfigTest('test_delete_reserved_host')) - suite.addTest(DHCPConfigTest('test_resolve_config_with_hosts')) - - runner = unittest.TextTestRunner() - runner.run(suite) \ No newline at end of file + suite = unittest.TestSuite() + suite.addTest(DHCPConfigTest('test_resolve_config')) + suite.addTest(DHCPConfigTest('test_disable_failover')) + suite.addTest(DHCPConfigTest('test_enable_failover')) + suite.addTest(DHCPConfigTest('test_add_reserved_host')) + suite.addTest(DHCPConfigTest('test_delete_reserved_host')) + suite.addTest(DHCPConfigTest('test_resolve_config_with_hosts')) + + runner = unittest.TextTestRunner() + runner.run(suite) diff --git a/modules/network/dhcp-1/python/src/grpc_server/dhcp_server.py b/modules/network/dhcp-1/python/src/grpc_server/dhcp_server.py index 2f67b0c2d..5e88d59fe 100644 --- a/modules/network/dhcp-1/python/src/grpc_server/dhcp_server.py +++ b/modules/network/dhcp-1/python/src/grpc_server/dhcp_server.py @@ -13,6 +13,7 @@ # limitations under the License. """Contains all the necessary classes to maintain the DHCP server""" +import sys import time from common import logger from common import util @@ -35,75 +36,74 @@ def __init__(self): self.dhcp_config.resolve_config() def restart(self): - LOGGER.info("Restarting DHCP Server") - isc_started = util.run_command("service isc-dhcp-server restart", False) + LOGGER.info('Restarting DHCP Server') + isc_started = util.run_command('service isc-dhcp-server restart', False) radvd_started = self.radvd.restart() started = isc_started and radvd_started - LOGGER.info("DHCP Restarted: " + str(started)) + LOGGER.info('DHCP Restarted: ' + str(started)) return started def start(self): - LOGGER.info("Starting DHCP Server") - isc_started = util.run_command("service isc-dhcp-server start", False) + LOGGER.info('Starting DHCP Server') + isc_started = util.run_command('service isc-dhcp-server start', False) radvd_started = self.radvd.start() started = isc_started and radvd_started - LOGGER.info("DHCP Started: " + str(started)) + LOGGER.info('DHCP Started: ' + str(started)) return started def stop(self): - LOGGER.info("Stopping DHCP Server") - isc_stopped = util.run_command("service isc-dhcp-server stop", False) + LOGGER.info('Stopping DHCP Server') + isc_stopped = util.run_command('service isc-dhcp-server stop', False) radvd_stopped = self.radvd.stop() stopped = isc_stopped and radvd_stopped - LOGGER.info("DHCP Stopped: " + str(stopped)) + LOGGER.info('DHCP Stopped: ' + str(stopped)) return stopped def is_running(self): - LOGGER.info("Checking DHCP Status") - response = util.run_command("service isc-dhcp-server status") - isc_running = response[0] == 'Status of ISC DHCPv4 server: dhcpd is running.' + LOGGER.info('Checking DHCP Status') + response = util.run_command('service isc-dhcp-server status') + isc_running = response[ + 0] == 'Status of ISC DHCPv4 server: dhcpd is running.' radvd_running = self.radvd.is_running() running = isc_running and radvd_running - LOGGER.info("DHCP Status: " + str(running)) + LOGGER.info('DHCP Status: ' + str(running)) return running def boot(self): - LOGGER.info("Booting DHCP Server") + LOGGER.info('Booting DHCP Server') isc_booted = False radvd_booted = False if self.is_running(): - LOGGER.info("Stopping isc-dhcp-server") + LOGGER.info('Stopping isc-dhcp-server') stopped = self.stop() - LOGGER.info("isc-dhcp-server stopped: " + str(stopped)) + LOGGER.info('isc-dhcp-server stopped: ' + str(stopped)) if self.radvd.is_running(): - LOGGER.info("Stopping RADVD") + LOGGER.info('Stopping RADVD') stopped = self.radvd.stop() - LOGGER.info("radvd stopped: " + str(stopped)) + LOGGER.info('radvd stopped: ' + str(stopped)) - LOGGER.info("Starting isc-dhcp-server") + LOGGER.info('Starting isc-dhcp-server') if self.start(): isc_booted = False # Scan for 5 seconds if not yet ready - for i in range(5): + for _ in range(5): time.sleep(1) isc_booted = self.is_running() if isc_booted: - break; - LOGGER.info("isc-dhcp-server started: " + str(isc_booted)) + break + LOGGER.info('isc-dhcp-server started: ' + str(isc_booted)) - LOGGER.info("Starting RADVD") + LOGGER.info('Starting RADVD') if self.radvd.start(): radvd_booted = False # Scan for 5 seconds if not yet ready - for i in range(5): + for _ in range(5): time.sleep(1) radvd_booted = self.radvd.is_running() if radvd_booted: - break; - LOGGER.info("RADVD started: " + str(radvd_booted)) - - + break + LOGGER.info('RADVD started: ' + str(radvd_booted)) return isc_booted and radvd_booted @@ -117,14 +117,14 @@ def run(): config = str(dhcp_server.dhcp_config) while True: - dhcp_server.dhcp_config.resolve_config() - new_config = str(dhcp_server.dhcp_config) - if config != new_config: - LOGGER.info("DHCP Config Changed") - config = new_config - success = dhcp_server.restart() - success = dhcp_server.radvd.restart() - time.sleep(1) + dhcp_server.dhcp_config.resolve_config() + new_config = str(dhcp_server.dhcp_config) + if config != new_config: + LOGGER.info('DHCP Config Changed') + config = new_config + dhcp_server.restart() + dhcp_server.radvd.restart() + time.sleep(1) if __name__ == '__main__': run() diff --git a/modules/network/dhcp-1/python/src/grpc_server/network_service.py b/modules/network/dhcp-1/python/src/grpc_server/network_service.py index a693ac3a1..043ca49b3 100644 --- a/modules/network/dhcp-1/python/src/grpc_server/network_service.py +++ b/modules/network/dhcp-1/python/src/grpc_server/network_service.py @@ -47,7 +47,7 @@ def RestartDHCPServer(self, request, context): # pylint: disable=W0613 started = self._dhcp_server.restart() LOGGER.info('DHCP server restarted: ' + (str(started))) return pb2.Response(code=200, message='{}') - except Exception as e: # pylint: disable=W0718 + except Exception as e: # pylint: disable=W0718 fail_message = 'Failed to restart DHCP server: ' + str(e) LOGGER.error(fail_message) LOGGER.error(traceback.format_exc()) @@ -59,7 +59,7 @@ def StartDHCPServer(self, request, context): # pylint: disable=W0613 started = self._dhcp_server.start() LOGGER.info('DHCP server started: ' + (str(started))) return pb2.Response(code=200, message='{}') - except Exception as e: # pylint: disable=W0718 + except Exception as e: # pylint: disable=W0718 fail_message = 'Failed to start DHCP server: ' + str(e) LOGGER.error(fail_message) LOGGER.error(traceback.format_exc()) @@ -71,12 +71,12 @@ def StopDHCPServer(self, request, context): # pylint: disable=W0613 stopped = self._dhcp_server.stop() LOGGER.info('DHCP server stopped: ' + (str(stopped))) return pb2.Response(code=200, message='{}') - except Exception as e: # pylint: disable=W0718 + except Exception as e: # pylint: disable=W0718 fail_message = 'Failed to stop DHCP server: ' + str(e) LOGGER.error(fail_message) LOGGER.error(traceback.format_exc()) return pb2.Response(code=500, message=fail_message) - + def AddReservedLease(self, request, context): # pylint: disable=W0613 LOGGER.info('Add reserved lease called') try: @@ -86,7 +86,7 @@ def AddReservedLease(self, request, context): # pylint: disable=W0613 dhcp_config.write_config() LOGGER.info('Reserved lease added') return pb2.Response(code=200, message='{}') - except Exception as e: # pylint: disable=W0718 + except Exception as e: # pylint: disable=W0718 fail_message = 'Failed to add reserved lease: ' + str(e) LOGGER.error(fail_message) LOGGER.error(traceback.format_exc()) @@ -100,7 +100,7 @@ def DeleteReservedLease(self, request, context): # pylint: disable=W0613 dhcp_config.write_config() LOGGER.info('Reserved lease deleted') return pb2.Response(code=200, message='{}') - except Exception as e: # pylint: disable=W0718 + except Exception as e: # pylint: disable=W0718 fail_message = 'Failed to delete reserved lease: ' + str(e) LOGGER.error(fail_message) LOGGER.error(traceback.format_exc()) @@ -114,7 +114,7 @@ def DisableFailover(self, request, contest): # pylint: disable=W0613 dhcp_config.write_config() LOGGER.info('Failover disabled') return pb2.Response(code=200, message='{}') - except Exception as e: # pylint: disable=W0718 + except Exception as e: # pylint: disable=W0718 fail_message = 'Failed to disable failover: ' + str(e) LOGGER.error(fail_message) LOGGER.error(traceback.format_exc()) @@ -128,7 +128,7 @@ def EnableFailover(self, request, contest): # pylint: disable=W0613 dhcp_config.write_config() LOGGER.info('Failover enabled') return pb2.Response(code=200, message='{}') - except Exception as e: # pylint: disable=W0718 + except Exception as e: # pylint: disable=W0718 fail_message = 'Failed to enable failover: ' + str(e) LOGGER.error(fail_message) LOGGER.error(traceback.format_exc()) @@ -143,7 +143,7 @@ def GetDHCPRange(self, request, context): # pylint: disable=W0613 try: pool = self._get_dhcp_config()._subnets[0].pools[0] return pb2.DHCPRange(code=200, start=pool.range_start, end=pool.range_end) - except Exception as e: # pylint: disable=W0718 + except Exception as e: # pylint: disable=W0718 fail_message = 'Failed to get DHCP range: ' + str(e) LOGGER.error(fail_message) LOGGER.error(traceback.format_exc()) @@ -161,7 +161,7 @@ def GetLease(self, request, context): # pylint: disable=W0613 return pb2.Response(code=200, message=str(lease)) else: return pb2.Response(code=200, message='{}') - except Exception as e: # pylint: disable=W0718 + except Exception as e: # pylint: disable=W0718 fail_message = 'Failed to get lease: ' + str(e) LOGGER.error(fail_message) LOGGER.error(traceback.format_exc()) @@ -179,7 +179,7 @@ def SetDHCPRange(self, request, context): # pylint: disable=W0613 dhcp_config.write_config() LOGGER.info('DHCP range set') return pb2.Response(code=200, message='DHCP Range Set') - except Exception as e: # pylint: disable=W0718 + except Exception as e: # pylint: disable=W0718 fail_message = 'Failed to set DHCP range: ' + str(e) LOGGER.error(fail_message) LOGGER.error(traceback.format_exc()) @@ -191,4 +191,4 @@ def GetStatus(self, request, context): # pylint: disable=W0613 """ dhcp_status = self._dhcp_server.is_running() message = str({'dhcpStatus': dhcp_status}) - return pb2.Response(code=200, message=message) \ No newline at end of file + return pb2.Response(code=200, message=message) diff --git a/modules/network/dhcp-1/python/src/grpc_server/radvd_server.py b/modules/network/dhcp-1/python/src/grpc_server/radvd_server.py index 48e063e61..8bb1d0539 100644 --- a/modules/network/dhcp-1/python/src/grpc_server/radvd_server.py +++ b/modules/network/dhcp-1/python/src/grpc_server/radvd_server.py @@ -13,10 +13,8 @@ # limitations under the License. """Contains all the necessary classes to maintain the DHCP server""" -import time from common import logger from common import util -from dhcp_config import DHCPConfig CONFIG_FILE = '/etc/dhcp/dhcpd.conf' LOG_NAME = 'radvd' @@ -31,25 +29,25 @@ def __init__(self): LOGGER = logger.get_logger(LOG_NAME, 'dhcp-1') def restart(self): - LOGGER.info("Restarting RADVD Server") - response = util.run_command("radvd-service restart", False) - LOGGER.info("RADVD Restarted: " + str(response)) + LOGGER.info('Restarting RADVD Server') + response = util.run_command('radvd-service restart', False) + LOGGER.info('RADVD Restarted: ' + str(response)) return response def start(self): - LOGGER.info("Starting RADVD Server") - response = util.run_command("radvd-service start", False) - LOGGER.info("RADVD Started: " + str(response)) + LOGGER.info('Starting RADVD Server') + response = util.run_command('radvd-service start', False) + LOGGER.info('RADVD Started: ' + str(response)) return response def stop(self): - LOGGER.info("Stopping RADVD Server") - response = util.run_command("radvd-service stop", False) - LOGGER.info("RADVD Stopped: " + str(response)) + LOGGER.info('Stopping RADVD Server') + response = util.run_command('radvd-service stop', False) + LOGGER.info('RADVD Stopped: ' + str(response)) return response def is_running(self): - LOGGER.info("Checking RADVD Status") - response = util.run_command("radvd-service status") - LOGGER.info("RADVD Status: " + str(response)) + LOGGER.info('Checking RADVD Status') + response = util.run_command('radvd-service status') + LOGGER.info('RADVD Status: ' + str(response)) return response[0] == 'radvd service is running.' diff --git a/modules/network/dhcp-2/python/src/grpc_server/dhcp_config.py b/modules/network/dhcp-2/python/src/grpc_server/dhcp_config.py index 33cb5938c..5da5e4cf2 100644 --- a/modules/network/dhcp-2/python/src/grpc_server/dhcp_config.py +++ b/modules/network/dhcp-2/python/src/grpc_server/dhcp_config.py @@ -186,13 +186,18 @@ def __str__(self): config += '\tprimary;' if self.primary else 'secondary;' config += '\n\t{ADDRESS_KEY} {ADDRESS};' if self.address is not None else '' config += '\n\t{PORT_KEY} {PORT};' if self.port is not None else '' - config += '\n\t{PEER_ADDRESS_KEY} {PEER_ADDRESS};' if self.peer_address is not None else '' - config += '\n\t{PEER_PORT_KEY} {PEER_PORT};' if self.peer_port is not None else '' - config += '\n\t{MAX_RESPONSE_DELAY_KEY} {MAX_RESPONSE_DELAY};' if self.max_response_delay is not None else '' - config += '\n\t{MAX_UNACKED_UPDATES_KEY} {MAX_UNACKED_UPDATES};' if self.max_unacked_updates is not None else '' + config += ('\n\t{PEER_ADDRESS_KEY} {PEER_ADDRESS};' + if self.peer_address is not None else '') + config += ('\n\t{PEER_PORT_KEY} {PEER_PORT};' + if self.peer_port is not None else '') + config += ('\n\t{MAX_RESPONSE_DELAY_KEY} {MAX_RESPONSE_DELAY};' + if self.max_response_delay is not None else '') + config += ('\n\t{MAX_UNACKED_UPDATES_KEY} {MAX_UNACKED_UPDATES};' + if self.max_unacked_updates is not None else '') config += '\n\t{MCLT_KEY} {MCLT};' if self.mclt is not None else '' config += '\n\t{SPLIT_KEY} {SPLIT};' if self.split is not None else '' - config += '\n\t{LOAD_BALANCE_MAX_SECONDS_KEY} {LOAD_BALANCE_MAX_SECONDS};' if self.load_balance_max_seconds is not None else '' + config += ('\n\t{LOAD_BALANCE_MAX_SECONDS_KEY} {LOAD_BALANCE_MAX_SECONDS};' + if self.load_balance_max_seconds is not None else '') config += '\n\r}}' config = config.format( @@ -220,9 +225,9 @@ def __str__(self): if not self.enabled: lines = config.strip().split('\n') - for i in range(len(lines)-1): + for i in range(len(lines) - 1): lines[i] = '#' + lines[i] - lines[-1] = '#' + lines[-1].strip() # Handle the last line separately + lines[-1] = '#' + lines[-1].strip() # Handle the last line separately config = '\n'.join(lines) return config @@ -302,15 +307,20 @@ def __init__(self, subnet): def __str__(self): config = 'subnet {SUBNET_OPTION} netmask {SUBNET_MASK_OPTION} {{' - config += '\n\t{NTP_OPTION_KEY} {NTP_OPTION};' if self._ntp_servers is not None else '' - config += '\n\t{SUBNET_MASK_OPTION_KEY} {SUBNET_MASK_OPTION};' if self._subnet_mask is not None else '' - config += '\n\t{BROADCAST_OPTION_KEY} {BROADCAST_OPTION};' if self._broadcast is not None else '' - config += '\n\t{ROUTER_OPTION_KEY} {ROUTER_OPTION};' if self._routers is not None else '' - config += '\n\t{DNS_OPTION_KEY} {DNS_OPTION};' if self._dns_servers is not None else '' - config += '\n\t{INTERFACE_KEY} {INTERFACE_OPTION};' if self._interface is not None else '' + config += ('\n\t{NTP_OPTION_KEY} {NTP_OPTION};' + if self._ntp_servers is not None else '') + config += ('\n\t{SUBNET_MASK_OPTION_KEY} {SUBNET_MASK_OPTION};' + if self._subnet_mask is not None else '') + config += ('\n\t{BROADCAST_OPTION_KEY} {BROADCAST_OPTION};' + if self._broadcast is not None else '') + config += ('\n\t{ROUTER_OPTION_KEY} {ROUTER_OPTION};' + if self._routers is not None else '') + config += ('\n\t{DNS_OPTION_KEY} {DNS_OPTION};' + if self._dns_servers is not None else '') + config += ('\n\t{INTERFACE_KEY} {INTERFACE_OPTION};' + if self._interface is not None else '') config += '\n\t{AUTHORITATIVE_KEY};' if self._authoritative else '' - config = config.format(length='multi-line', SUBNET_OPTION=self._subnet, NTP_OPTION_KEY=NTP_OPTION_KEY, @@ -407,8 +417,11 @@ def __init__(self, pool): def __str__(self): config = 'pool {{' - config += '\n\t\t{FAILOVER_KEY} "{FAILOVER}";' if self.failover_peer is not None else '' - config += '\n\t\t{RANGE_KEY} {RANGE_START} {RANGE_END};' if self.range_start is not None and self.range_end is not None else '' + config += ('\n\t\t{FAILOVER_KEY} "{FAILOVER}";' + if self.failover_peer is not None else '') + config += ('\n\t\t{RANGE_KEY} {RANGE_START} {RANGE_END};' + if self.range_start is not None and self.range_end is not None + else '') config += '\n\t}}' config = config.format( @@ -490,4 +503,4 @@ def resolve_host(self, reserved_host): self.hw_addr = part.strip().split(HARDWARE_KEY)[1].strip().split(';')[0] elif FIXED_ADDRESS_KEY in part: self.fixed_addr = part.strip().split( - FIXED_ADDRESS_KEY)[1].strip().split(';')[0] \ No newline at end of file + FIXED_ADDRESS_KEY)[1].strip().split(';')[0] diff --git a/modules/network/dhcp-2/python/src/grpc_server/dhcp_config_test.py b/modules/network/dhcp-2/python/src/grpc_server/dhcp_config_test.py index 2cc78403a..b07f57b27 100644 --- a/modules/network/dhcp-2/python/src/grpc_server/dhcp_config_test.py +++ b/modules/network/dhcp-2/python/src/grpc_server/dhcp_config_test.py @@ -17,87 +17,86 @@ import os CONFIG_FILE = 'conf/dhcpd.conf' - DHCP_CONFIG = None def get_config_file_path(): - dhcp_config = DHCPConfig() - current_dir = os.path.dirname(os.path.abspath(__file__)) - module_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(current_dir)))) - conf_file = os.path.join(module_dir,CONFIG_FILE) - return conf_file + current_dir = os.path.dirname(os.path.abspath(__file__)) + module_dir = os.path.dirname( + os.path.dirname(os.path.dirname(os.path.abspath(current_dir)))) + conf_file = os.path.join(module_dir, CONFIG_FILE) + return conf_file def get_config(): - dhcp_config = DHCPConfig() - dhcp_config.resolve_config(get_config_file_path()) - return dhcp_config + dhcp_config = DHCPConfig() + dhcp_config.resolve_config(get_config_file_path()) + return dhcp_config class DHCPConfigTest(unittest.TestCase): - @classmethod - def setUpClass(cls): - # Resolve the config - global DHCP_CONFIG - DHCP_CONFIG = get_config() - - def test_resolve_config(self): - print('Test Resolve Config:\n' + str(DHCP_CONFIG)) - - # Resolve the raw config file - with open(get_config_file_path(),'r') as f: - lines = f.readlines() - - # Get the resolved config as a - conf_parts = str(DHCP_CONFIG).split('\n') - - # dhcpd conf is not picky about spacing so we just - # need to check contents of each line for matching - # to make sure evertying matches - for i in range(len(lines)): - self.assertEqual(lines[i].strip(),conf_parts[i].strip()) - - def test_disable_failover(self): - DHCP_CONFIG.disable_failover() - print('Test Disable Config:\n' + str(DHCP_CONFIG)) - config_lines = str(DHCP_CONFIG._peer).split('\n') - for line in config_lines: - self.assertTrue(line.startswith('#')) - - def test_enable_failover(self): - DHCP_CONFIG.enable_failover() - print('Test Enable Config:\n' + str(DHCP_CONFIG)) - config_lines = str(DHCP_CONFIG._peer).split('\n') - for line in config_lines: - self.assertFalse(line.startswith('#')) - - def test_add_reserved_host(self): - DHCP_CONFIG.add_reserved_host('test','00:11:22:33:44:55','192.168.10.5') - host = DHCP_CONFIG.get_reserved_host('00:11:22:33:44:55') - self.assertIsNotNone(host) - print('AddHostConfig:\n' + str(DHCP_CONFIG)) - - def test_delete_reserved_host(self): - DHCP_CONFIG.delete_reserved_host('00:11:22:33:44:55') - host = DHCP_CONFIG.get_reserved_host('00:11:22:33:44:55') - self.assertIsNone(host) - print('DeleteHostConfig:\n' + str(DHCP_CONFIG)) - - def test_resolve_config_with_hosts(self): - DHCP_CONFIG.add_reserved_host('test','00:11:22:33:44:55','192.168.10.5') - config_with_hosts = DHCPConfig() - config_with_hosts.make(str(DHCP_CONFIG)) - host = config_with_hosts.get_reserved_host('00:11:22:33:44:55') - self.assertIsNotNone(host) - print("ResolveConfigWithHosts:\n" + str(config_with_hosts)) + @classmethod + def setUpClass(cls): + # Resolve the config + global DHCP_CONFIG + DHCP_CONFIG = get_config() + + def test_resolve_config(self): + print('Test Resolve Config:\n' + str(DHCP_CONFIG)) + + # Resolve the raw config file + with open(get_config_file_path(), 'r', encoding='UTF-8') as f: + lines = f.readlines() + + # Get the resolved config as a + conf_parts = str(DHCP_CONFIG).split('\n') + + # dhcpd conf is not picky about spacing so we just + # need to check contents of each line for matching + # to make sure evertying matches + for i in range(len(lines)): + self.assertEqual(lines[i].strip(), conf_parts[i].strip()) + + def test_disable_failover(self): + DHCP_CONFIG.disable_failover() + print('Test Disable Config:\n' + str(DHCP_CONFIG)) + config_lines = str(DHCP_CONFIG._peer).split('\n') + for line in config_lines: + self.assertTrue(line.startswith('#')) + + def test_enable_failover(self): + DHCP_CONFIG.enable_failover() + print('Test Enable Config:\n' + str(DHCP_CONFIG)) + config_lines = str(DHCP_CONFIG._peer).split('\n') + for line in config_lines: + self.assertFalse(line.startswith('#')) + + def test_add_reserved_host(self): + DHCP_CONFIG.add_reserved_host('test', '00:11:22:33:44:55', '192.168.10.5') + host = DHCP_CONFIG.get_reserved_host('00:11:22:33:44:55') + self.assertIsNotNone(host) + print('AddHostConfig:\n' + str(DHCP_CONFIG)) + + def test_delete_reserved_host(self): + DHCP_CONFIG.delete_reserved_host('00:11:22:33:44:55') + host = DHCP_CONFIG.get_reserved_host('00:11:22:33:44:55') + self.assertIsNone(host) + print('DeleteHostConfig:\n' + str(DHCP_CONFIG)) + + def test_resolve_config_with_hosts(self): + DHCP_CONFIG.add_reserved_host('test', '00:11:22:33:44:55', '192.168.10.5') + config_with_hosts = DHCPConfig() + config_with_hosts.make(str(DHCP_CONFIG)) + host = config_with_hosts.get_reserved_host('00:11:22:33:44:55') + self.assertIsNotNone(host) + print('ResolveConfigWithHosts:\n' + str(config_with_hosts)) if __name__ == '__main__': - suite = unittest.TestSuite() - suite.addTest(DHCPConfigTest('test_resolve_config')) - suite.addTest(DHCPConfigTest('test_disable_failover')) - suite.addTest(DHCPConfigTest('test_enable_failover')) - suite.addTest(DHCPConfigTest('test_add_reserved_host')) - suite.addTest(DHCPConfigTest('test_delete_reserved_host')) - suite.addTest(DHCPConfigTest('test_resolve_config_with_hosts')) - - runner = unittest.TextTestRunner() - runner.run(suite) \ No newline at end of file + suite = unittest.TestSuite() + suite.addTest(DHCPConfigTest('test_resolve_config')) + suite.addTest(DHCPConfigTest('test_disable_failover')) + suite.addTest(DHCPConfigTest('test_enable_failover')) + suite.addTest(DHCPConfigTest('test_add_reserved_host')) + suite.addTest(DHCPConfigTest('test_delete_reserved_host')) + suite.addTest(DHCPConfigTest('test_resolve_config_with_hosts')) + + runner = unittest.TextTestRunner() + runner.run(suite) diff --git a/modules/network/dhcp-2/python/src/grpc_server/dhcp_server.py b/modules/network/dhcp-2/python/src/grpc_server/dhcp_server.py index 1431d6ddd..67a31c2cb 100644 --- a/modules/network/dhcp-2/python/src/grpc_server/dhcp_server.py +++ b/modules/network/dhcp-2/python/src/grpc_server/dhcp_server.py @@ -13,6 +13,7 @@ # limitations under the License. """Contains all the necessary classes to maintain the DHCP server""" +import sys import time from common import logger from common import util @@ -35,75 +36,74 @@ def __init__(self): self.dhcp_config.resolve_config() def restart(self): - LOGGER.info("Restarting DHCP Server") - isc_started = util.run_command("service isc-dhcp-server restart", False) + LOGGER.info('Restarting DHCP Server') + isc_started = util.run_command('service isc-dhcp-server restart', False) radvd_started = self.radvd.restart() started = isc_started and radvd_started - LOGGER.info("DHCP Restarted: " + str(started)) + LOGGER.info('DHCP Restarted: ' + str(started)) return started def start(self): - LOGGER.info("Starting DHCP Server") - isc_started = util.run_command("service isc-dhcp-server start", False) + LOGGER.info('Starting DHCP Server') + isc_started = util.run_command('service isc-dhcp-server start', False) radvd_started = self.radvd.start() started = isc_started and radvd_started - LOGGER.info("DHCP Started: " + str(started)) + LOGGER.info('DHCP Started: ' + str(started)) return started def stop(self): - LOGGER.info("Stopping DHCP Server") - isc_stopped = util.run_command("service isc-dhcp-server stop", False) + LOGGER.info('Stopping DHCP Server') + isc_stopped = util.run_command('service isc-dhcp-server stop', False) radvd_stopped = self.radvd.stop() stopped = isc_stopped and radvd_stopped - LOGGER.info("DHCP Stopped: " + str(stopped)) + LOGGER.info('DHCP Stopped: ' + str(stopped)) return stopped def is_running(self): - LOGGER.info("Checking DHCP Status") - response = util.run_command("service isc-dhcp-server status") - isc_running = response[0] == 'Status of ISC DHCPv4 server: dhcpd is running.' + LOGGER.info('Checking DHCP Status') + response = util.run_command('service isc-dhcp-server status') + isc_running = response[ + 0] == 'Status of ISC DHCPv4 server: dhcpd is running.' radvd_running = self.radvd.is_running() running = isc_running and radvd_running - LOGGER.info("DHCP Status: " + str(running)) + LOGGER.info('DHCP Status: ' + str(running)) return running def boot(self): - LOGGER.info("Booting DHCP Server") + LOGGER.info('Booting DHCP Server') isc_booted = False radvd_booted = False if self.is_running(): - LOGGER.info("Stopping isc-dhcp-server") + LOGGER.info('Stopping isc-dhcp-server') stopped = self.stop() - LOGGER.info("isc-dhcp-server stopped: " + str(stopped)) + LOGGER.info('isc-dhcp-server stopped: ' + str(stopped)) if self.radvd.is_running(): - LOGGER.info("Stopping RADVD") + LOGGER.info('Stopping RADVD') stopped = self.radvd.stop() - LOGGER.info("radvd stopped: " + str(stopped)) + LOGGER.info('radvd stopped: ' + str(stopped)) - LOGGER.info("Starting isc-dhcp-server") + LOGGER.info('Starting isc-dhcp-server') if self.start(): isc_booted = False # Scan for 5 seconds if not yet ready - for i in range(5): + for _ in range(5): time.sleep(1) isc_booted = self.is_running() if isc_booted: - break; - LOGGER.info("isc-dhcp-server started: " + str(isc_booted)) + break + LOGGER.info('isc-dhcp-server started: ' + str(isc_booted)) - LOGGER.info("Starting RADVD") + LOGGER.info('Starting RADVD') if self.radvd.start(): radvd_booted = False # Scan for 5 seconds if not yet ready - for i in range(5): + for _ in range(5): time.sleep(1) radvd_booted = self.radvd.is_running() if radvd_booted: - break; - LOGGER.info("RADVD started: " + str(radvd_booted)) - - + break + LOGGER.info('RADVD started: ' + str(radvd_booted)) return isc_booted and radvd_booted @@ -117,14 +117,14 @@ def run(): config = str(dhcp_server.dhcp_config) while True: - dhcp_server.dhcp_config.resolve_config() - new_config = str(dhcp_server.dhcp_config) - if config != new_config: - LOGGER.info("DHCP Config Changed") - config = new_config - success = dhcp_server.restart() - success = dhcp_server.radvd.restart() - time.sleep(1) + dhcp_server.dhcp_config.resolve_config() + new_config = str(dhcp_server.dhcp_config) + if config != new_config: + LOGGER.info('DHCP Config Changed') + config = new_config + dhcp_server.restart() + dhcp_server.radvd.restart() + time.sleep(1) if __name__ == '__main__': run() diff --git a/modules/network/dhcp-2/python/src/grpc_server/network_service.py b/modules/network/dhcp-2/python/src/grpc_server/network_service.py index 5af9e6c44..f9deba965 100644 --- a/modules/network/dhcp-2/python/src/grpc_server/network_service.py +++ b/modules/network/dhcp-2/python/src/grpc_server/network_service.py @@ -25,6 +25,7 @@ LOG_NAME = 'network_service' LOGGER = None + class NetworkService(pb2_grpc.NetworkModule): """gRPC endpoints for the DHCP Server""" @@ -47,7 +48,7 @@ def RestartDHCPServer(self, request, context): # pylint: disable=W0613 started = self._dhcp_server.restart() LOGGER.info('DHCP server restarted: ' + (str(started))) return pb2.Response(code=200, message='{}') - except Exception as e: # pylint: disable=W0718 + except Exception as e: # pylint: disable=W0718 fail_message = 'Failed to restart DHCP server: ' + str(e) LOGGER.error(fail_message) LOGGER.error(traceback.format_exc()) @@ -59,7 +60,7 @@ def StartDHCPServer(self, request, context): # pylint: disable=W0613 started = self._dhcp_server.start() LOGGER.info('DHCP server started: ' + (str(started))) return pb2.Response(code=200, message='{}') - except Exception as e: # pylint: disable=W0718 + except Exception as e: # pylint: disable=W0718 fail_message = 'Failed to start DHCP server: ' + str(e) LOGGER.error(fail_message) LOGGER.error(traceback.format_exc()) @@ -71,12 +72,12 @@ def StopDHCPServer(self, request, context): # pylint: disable=W0613 stopped = self._dhcp_server.stop() LOGGER.info('DHCP server stopped: ' + (str(stopped))) return pb2.Response(code=200, message='{}') - except Exception as e: # pylint: disable=W0718 + except Exception as e: # pylint: disable=W0718 fail_message = 'Failed to stop DHCP server: ' + str(e) LOGGER.error(fail_message) LOGGER.error(traceback.format_exc()) return pb2.Response(code=500, message=fail_message) - + def AddReservedLease(self, request, context): # pylint: disable=W0613 LOGGER.info('Add reserved lease called') try: @@ -86,7 +87,7 @@ def AddReservedLease(self, request, context): # pylint: disable=W0613 dhcp_config.write_config() LOGGER.info('Reserved lease added') return pb2.Response(code=200, message='{}') - except Exception as e: # pylint: disable=W0718 + except Exception as e: # pylint: disable=W0718 fail_message = 'Failed to add reserved lease: ' + str(e) LOGGER.error(fail_message) LOGGER.error(traceback.format_exc()) @@ -100,7 +101,7 @@ def DeleteReservedLease(self, request, context): # pylint: disable=W0613 dhcp_config.write_config() LOGGER.info('Reserved lease deleted') return pb2.Response(code=200, message='{}') - except Exception as e: # pylint: disable=W0718 + except Exception as e: # pylint: disable=W0718 fail_message = 'Failed to delete reserved lease: ' + str(e) LOGGER.error(fail_message) LOGGER.error(traceback.format_exc()) @@ -114,7 +115,7 @@ def DisableFailover(self, request, contest): # pylint: disable=W0613 dhcp_config.write_config() LOGGER.info('Failover disabled') return pb2.Response(code=200, message='{}') - except Exception as e: # pylint: disable=W0718 + except Exception as e: # pylint: disable=W0718 fail_message = 'Failed to disable failover: ' + str(e) LOGGER.error(fail_message) LOGGER.error(traceback.format_exc()) @@ -128,7 +129,7 @@ def EnableFailover(self, request, contest): # pylint: disable=W0613 dhcp_config.write_config() LOGGER.info('Failover enabled') return pb2.Response(code=200, message='{}') - except Exception as e: # pylint: disable=W0718 + except Exception as e: # pylint: disable=W0718 fail_message = 'Failed to enable failover: ' + str(e) LOGGER.error(fail_message) LOGGER.error(traceback.format_exc()) @@ -143,7 +144,7 @@ def GetDHCPRange(self, request, context): # pylint: disable=W0613 try: pool = self._get_dhcp_config()._subnets[0].pools[0] return pb2.DHCPRange(code=200, start=pool.range_start, end=pool.range_end) - except Exception as e: # pylint: disable=W0718 + except Exception as e: # pylint: disable=W0718 fail_message = 'Failed to get DHCP range: ' + str(e) LOGGER.error(fail_message) LOGGER.error(traceback.format_exc()) @@ -161,7 +162,7 @@ def GetLease(self, request, context): # pylint: disable=W0613 return pb2.Response(code=200, message=str(lease)) else: return pb2.Response(code=200, message='{}') - except Exception as e: # pylint: disable=W0718 + except Exception as e: # pylint: disable=W0718 fail_message = 'Failed to get lease: ' + str(e) LOGGER.error(fail_message) LOGGER.error(traceback.format_exc()) @@ -179,7 +180,7 @@ def SetDHCPRange(self, request, context): # pylint: disable=W0613 dhcp_config.write_config() LOGGER.info('DHCP range set') return pb2.Response(code=200, message='DHCP Range Set') - except Exception as e: # pylint: disable=W0718 + except Exception as e: # pylint: disable=W0718 fail_message = 'Failed to set DHCP range: ' + str(e) LOGGER.error(fail_message) LOGGER.error(traceback.format_exc()) @@ -191,4 +192,4 @@ def GetStatus(self, request, context): # pylint: disable=W0613 """ dhcp_status = self._dhcp_server.is_running() message = str({'dhcpStatus': dhcp_status}) - return pb2.Response(code=200, message=message) \ No newline at end of file + return pb2.Response(code=200, message=message) diff --git a/modules/network/dhcp-2/python/src/grpc_server/radvd_server.py b/modules/network/dhcp-2/python/src/grpc_server/radvd_server.py index 0c6ef90d6..bc5d8b55f 100644 --- a/modules/network/dhcp-2/python/src/grpc_server/radvd_server.py +++ b/modules/network/dhcp-2/python/src/grpc_server/radvd_server.py @@ -13,10 +13,8 @@ # limitations under the License. """Contains all the necessary classes to maintain the DHCP server""" -import time from common import logger from common import util -from dhcp_config import DHCPConfig CONFIG_FILE = '/etc/dhcp/dhcpd.conf' LOG_NAME = 'radvd' @@ -31,25 +29,25 @@ def __init__(self): LOGGER = logger.get_logger(LOG_NAME, 'dhcp-2') def restart(self): - LOGGER.info("Restarting RADVD Server") - response = util.run_command("radvd-service restart", False) - LOGGER.info("RADVD Restarted: " + str(response)) + LOGGER.info('Restarting RADVD Server') + response = util.run_command('radvd-service restart', False) + LOGGER.info('RADVD Restarted: ' + str(response)) return response def start(self): - LOGGER.info("Starting RADVD Server") - response = util.run_command("radvd-service start", False) - LOGGER.info("RADVD Started: " + str(response)) + LOGGER.info('Starting RADVD Server') + response = util.run_command('radvd-service start', False) + LOGGER.info('RADVD Started: ' + str(response)) return response def stop(self): - LOGGER.info("Stopping RADVD Server") - response = util.run_command("radvd-service stop", False) - LOGGER.info("RADVD Stopped: " + str(response)) + LOGGER.info('Stopping RADVD Server') + response = util.run_command('radvd-service stop', False) + LOGGER.info('RADVD Stopped: ' + str(response)) return response def is_running(self): - LOGGER.info("Checking RADVD Status") - response = util.run_command("radvd-service status") - LOGGER.info("RADVD Status: " + str(response)) + LOGGER.info('Checking RADVD Status') + response = util.run_command('radvd-service status') + LOGGER.info('RADVD Status: ' + str(response)) return response[0] == 'radvd service is running.' diff --git a/modules/test/base/python/src/test_module.py b/modules/test/base/python/src/test_module.py index 5342e36f8..2a892b810 100644 --- a/modules/test/base/python/src/test_module.py +++ b/modules/test/base/python/src/test_module.py @@ -89,14 +89,13 @@ def run_tests(self): else: result = getattr(self, test_method_name)() else: - LOGGER.info('Test ' + test['name'] + ' not resolved. Skipping') + LOGGER.info(f'Test {test["name"]} not resolved. Skipping') result = None else: - LOGGER.info('Test ' + test['name'] + ' disabled. Skipping') + LOGGER.info(f'Test {test["name"]} disabled. Skipping') if result is not None: - success = None - if isinstance(result,bool): - test['result'] = 'compliant' if result else 'non-compliant' + if isinstance(result, bool): + test['result'] = 'compliant' if result else 'non-compliant' else: test['result'] = 'compliant' if result[0] else 'non-compliant' test['result_details'] = result[1] diff --git a/modules/test/conn/python/src/connection_module.py b/modules/test/conn/python/src/connection_module.py index 5b3bf7038..b4635ffb8 100644 --- a/modules/test/conn/python/src/connection_module.py +++ b/modules/test/conn/python/src/connection_module.py @@ -11,17 +11,17 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. - """Connection test module""" import util import sys -from scapy.all import * +import json +from scapy.all import rdpcap, DHCP, Ether from test_module import TestModule from dhcp1.client import Client as DHCPClient1 -LOG_NAME = "test_connection" +LOG_NAME = 'test_connection' LOGGER = None -OUI_FILE="/usr/local/etc/oui.txt" +OUI_FILE = '/usr/local/etc/oui.txt' DHCP_SERVER_CAPTURE_FILE = '/runtime/network/dhcp-1.pcap' STARTUP_CAPTURE_FILE = '/runtime/device/startup.pcap' MONITOR_CAPTURE_FILE = '/runtime/device/monitor.pcap' @@ -35,10 +35,11 @@ def __init__(self, module): global LOGGER LOGGER = self._get_logger() self.dhcp1_client = DHCPClient1() - - # ToDo: Move this into some level of testing, leave for + + # ToDo: Move this into some level of testing, leave for # reference until tests are implemented with these calls - # response = self.dhcp1_client.add_reserved_lease('test','00:11:22:33:44:55','10.10.10.21') + # response = self.dhcp1_client.add_reserved_lease( + # 'test','00:11:22:33:44:55','10.10.10.21') # print("AddLeaseResp: " + str(response)) # response = self.dhcp1_client.delete_reserved_lease('00:11:22:33:44:55') @@ -63,52 +64,52 @@ def __init__(self, module): # print("Set Range: " + str(response)) def _connection_dhcp_address(self): - LOGGER.info("Running connection.dhcp_address") + LOGGER.info('Running connection.dhcp_address') response = self.dhcp1_client.get_lease(self._device_mac) - LOGGER.info("DHCP Lease resolved:\n" + str(response)) + LOGGER.info('DHCP Lease resolved:\n' + str(response)) if response.code == 200: - lease = eval(response.message) + lease = eval(response.message) # pylint: disable=E0203 if 'ip' in lease: ip_addr = lease['ip'] - LOGGER.info("IP Resolved: " + ip_addr) - LOGGER.info("Attempting to ping device..."); + LOGGER.info('IP Resolved: ' + ip_addr) + LOGGER.info('Attempting to ping device...') ping_success = self._ping(self._device_ipv4_addr) - LOGGER.info("Ping Success: " + str(ping_success)) + LOGGER.info('Ping Success: ' + str(ping_success)) if ping_success: - return True, "Device responded to leased ip address" + return True, 'Device responded to leased ip address' else: - return False, "Device did not respond to leased ip address" + return False, 'Device did not respond to leased ip address' else: - LOGGER.info("No DHCP lease found for: " + self._device_mac) - return False, "No DHCP lease found for: " + self._device_mac + LOGGER.info('No DHCP lease found for: ' + self._device_mac) + return False, 'No DHCP lease found for: ' + self._device_mac def _connection_mac_address(self): - LOGGER.info("Running connection.mac_address") + LOGGER.info('Running connection.mac_address') if self._device_mac is not None: - LOGGER.info("MAC address found: " + self._device_mac) - return True, "MAC address found: " + self._device_mac + LOGGER.info('MAC address found: ' + self._device_mac) + return True, 'MAC address found: ' + self._device_mac else: - LOGGER.info("No MAC address found: " + self._device_mac) - return False, "No MAC address found." + LOGGER.info('No MAC address found: ' + self._device_mac) + return False, 'No MAC address found.' def _connection_mac_oui(self): - LOGGER.info("Running connection.mac_oui") + LOGGER.info('Running connection.mac_oui') manufacturer = self._get_oui_manufacturer(self._device_mac) if manufacturer is not None: - LOGGER.info("OUI Manufacturer found: " + manufacturer) - return True, "OUI Manufacturer found: " + manufacturer + LOGGER.info('OUI Manufacturer found: ' + manufacturer) + return True, 'OUI Manufacturer found: ' + manufacturer else: - LOGGER.info("No OUI Manufacturer found for: " + self._device_mac) - return False, "No OUI Manufacturer found for: " + self._device_mac + LOGGER.info('No OUI Manufacturer found for: ' + self._device_mac) + return False, 'No OUI Manufacturer found for: ' + self._device_mac def _connection_single_ip(self): - LOGGER.info("Running connection.single_ip") + LOGGER.info('Running connection.single_ip') result = None if self._device_mac is None: - LOGGER.info("No MAC address found: ") - return result, "No MAC address found." - + LOGGER.info('No MAC address found: ') + return result, 'No MAC address found.' + # Read all the pcap files containing DHCP packet information packets = rdpcap(DHCP_SERVER_CAPTURE_FILE) packets.append(rdpcap(STARTUP_CAPTURE_FILE)) @@ -116,50 +117,48 @@ def _connection_single_ip(self): # Extract MAC addresses from DHCP packets mac_addresses = set() - LOGGER.info("Inspecting: " + str(len(packets)) + " packets") + LOGGER.info('Inspecting: ' + str(len(packets)) + ' packets') for packet in packets: # Option[1] = message-type, option 3 = DHCPREQUEST - if DHCP in packet and packet[DHCP].options[0][1] == 3: - mac_address = packet[Ether].src - mac_addresses.add(mac_address.upper()) + if DHCP in packet and packet[DHCP].options[0][1] == 3: + mac_address = packet[Ether].src + mac_addresses.add(mac_address.upper()) # Check if the device mac address is in the list of DHCPREQUESTs result = self._device_mac.upper() in mac_addresses - LOGGER.info("DHCPREQUEST detected from device: " + str(result)) + LOGGER.info('DHCPREQUEST detected from device: ' + str(result)) # Check the unique MAC addresses to see if they match the device for mac_address in mac_addresses: - LOGGER.info("DHCPREQUEST from MAC address: " + mac_address) - result &= self._device_mac.upper() == mac_address + LOGGER.info('DHCPREQUEST from MAC address: ' + mac_address) + result &= self._device_mac.upper() == mac_address return result - def _connection_target_ping(self): - LOGGER.info("Running connection.target_ping") + LOGGER.info('Running connection.target_ping') # If the ipv4 address wasn't resolved yet, try again if self._device_ipv4_addr is None: - self._device_ipv4_addr = self._get_device_ipv4(self) + self._device_ipv4_addr = self._get_device_ipv4(self) if self._device_ipv4_addr is None: - LOGGER.error("No device IP could be resolved") + LOGGER.error('No device IP could be resolved') sys.exit(1) else: return self._ping(self._device_ipv4_addr) - def _get_oui_manufacturer(self,mac_address): + def _get_oui_manufacturer(self, mac_address): # Do some quick fixes on the format of the mac_address # to match the oui file pattern - mac_address = mac_address.replace(":","-").upper() - with open(OUI_FILE, "r") as file: - for line in file: - if mac_address.startswith(line[:8]): - start = line.index("(hex)") + len("(hex)") - return line[start:].strip() # Extract the company name + mac_address = mac_address.replace(':', '-').upper() + with open(OUI_FILE, 'r', encoding='UTF-8') as file: + for line in file: + if mac_address.startswith(line[:8]): + start = line.index('(hex)') + len('(hex)') + return line[start:].strip() # Extract the company name return None def _ping(self, host): cmd = 'ping -c 1 ' + str(host) success = util.run_command(cmd, output=False) return success - \ No newline at end of file diff --git a/modules/test/nmap/nmap.Dockerfile b/modules/test/nmap/nmap.Dockerfile index 1789da382..ea90ee06f 100644 --- a/modules/test/nmap/nmap.Dockerfile +++ b/modules/test/nmap/nmap.Dockerfile @@ -18,10 +18,10 @@ FROM test-run/base-test:latest ARG MODULE_NAME=nmap ARG MODULE_DIR=modules/test/$MODULE_NAME -#Load the requirements file +# Load the requirements file COPY $MODULE_DIR/python/requirements.txt /testrun/python -#Install all python requirements for the module +# Install all python requirements for the module RUN pip3 install -r /testrun/python/requirements.txt # Copy over all configuration files diff --git a/modules/test/ntp/bin/start_test_module b/modules/test/ntp/bin/start_test_module new file mode 100644 index 000000000..a09349cf9 --- /dev/null +++ b/modules/test/ntp/bin/start_test_module @@ -0,0 +1,42 @@ +#!/bin/bash + +# An example startup script that does the bare minimum to start +# a test module via a pyhon script. Each test module should include a +# start_test_module file that overwrites this one to boot all of its +# specific requirements to run. + +# Define where the python source files are located +PYTHON_SRC_DIR=/testrun/python/src + +# Fetch module name +MODULE_NAME=$1 + +# Default interface should be veth0 for all containers +DEFAULT_IFACE=veth0 + +# Allow a user to define an interface by passing it into this script +DEFINED_IFACE=$2 + +# Select which interace to use +if [[ -z $DEFINED_IFACE || "$DEFINED_IFACE" == "null" ]] +then + echo "No interface defined, defaulting to veth0" + INTF=$DEFAULT_IFACE +else + INTF=$DEFINED_IFACE +fi + +# Create and set permissions on the log files +LOG_FILE=/runtime/output/$MODULE_NAME.log +RESULT_FILE=/runtime/output/$MODULE_NAME-result.json +touch $LOG_FILE +touch $RESULT_FILE +chown $HOST_USER $LOG_FILE +chown $HOST_USER $RESULT_FILE + +# Run the python scrip that will execute the tests for this module +# -u flag allows python print statements +# to be logged by docker by running unbuffered +python3 -u $PYTHON_SRC_DIR/run.py "-m $MODULE_NAME" + +echo Module has finished \ No newline at end of file diff --git a/modules/test/ntp/conf/module_config.json b/modules/test/ntp/conf/module_config.json new file mode 100644 index 000000000..288474868 --- /dev/null +++ b/modules/test/ntp/conf/module_config.json @@ -0,0 +1,27 @@ +{ + "config": { + "meta": { + "name": "ntp", + "display_name": "NTP", + "description": "NTP test" + }, + "network": false, + "docker": { + "depends_on": "base", + "enable_container": true, + "timeout": 30 + }, + "tests":[ + { + "name": "ntp.network.ntp_support", + "description": "Does the device request network time sync as client as per RFC 5905 - Network Time Protocol Version 4: Protocol and Algorithms Specification", + "expected_behavior": "The device sends an NTPv4 request to the configured NTP server." + }, + { + "name": "ntp.network.ntp_dhcp", + "description": "Accept NTP address over DHCP", + "expected_behavior": "Device can accept NTP server address, provided by the DHCP server (DHCP OFFER PACKET)" + } + ] + } +} diff --git a/modules/test/ntp/ntp.Dockerfile b/modules/test/ntp/ntp.Dockerfile new file mode 100644 index 000000000..33b06287e --- /dev/null +++ b/modules/test/ntp/ntp.Dockerfile @@ -0,0 +1,20 @@ +# Image name: test-run/ntp-test +FROM test-run/base-test:latest + +ARG MODULE_NAME=ntp +ARG MODULE_DIR=modules/test/$MODULE_NAME + +# Load the requirements file +COPY $MODULE_DIR/python/requirements.txt /testrun/python + +# Install all python requirements for the module +RUN pip3 install -r /testrun/python/requirements.txt + +# Copy over all configuration files +COPY $MODULE_DIR/conf /testrun/conf + +# Copy over all binary files +COPY $MODULE_DIR/bin /testrun/bin + +# Copy over all python files +COPY $MODULE_DIR/python /testrun/python \ No newline at end of file diff --git a/modules/test/ntp/python/requirements.txt b/modules/test/ntp/python/requirements.txt new file mode 100644 index 000000000..93b351f44 --- /dev/null +++ b/modules/test/ntp/python/requirements.txt @@ -0,0 +1 @@ +scapy \ No newline at end of file diff --git a/modules/test/ntp/python/src/ntp_module.py b/modules/test/ntp/python/src/ntp_module.py new file mode 100644 index 000000000..4053ce98a --- /dev/null +++ b/modules/test/ntp/python/src/ntp_module.py @@ -0,0 +1,79 @@ +# Copyright 2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""NTP test module""" +from test_module import TestModule +from scapy.all import rdpcap, NTP, IP + +LOG_NAME = 'test_ntp' +NTP_SERVER_CAPTURE_FILE = '/runtime/network/ntp.pcap' +STARTUP_CAPTURE_FILE = '/runtime/device/startup.pcap' +MONITOR_CAPTURE_FILE = '/runtime/device/monitor.pcap' +LOGGER = None + +class NTPModule(TestModule): + """NTP Test module""" + + def __init__(self, module): + super().__init__(module_name=module, log_name=LOG_NAME) + # TODO: This should be fetched dynamically + self._ntp_server = '10.10.10.5' + + global LOGGER + LOGGER = self._get_logger() + + def _ntp_network_ntp_support(self): + LOGGER.info('Running ntp.network.ntp_support') + + packet_capture = rdpcap(STARTUP_CAPTURE_FILE) + rdpcap(MONITOR_CAPTURE_FILE) + + device_sends_ntp4 = False + device_sends_ntp3 = False + + for packet in packet_capture: + + if NTP in packet and packet.src == self._device_mac: + if packet[NTP].version == 4: + device_sends_ntp4 = True + LOGGER.info(f'Device sent NTPv4 request to {packet[IP].dst}') + elif packet[NTP].version == 3: + device_sends_ntp3 = True + LOGGER.info(f'Device sent NTPv3 request to {packet[IP].dst}') + + if not (device_sends_ntp3 or device_sends_ntp4): + LOGGER.info('Device has not sent any NTP requests') + + return device_sends_ntp4 and not device_sends_ntp3 + + def _ntp_network_ntp_dhcp(self): + LOGGER.info('Running ntp.network.ntp_dhcp') + + packet_capture = rdpcap(STARTUP_CAPTURE_FILE) + rdpcap(MONITOR_CAPTURE_FILE) + + device_sends_ntp = False + + for packet in packet_capture: + + if NTP in packet and packet.src == self._device_mac: + device_sends_ntp = True + if packet[IP].dst == self._ntp_server: + LOGGER.info('Device sent NTP request to DHCP provided NTP server') + return True + + if not device_sends_ntp: + LOGGER.info('Device has not sent any NTP requests') + else: + LOGGER.info('Device has not sent NTP requests to DHCP provided NTP server') + + return False diff --git a/modules/test/ntp/python/src/run.py b/modules/test/ntp/python/src/run.py new file mode 100644 index 000000000..685bb4083 --- /dev/null +++ b/modules/test/ntp/python/src/run.py @@ -0,0 +1,75 @@ +# Copyright 2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Run NTP test module""" +import argparse +import signal +import sys +import logger + +from ntp_module import NTPModule + +LOG_NAME = "ntp_runner" +LOGGER = logger.get_logger(LOG_NAME) + + +class NTPModuleRunner: + """Run the NTP module tests.""" + def __init__(self, module): + + signal.signal(signal.SIGINT, self._handler) + signal.signal(signal.SIGTERM, self._handler) + signal.signal(signal.SIGABRT, self._handler) + signal.signal(signal.SIGQUIT, self._handler) + self.add_logger(module) + + LOGGER.info("Starting NTP test module") + + self._test_module = NTPModule(module) + self._test_module.run_tests() + + LOGGER.info("NTP test module finished") + + def add_logger(self, module): + global LOGGER + LOGGER = logger.get_logger(LOG_NAME, module) + + def _handler(self, signum): + LOGGER.debug("SigtermEnum: " + str(signal.SIGTERM)) + LOGGER.debug("Exit signal received: " + str(signum)) + if signum in (2, signal.SIGTERM): + LOGGER.info("Exit signal received. Stopping test module...") + LOGGER.info("Test module stopped") + sys.exit(1) + + +def run(): + parser = argparse.ArgumentParser( + description="NTP Module Help", + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + + parser.add_argument( + "-m", + "--module", + help="Define the module name to be used to create the log file") + + args = parser.parse_args() + + # For some reason passing in the args from bash adds an extra + # space before the argument so we'll just strip out extra space + NTPModuleRunner(args.module.strip()) + + +if __name__ == "__main__": + run() diff --git a/resources/devices/template/device_config.json b/resources/devices/template/device_config.json index 3bb804b22..1e92de25d 100644 --- a/resources/devices/template/device_config.json +++ b/resources/devices/template/device_config.json @@ -14,6 +14,35 @@ } } }, + "connection": { + "enabled": true, + "tests": { + "connection.mac_address": { + "enabled": true + }, + "connection.mac_oui": { + "enabled": true + }, + "connection.target_ping": { + "enabled": true + } + , + "connection.single_ip": { + "enabled": true + } + } + }, + "ntp": { + "enabled": true, + "tests": { + "ntp.network.ntp_support": { + "enabled": true + }, + "ntp.network.ntp_dhcp": { + "enabled": true + } + } + }, "baseline": { "enabled": false, "tests": { diff --git a/testing/test_pylint b/testing/test_pylint index 5cd1dff73..2ba696af5 100755 --- a/testing/test_pylint +++ b/testing/test_pylint @@ -14,7 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -ERROR_LIMIT=1100 +ERROR_LIMIT=100 sudo cmd/install