From 03eac20e1cc0eb162666822f1c3ac21ab68c5569 Mon Sep 17 00:00:00 2001 From: yaqiangz Date: Thu, 16 Nov 2023 02:18:23 -0500 Subject: [PATCH] [featured] Add support for dhcp_server in featured --- scripts/featured | 44 ++++++++- tests/featured/featured_test.py | 5 +- tests/featured/test_vectors.py | 161 ++++++++++++++++++++++++++++++++ 3 files changed, 206 insertions(+), 4 deletions(-) diff --git a/scripts/featured b/scripts/featured index d66e8b10..a1587c78 100644 --- a/scripts/featured +++ b/scripts/featured @@ -24,15 +24,48 @@ DEFAULT_SELECT_TIMEOUT = 1000 # 1sec PORT_INIT_TIMEOUT_SEC = 180 -def run_cmd(cmd, log_err=True, raise_exception=False): +def run_cmd(cmd, log_err=True, raise_exception=False, return_res=False): try: - subprocess.check_call(cmd) + if return_res: + return 0, subprocess.check_output(cmd, universal_newlines=True) + else: + subprocess.check_call(cmd) except Exception as err: if log_err: syslog.syslog(syslog.LOG_ERR, "{} - failed: return code - {}, output:\n{}" .format(err.cmd, err.returncode, err.output)) if raise_exception: raise + if return_res: + return err.returncode, str("Encouter error: {}".format(err.output)) + + +def is_service_enabled(service_name): + """ + Check whether service is enabled + Args: + service_name: Name of service to be checked + Returns: + If service is enabled, return True. Else return False + """ + state, output = run_cmd(["systemctl", "is-enabled", service_name], return_res=True) + return state == 0 and output.strip() == "enabled" + + +def restart_service(service_name): + """ + Restart service by name + Args: + service_name: Name of service to be restarted + """ + if is_service_enabled(service_name): + syslog.syslog(syslog.LOG_INFO, "Restarting {}".format(service_name)) + run_cmd(["sudo", "systemctl", "stop", service_name]) + run_cmd(["sudo", "systemctl", "reset-failed", service_name]) + run_cmd(["sudo", "systemctl", "start", service_name], raise_exception=True) + syslog.syslog(syslog.LOG_INFO, "{} restarted successfully".format(service_name)) + else: + syslog.syslog(syslog.LOG_ERR, "Cannot restart {} because it's not enabled".format(service_name)) def signal_handler(sig, frame): @@ -414,7 +447,9 @@ class FeatureHandler(object): .format(feature.name, feature_suffixes[-1])) self.set_feature_state(feature, self.FEATURE_STATE_FAILED) return - + if feature_name == "dhcp_server": + # For dhcp_server feature change, we need to restart dhcp_relay container too + restart_service("dhcp_relay") self.set_feature_state(feature, self.FEATURE_STATE_ENABLED) def disable_feature(self, feature): @@ -438,6 +473,9 @@ class FeatureHandler(object): .format(feature.name, feature_suffixes[-1])) self.set_feature_state(feature, self.FEATURE_STATE_FAILED) return + if feature.name == "dhcp_server": + # For dhcp_server feature change, we need to restart dhcp_relay container too + restart_service("dhcp_relay") self.set_feature_state(feature, self.FEATURE_STATE_DISABLED) diff --git a/tests/featured/featured_test.py b/tests/featured/featured_test.py index 8ee04181..f86ab810 100644 --- a/tests/featured/featured_test.py +++ b/tests/featured/featured_test.py @@ -203,11 +203,14 @@ def test_handler(self, test_scenario_name, config_data, fs): with mock.patch('featured.subprocess') as mocked_subprocess: with mock.patch("sonic_py_common.device_info.get_device_runtime_metadata", return_value=config_data['device_runtime_metadata']): with mock.patch("sonic_py_common.device_info.is_multi_npu", return_value=True if 'num_npu' in config_data else False): - with mock.patch("sonic_py_common.device_info.get_num_npus", return_value=config_data['num_npu'] if 'num_npu' in config_data else 1): + with mock.patch("sonic_py_common.device_info.get_num_npus", return_value=config_data['num_npu'] if 'num_npu' in config_data else 1), \ + mock.patch("featured.is_service_enabled") as mock_is_service_enabled: popen_mock = mock.Mock() attrs = config_data['popen_attributes'] popen_mock.configure_mock(**attrs) mocked_subprocess.Popen.return_value = popen_mock + if "is_service_enabled_side_effict" in config_data: + mock_is_service_enabled.side_effect = config_data["is_service_enabled_side_effict"] device_config = {} device_config['DEVICE_METADATA'] = MockConfigDb.CONFIG_DB['DEVICE_METADATA'] diff --git a/tests/featured/test_vectors.py b/tests/featured/test_vectors.py index bc0d0cac..7b92c569 100644 --- a/tests/featured/test_vectors.py +++ b/tests/featured/test_vectors.py @@ -402,6 +402,167 @@ }, }, ], + [ + "SingleToRCase_DHCP_Server_Enabled", + { + "device_runtime_metadata": { + "DEVICE_RUNTIME_METADATA": { + "ETHERNET_PORTS_PRESENT": True + } + }, + "config_db": { + "DEVICE_METADATA": { + "localhost": { + "type": "ToR", + } + }, + "FEATURE": { + "dhcp_server": { + "auto_restart": "enabled", + "has_global_scope": "True", + "has_per_asic_scope": "False", + "delayed": "False", + "high_mem_alert": "disabled", + "set_owner": "local", + "state": "enabled" + } + }, + }, + "expected_config_db": { + "FEATURE": { + "dhcp_server": { + "auto_restart": "enabled", + "has_global_scope": "True", + "has_per_asic_scope": "False", + "delayed": "False", + "high_mem_alert": "disabled", + "set_owner": "local", + "state": "enabled" + } + }, + }, + "enable_feature_subprocess_calls": [ + call(["sudo", "systemctl", "unmask", "dhcp_server.service"]), + call(["sudo", "systemctl", "enable", "dhcp_server.service"]), + call(["sudo", "systemctl", "start", "dhcp_server.service"]), + call(["sudo", "systemctl", "stop", "dhcp_relay"]), + call(["sudo", "systemctl", "reset-failed", "dhcp_relay"]), + call(["sudo", "systemctl", "start", "dhcp_relay"]) + ], + "daemon_reload_subprocess_call": [ + call(["sudo", "systemctl", "daemon-reload"]), + ], + "popen_attributes": { + 'communicate.return_value': ('output', 'error') + }, + "is_service_enabled_side_effict": [True] + }, + ], + [ + "SingleToRCase_DHCP_Server_Enabled_Dhcp_Relay_Disabled", + { + "device_runtime_metadata": { + "DEVICE_RUNTIME_METADATA": { + "ETHERNET_PORTS_PRESENT": True + } + }, + "config_db": { + "DEVICE_METADATA": { + "localhost": { + "type": "ToR", + } + }, + "FEATURE": { + "dhcp_server": { + "auto_restart": "enabled", + "has_global_scope": "True", + "has_per_asic_scope": "False", + "delayed": "False", + "high_mem_alert": "disabled", + "set_owner": "local", + "state": "enabled" + } + }, + }, + "expected_config_db": { + "FEATURE": { + "dhcp_server": { + "auto_restart": "enabled", + "has_global_scope": "True", + "has_per_asic_scope": "False", + "delayed": "False", + "high_mem_alert": "disabled", + "set_owner": "local", + "state": "enabled" + } + }, + }, + "enable_feature_subprocess_calls": [ + call(["sudo", "systemctl", "unmask", "dhcp_server.service"]), + call(["sudo", "systemctl", "enable", "dhcp_server.service"]), + call(["sudo", "systemctl", "start", "dhcp_server.service"]) + ], + "daemon_reload_subprocess_call": [ + call(["sudo", "systemctl", "daemon-reload"]), + ], + "popen_attributes": { + 'communicate.return_value': ('output', 'error') + }, + "is_service_enabled_side_effict": [False] + }, + ], + [ + "SingleToRCase_DHCP_Server_Disabled", + { + "device_runtime_metadata": { + "DEVICE_RUNTIME_METADATA": { + "ETHERNET_PORTS_PRESENT": True + } + }, + "config_db": { + "DEVICE_METADATA": { + "localhost": { + "type": "ToR", + } + }, + "FEATURE": { + "dhcp_server": { + "auto_restart": "enabled", + "has_global_scope": "True", + "has_per_asic_scope": "False", + "delayed": "False", + "high_mem_alert": "disabled", + "set_owner": "local", + "state": "disabled" + } + }, + }, + "expected_config_db": { + "FEATURE": { + "dhcp_server": { + "auto_restart": "enabled", + "has_global_scope": "True", + "has_per_asic_scope": "False", + "delayed": "False", + "high_mem_alert": "disabled", + "set_owner": "local", + "state": "disabled" + } + }, + }, + "enable_feature_subprocess_calls": [ + call(["sudo", "systemctl", "stop", "dhcp_server.service"]), + call(["sudo", "systemctl", "disable", "dhcp_server.service"]), + call(["sudo", "systemctl", "mask", "dhcp_server.service"]) + ], + "daemon_reload_subprocess_call": [ + call(["sudo", "systemctl", "daemon-reload"]), + ], + "popen_attributes": { + 'communicate.return_value': ('output', 'error') + }, + }, + ], [ "DualTorCaseWithNoSystemCalls", {