Cherry-pick of sonic-net/sonic-host-services#14 in 202205 branch
This commit is contained in:
abdosi 2022-11-08 10:23:29 -08:00 committed by GitHub
parent bdde527120
commit bb68cbcd3d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 666 additions and 68 deletions

View File

@ -146,30 +146,31 @@ class Feature(object):
""" """
self.name = feature_name self.name = feature_name
self.state = self._get_target_state(feature_cfg.get('state'), device_config or {}) self.state = self._get_feature_table_key_render_value(feature_cfg.get('state'), device_config or {}, ['enabled', 'disabled', 'always_enabled', 'always_disabled'])
self.auto_restart = feature_cfg.get('auto_restart', 'disabled') self.auto_restart = feature_cfg.get('auto_restart', 'disabled')
self.has_timer = safe_eval(feature_cfg.get('has_timer', 'False')) self.has_timer = safe_eval(feature_cfg.get('has_timer', 'False'))
self.has_global_scope = safe_eval(feature_cfg.get('has_global_scope', 'True')) self.has_global_scope = safe_eval(feature_cfg.get('has_global_scope', 'True'))
self.has_per_asic_scope = safe_eval(feature_cfg.get('has_per_asic_scope', 'False')) self.has_per_asic_scope = safe_eval(self._get_feature_table_key_render_value(feature_cfg.get('has_per_asic_scope', 'False'), device_config or {}, ['True', 'False']))
def _get_target_state(self, state_configuration, device_config): def _get_feature_table_key_render_value(self, configuration, device_config, expected_values):
""" Returns the target state for the feature by rendering the state field as J2 template. """ Returns the target value for the feature by rendering the configuration as J2 template.
Args: Args:
state_configuration (str): State configuration from CONFIG_DB configuration (str): Feature Table value from CONFIG_DB for given key
deviec_config (dict): DEVICE_METADATA section of CONFIG_DB device_config (dict): DEVICE_METADATA section of CONFIG_DB and populated Device Running Metadata
expected_values (list): Expected set of Feature Table value for given key
Returns: Returns:
(str): Target feature state (str): Target feature table value for given key
""" """
if state_configuration is None: if configuration is None:
return None return None
template = jinja2.Template(state_configuration) template = jinja2.Template(configuration)
target_state = template.render(device_config) target_value = template.render(device_config)
if target_state not in ('enabled', 'disabled', 'always_enabled', 'always_disabled'): if target_value not in expected_values:
raise ValueError('Invalid state rendered for feature {}: {}'.format(self.name, target_state)) raise ValueError('Invalid value rendered for feature {}: {}'.format(self.name, target_value))
return target_state return target_value
def compare_state(self, feature_name, feature_cfg): def compare_state(self, feature_name, feature_cfg):
if self.name != feature_name or not isinstance(feature_cfg, dict): if self.name != feature_name or not isinstance(feature_cfg, dict):
@ -197,6 +198,7 @@ class FeatureHandler(object):
self._device_config = device_config self._device_config = device_config
self._cached_config = {} self._cached_config = {}
self.is_multi_npu = device_info.is_multi_npu() self.is_multi_npu = device_info.is_multi_npu()
self._device_running_config = device_info.get_device_runtime_metadata()
def handler(self, feature_name, op, feature_cfg): def handler(self, feature_name, op, feature_cfg):
if not feature_cfg: if not feature_cfg:
@ -205,7 +207,11 @@ class FeatureHandler(object):
self._feature_state_table._del(feature_name) self._feature_state_table._del(feature_name)
return return
feature = Feature(feature_name, feature_cfg, self._device_config) device_config = {}
device_config.update(self._device_config)
device_config.update(self._device_running_config)
feature = Feature(feature_name, feature_cfg, device_config)
self._cached_config.setdefault(feature_name, Feature(feature_name, {})) self._cached_config.setdefault(feature_name, Feature(feature_name, {}))
# Change auto-restart configuration first. # Change auto-restart configuration first.
@ -230,14 +236,18 @@ class FeatureHandler(object):
""" """
Summary: Summary:
Updates the state field in the FEATURE|* tables as the state field Updates the state field in the FEATURE|* tables as the state field
might have to be rendered based on DEVICE_METADATA table might have to be rendered based on DEVICE_METADATA table and generated Device Running Metadata
""" """
for feature_name in feature_table.keys(): for feature_name in feature_table.keys():
if not feature_name: if not feature_name:
syslog.syslog(syslog.LOG_WARNING, "Feature is None") syslog.syslog(syslog.LOG_WARNING, "Feature is None")
continue continue
feature = Feature(feature_name, feature_table[feature_name], self._device_config) device_config = {}
device_config.update(self._device_config)
device_config.update(self._device_running_config)
feature = Feature(feature_name, feature_table[feature_name], device_config)
self._cached_config.setdefault(feature_name, feature) self._cached_config.setdefault(feature_name, feature)
self.update_systemd_config(feature) self.update_systemd_config(feature)
@ -283,8 +293,47 @@ class FeatureHandler(object):
self.disable_feature(feature) self.disable_feature(feature)
syslog.syslog(syslog.LOG_INFO, "Feature {} is stopped and disabled".format(feature.name)) syslog.syslog(syslog.LOG_INFO, "Feature {} is stopped and disabled".format(feature.name))
self.sync_feature_asic_scope(feature)
return True return True
def sync_feature_asic_scope(self, feature_config):
"""Updates the has_per_asic_scope field in the FEATURE|* tables as the field
might have to be rendered based on DEVICE_METADATA table or Device Running configuration.
Disable the ASIC instance service unit file it the render value is False and update config
Args:
feature: An object represents a feature's configuration in `FEATURE`
table of `CONFIG_DB`.
Returns:
None.
"""
cmds = []
feature_names, feature_suffixes = self.get_multiasic_feature_instances(feature_config, True)
for feature_name in feature_names:
if '@' not in feature_name:
continue
unit_file_state = self.get_systemd_unit_state("{}.{}".format(feature_name, feature_suffixes[-1]))
if not unit_file_state:
continue
if unit_file_state != "disabled" and not feature_config.has_per_asic_scope:
for suffix in reversed(feature_suffixes):
cmds.append("sudo systemctl stop {}.{}".format(feature_name, suffix))
cmds.append("sudo systemctl disable {}.{}".format(feature_name, feature_suffixes[-1]))
cmds.append("sudo systemctl mask {}.{}".format(feature_name, feature_suffixes[-1]))
for cmd in cmds:
syslog.syslog(syslog.LOG_INFO, "Running cmd: '{}'".format(cmd))
try:
run_cmd(cmd, raise_exception=True)
except Exception as err:
syslog.syslog(syslog.LOG_ERR, "Feature '{}.{}' failed to be stopped and disabled"
.format(feature.name, feature_suffixes[-1]))
self.set_feature_state(feature, self.FEATURE_STATE_FAILED)
return
self._config_db.mod_entry('FEATURE', feature_config.name, {'has_per_asic_scope': str(feature_config.has_per_asic_scope)})
def update_systemd_config(self, feature_config): def update_systemd_config(self, feature_config):
"""Updates `Restart=` field in feature's systemd configuration file """Updates `Restart=` field in feature's systemd configuration file
according to the value of `auto_restart` field in `FEATURE` table of `CONFIG_DB`. according to the value of `auto_restart` field in `FEATURE` table of `CONFIG_DB`.
@ -323,12 +372,12 @@ class FeatureHandler(object):
except Exception as err: except Exception as err:
syslog.syslog(syslog.LOG_ERR, "Failed to reload systemd configuration files!") syslog.syslog(syslog.LOG_ERR, "Failed to reload systemd configuration files!")
def get_multiasic_feature_instances(self, feature): def get_multiasic_feature_instances(self, feature, all_instance=False):
# Create feature name suffix depending feature is running in host or namespace or in both # Create feature name suffix depending feature is running in host or namespace or in both
feature_names = ( feature_names = (
([feature.name] if feature.has_global_scope or not self.is_multi_npu else []) + ([feature.name] if feature.has_global_scope or not self.is_multi_npu else []) +
([(feature.name + '@' + str(asic_inst)) for asic_inst in range(device_info.get_num_npus()) ([(feature.name + '@' + str(asic_inst)) for asic_inst in range(device_info.get_num_npus())
if feature.has_per_asic_scope and self.is_multi_npu]) if self.is_multi_npu and (all_instance or feature.has_per_asic_scope)])
) )
if not feature_names: if not feature_names:
@ -358,7 +407,7 @@ class FeatureHandler(object):
for feature_name in feature_names: for feature_name in feature_names:
# Check if it is already enabled, if yes skip the system call # Check if it is already enabled, if yes skip the system call
unit_file_state = self.get_systemd_unit_state("{}.{}".format(feature_name, feature_suffixes[-1])) unit_file_state = self.get_systemd_unit_state("{}.{}".format(feature_name, feature_suffixes[-1]))
if unit_file_state == "enabled": if unit_file_state == "enabled" or not unit_file_state:
continue continue
for suffix in feature_suffixes: for suffix in feature_suffixes:
@ -388,7 +437,7 @@ class FeatureHandler(object):
for feature_name in feature_names: for feature_name in feature_names:
# Check if it is already disabled, if yes skip the system call # Check if it is already disabled, if yes skip the system call
unit_file_state = self.get_systemd_unit_state("{}.{}".format(feature_name, feature_suffixes[-1])) unit_file_state = self.get_systemd_unit_state("{}.{}".format(feature_name, feature_suffixes[-1]))
if unit_file_state in ("disabled", "masked"): if unit_file_state in ("disabled", "masked") or not unit_file_state:
continue continue
for suffix in reversed(feature_suffixes): for suffix in reversed(feature_suffixes):

View File

@ -1,6 +1,7 @@
import os import os
import sys import sys
import swsscommon as swsscommon_package import swsscommon as swsscommon_package
from sonic_py_common import device_info
from swsscommon import swsscommon from swsscommon import swsscommon
from parameterized import parameterized from parameterized import parameterized
@ -45,7 +46,7 @@ class TestFeatureHandler(TestCase):
return True if not ddiff else False return True if not ddiff else False
def checks_systemd_config_file(self, feature_table): def checks_systemd_config_file(self, feature_table, feature_systemd_name_map=None):
"""Checks whether the systemd configuration file of each feature was created or not """Checks whether the systemd configuration file of each feature was created or not
and whether the `Restart=` field in the file is set correctly or not. and whether the `Restart=` field in the file is set correctly or not.
@ -68,13 +69,16 @@ class TestFeatureHandler(TestCase):
elif "disabled" in auto_restart_status: elif "disabled" in auto_restart_status:
auto_restart_status = "disabled" auto_restart_status = "disabled"
feature_systemd_config_file_path = systemd_config_file_path.format(feature_name) feature_systemd_list = feature_systemd_name_map[feature_name] if feature_systemd_name_map else [feature_name]
is_config_file_existing = os.path.exists(feature_systemd_config_file_path)
assert is_config_file_existing, "Systemd configuration file of feature '{}' does not exist!".format(feature_name)
with open(feature_systemd_config_file_path) as systemd_config_file: for feature_systemd in feature_systemd_list:
status = systemd_config_file.read().strip() feature_systemd_config_file_path = systemd_config_file_path.format(feature_systemd)
assert status == '[Service]\nRestart={}'.format(truth_table[auto_restart_status]) is_config_file_existing = os.path.exists(feature_systemd_config_file_path)
assert is_config_file_existing, "Systemd configuration file of feature '{}' does not exist!".format(feature_systemd)
with open(feature_systemd_config_file_path) as systemd_config_file:
status = systemd_config_file.read().strip()
assert status == '[Service]\nRestart={}'.format(truth_table[auto_restart_status])
def get_state_db_set_calls(self, feature_table): def get_state_db_set_calls(self, feature_table):
"""Returns a Mock call objects which recorded the `set` calls to `FEATURE` table in `STATE_DB`. """Returns a Mock call objects which recorded the `set` calls to `FEATURE` table in `STATE_DB`.
@ -119,31 +123,42 @@ class TestFeatureHandler(TestCase):
MockConfigDb.set_config_db(config_data['config_db']) MockConfigDb.set_config_db(config_data['config_db'])
feature_state_table_mock = mock.Mock() feature_state_table_mock = mock.Mock()
with mock.patch('hostcfgd.subprocess') as mocked_subprocess: with mock.patch('hostcfgd.subprocess') as mocked_subprocess:
popen_mock = mock.Mock() with mock.patch("sonic_py_common.device_info.get_device_runtime_metadata", return_value=config_data['device_runtime_metadata']):
attrs = config_data['popen_attributes'] with mock.patch("sonic_py_common.device_info.is_multi_npu", return_value=True if 'num_npu' in config_data else False):
popen_mock.configure_mock(**attrs) 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):
mocked_subprocess.Popen.return_value = popen_mock popen_mock = mock.Mock()
attrs = config_data['popen_attributes']
popen_mock.configure_mock(**attrs)
mocked_subprocess.Popen.return_value = popen_mock
device_config = {} device_config = {}
device_config['DEVICE_METADATA'] = MockConfigDb.CONFIG_DB['DEVICE_METADATA'] device_config['DEVICE_METADATA'] = MockConfigDb.CONFIG_DB['DEVICE_METADATA']
feature_handler = hostcfgd.FeatureHandler(MockConfigDb(), feature_state_table_mock, device_config) device_config.update(config_data['device_runtime_metadata'])
feature_table = MockConfigDb.CONFIG_DB['FEATURE'] feature_handler = hostcfgd.FeatureHandler(MockConfigDb(), feature_state_table_mock, device_config)
feature_handler.sync_state_field(feature_table)
is_any_difference = self.checks_config_table(MockConfigDb.get_config_db()['FEATURE'], feature_table = MockConfigDb.CONFIG_DB['FEATURE']
config_data['expected_config_db']['FEATURE']) feature_handler.sync_state_field(feature_table)
assert is_any_difference, "'FEATURE' table in 'CONFIG_DB' is modified unexpectedly!"
feature_table_state_db_calls = self.get_state_db_set_calls(feature_table) feature_systemd_name_map = {}
for feature_name in feature_table.keys():
feature = hostcfgd.Feature(feature_name, feature_table[feature_name], device_config)
feature_names, _ = feature_handler.get_multiasic_feature_instances(feature)
feature_systemd_name_map[feature_name] = feature_names
self.checks_systemd_config_file(config_data['config_db']['FEATURE']) is_any_difference = self.checks_config_table(MockConfigDb.get_config_db()['FEATURE'],
mocked_subprocess.check_call.assert_has_calls(config_data['enable_feature_subprocess_calls'], config_data['expected_config_db']['FEATURE'])
any_order=True) assert is_any_difference, "'FEATURE' table in 'CONFIG_DB' is modified unexpectedly!"
mocked_subprocess.check_call.assert_has_calls(config_data['daemon_reload_subprocess_call'],
any_order=True) feature_table_state_db_calls = self.get_state_db_set_calls(feature_table)
feature_state_table_mock.set.assert_has_calls(feature_table_state_db_calls)
self.checks_systemd_config_file(config_data['config_db']['FEATURE']) self.checks_systemd_config_file(config_data['config_db']['FEATURE'], feature_systemd_name_map)
mocked_subprocess.check_call.assert_has_calls(config_data['enable_feature_subprocess_calls'],
any_order=True)
mocked_subprocess.check_call.assert_has_calls(config_data['daemon_reload_subprocess_call'],
any_order=True)
feature_state_table_mock.set.assert_has_calls(feature_table_state_db_calls)
self.checks_systemd_config_file(config_data['config_db']['FEATURE'], feature_systemd_name_map)
@parameterized.expand(HOSTCFGD_TEST_VECTOR) @parameterized.expand(HOSTCFGD_TEST_VECTOR)
@patchfs @patchfs
@ -164,25 +179,33 @@ class TestFeatureHandler(TestCase):
MockConfigDb.set_config_db(config_data['config_db']) MockConfigDb.set_config_db(config_data['config_db'])
feature_state_table_mock = mock.Mock() feature_state_table_mock = mock.Mock()
with mock.patch('hostcfgd.subprocess') as mocked_subprocess: with mock.patch('hostcfgd.subprocess') as mocked_subprocess:
popen_mock = mock.Mock() with mock.patch("sonic_py_common.device_info.get_device_runtime_metadata", return_value=config_data['device_runtime_metadata']):
attrs = config_data['popen_attributes'] with mock.patch("sonic_py_common.device_info.is_multi_npu", return_value=True if 'num_npu' in config_data else False):
popen_mock.configure_mock(**attrs) 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):
mocked_subprocess.Popen.return_value = popen_mock popen_mock = mock.Mock()
attrs = config_data['popen_attributes']
popen_mock.configure_mock(**attrs)
mocked_subprocess.Popen.return_value = popen_mock
device_config = {} device_config = {}
device_config['DEVICE_METADATA'] = MockConfigDb.CONFIG_DB['DEVICE_METADATA'] device_config['DEVICE_METADATA'] = MockConfigDb.CONFIG_DB['DEVICE_METADATA']
feature_handler = hostcfgd.FeatureHandler(MockConfigDb(), feature_state_table_mock, device_config) device_config.update(config_data['device_runtime_metadata'])
feature_handler = hostcfgd.FeatureHandler(MockConfigDb(), feature_state_table_mock, device_config)
feature_table = MockConfigDb.CONFIG_DB['FEATURE'] feature_table = MockConfigDb.CONFIG_DB['FEATURE']
for feature_name, feature_config in feature_table.items(): feature_systemd_name_map = {}
feature_handler.handler(feature_name, 'SET', feature_config) for feature_name, feature_config in feature_table.items():
feature_handler.handler(feature_name, 'SET', feature_config)
feature = hostcfgd.Feature(feature_name, feature_table[feature_name], device_config)
feature_names, _ = feature_handler.get_multiasic_feature_instances(feature)
feature_systemd_name_map[feature_name] = feature_names
self.checks_systemd_config_file(config_data['config_db']['FEATURE']) self.checks_systemd_config_file(config_data['config_db']['FEATURE'], feature_systemd_name_map)
mocked_subprocess.check_call.assert_has_calls(config_data['enable_feature_subprocess_calls'], mocked_subprocess.check_call.assert_has_calls(config_data['enable_feature_subprocess_calls'],
any_order=True) any_order=True)
mocked_subprocess.check_call.assert_has_calls(config_data['daemon_reload_subprocess_call'], mocked_subprocess.check_call.assert_has_calls(config_data['daemon_reload_subprocess_call'],
any_order=True) any_order=True)
def test_feature_config_parsing(self): def test_feature_config_parsing(self):
swss_feature = hostcfgd.Feature('swss', { swss_feature = hostcfgd.Feature('swss', {

View File

@ -7,6 +7,11 @@ HOSTCFGD_TEST_VECTOR = [
[ [
"DualTorCase", "DualTorCase",
{ {
"device_runtime_metadata": {
"DEVICE_RUNTIME_METADATA": {
"ETHERNET_PORTS_PRESENT":True
}
},
"config_db": { "config_db": {
"DEVICE_METADATA": { "DEVICE_METADATA": {
"localhost": { "localhost": {
@ -19,7 +24,7 @@ HOSTCFGD_TEST_VECTOR = [
"enabled": "false", "enabled": "false",
"num_dumps": "3", "num_dumps": "3",
"memory": "0M-2G:256M,2G-4G:320M,4G-8G:384M,8G-:448M" "memory": "0M-2G:256M,2G-4G:320M,4G-8G:384M,8G-:448M"
} }
}, },
"FEATURE": { "FEATURE": {
"dhcp_relay": { "dhcp_relay": {
@ -107,6 +112,11 @@ HOSTCFGD_TEST_VECTOR = [
[ [
"SingleToRCase", "SingleToRCase",
{ {
"device_runtime_metadata": {
"DEVICE_RUNTIME_METADATA": {
"ETHERNET_PORTS_PRESENT":True
}
},
"config_db": { "config_db": {
"DEVICE_METADATA": { "DEVICE_METADATA": {
"localhost": { "localhost": {
@ -118,7 +128,7 @@ HOSTCFGD_TEST_VECTOR = [
"enabled": "false", "enabled": "false",
"num_dumps": "3", "num_dumps": "3",
"memory": "0M-2G:256M,2G-4G:320M,4G-8G:384M,8G-:448M" "memory": "0M-2G:256M,2G-4G:320M,4G-8G:384M,8G-:448M"
} }
}, },
"FEATURE": { "FEATURE": {
"dhcp_relay": { "dhcp_relay": {
@ -224,6 +234,11 @@ HOSTCFGD_TEST_VECTOR = [
[ [
"T1Case", "T1Case",
{ {
"device_runtime_metadata": {
"DEVICE_RUNTIME_METADATA": {
"ETHERNET_PORTS_PRESENT":True
}
},
"config_db": { "config_db": {
"DEVICE_METADATA": { "DEVICE_METADATA": {
"localhost": { "localhost": {
@ -235,7 +250,7 @@ HOSTCFGD_TEST_VECTOR = [
"enabled": "false", "enabled": "false",
"num_dumps": "3", "num_dumps": "3",
"memory": "0M-2G:256M,2G-4G:320M,4G-8G:384M,8G-:448M" "memory": "0M-2G:256M,2G-4G:320M,4G-8G:384M,8G-:448M"
} }
}, },
"FEATURE": { "FEATURE": {
"dhcp_relay": { "dhcp_relay": {
@ -320,6 +335,11 @@ HOSTCFGD_TEST_VECTOR = [
[ [
"SingleToRCase_DHCP_Relay_Enabled", "SingleToRCase_DHCP_Relay_Enabled",
{ {
"device_runtime_metadata": {
"DEVICE_RUNTIME_METADATA": {
"ETHERNET_PORTS_PRESENT":True
}
},
"config_db": { "config_db": {
"DEVICE_METADATA": { "DEVICE_METADATA": {
"localhost": { "localhost": {
@ -331,7 +351,7 @@ HOSTCFGD_TEST_VECTOR = [
"enabled": "false", "enabled": "false",
"num_dumps": "3", "num_dumps": "3",
"memory": "0M-2G:256M,2G-4G:320M,4G-8G:384M,8G-:448M" "memory": "0M-2G:256M,2G-4G:320M,4G-8G:384M,8G-:448M"
} }
}, },
"FEATURE": { "FEATURE": {
"dhcp_relay": { "dhcp_relay": {
@ -419,6 +439,11 @@ HOSTCFGD_TEST_VECTOR = [
[ [
"DualTorCaseWithNoSystemCalls", "DualTorCaseWithNoSystemCalls",
{ {
"device_runtime_metadata": {
"DEVICE_RUNTIME_METADATA": {
"ETHERNET_PORTS_PRESENT":True
}
},
"config_db": { "config_db": {
"DEVICE_METADATA": { "DEVICE_METADATA": {
"localhost": { "localhost": {
@ -431,7 +456,7 @@ HOSTCFGD_TEST_VECTOR = [
"enabled": "false", "enabled": "false",
"num_dumps": "3", "num_dumps": "3",
"memory": "0M-2G:256M,2G-4G:320M,4G-8G:384M,8G-:448M" "memory": "0M-2G:256M,2G-4G:320M,4G-8G:384M,8G-:448M"
} }
}, },
"FEATURE": { "FEATURE": {
"dhcp_relay": { "dhcp_relay": {
@ -504,7 +529,508 @@ HOSTCFGD_TEST_VECTOR = [
'communicate.return_value': ('enabled', 'error') 'communicate.return_value': ('enabled', 'error')
}, },
} }
] ],
[
"Chassis_Supervisor_PACKET",
{
"device_runtime_metadata": {
"DEVICE_RUNTIME_METADATA": {
"CHASSIS_METADATA": {
"module_type": "supervisor",
"chassis_type": "packet"
},
"ETHERNET_PORTS_PRESENT":True
}
},
"config_db": {
"DEVICE_METADATA": {
"localhost": {
"type": "SpineRouter",
}
},
"KDUMP": {
"config": {
"enabled": "false",
"num_dumps": "3",
"memory": "0M-2G:256M,2G-4G:320M,4G-8G:384M,8G-:448M"
}
},
"FEATURE": {
"bgp": {
"state": "{% if not DEVICE_RUNTIME_METADATA['ETHERNET_PORTS_PRESENT'] or ('CHASSIS_METADATA' in DEVICE_RUNTIME_METADATA and DEVICE_RUNTIME_METADATA['CHASSIS_METADATA']['module_type'] in ['supervisor']) %}disabled{% else %}enabled{% endif %}",
"has_timer": "False",
"has_global_scope": "True",
"has_per_asic_scope": "True",
"auto_restart": "enabled",
"high_mem_alert": "disabled"
},
"teamd": {
"state": "{% if not DEVICE_RUNTIME_METADATA['ETHERNET_PORTS_PRESENT'] %}disabled{% else %}enabled{% endif %}",
"has_timer": "False",
"has_global_scope": "True",
"has_per_asic_scope": "True",
"auto_restart": "enabled",
"high_mem_alert": "disabled"
},
"lldp": {
"state": "enabled",
"has_timer": "False",
"has_global_scope": "True",
"has_per_asic_scope": "{% if not DEVICE_RUNTIME_METADATA['ETHERNET_PORTS_PRESENT'] or ('CHASSIS_METADATA' in DEVICE_RUNTIME_METADATA and DEVICE_RUNTIME_METADATA['CHASSIS_METADATA']['module_type'] in ['supervisor']) %}False{% else %}True{% endif %}",
"auto_restart": "enabled",
"high_mem_alert": "disabled"
},
},
},
"expected_config_db": {
"FEATURE": {
"bgp": {
"auto_restart": "enabled",
"has_global_scope": "True",
"has_per_asic_scope": "True",
"has_timer": "False",
"high_mem_alert": "disabled",
"state": "disabled"
},
"teamd": {
"auto_restart": "enabled",
"has_global_scope": "True",
"has_per_asic_scope": "True",
"has_timer": "False",
"high_mem_alert": "disabled",
"state": "enabled"
},
"lldp": {
"auto_restart": "enabled",
"has_global_scope": "True",
"has_per_asic_scope": "False",
"has_timer": "False",
"high_mem_alert": "disabled",
"state": "enabled"
},
},
},
"enable_feature_subprocess_calls": [
call("sudo systemctl stop bgp.service", shell=True),
call("sudo systemctl disable bgp.service", shell=True),
call("sudo systemctl mask bgp.service", shell=True),
],
"daemon_reload_subprocess_call": [
call("sudo systemctl daemon-reload", shell=True),
],
"popen_attributes": {
'communicate.return_value': ('output', 'error')
},
},
],
[
"Chassis_Supervisor_VOQ",
{
"device_runtime_metadata": {
"DEVICE_RUNTIME_METADATA": {
"CHASSIS_METADATA": {
"module_type": "supervisor",
"chassis_type": "voq"
},
"ETHERNET_PORTS_PRESENT":False
}
},
"config_db": {
"DEVICE_METADATA": {
"localhost": {
"type": "SpineRouter",
}
},
"KDUMP": {
"config": {
"enabled": "false",
"num_dumps": "3",
"memory": "0M-2G:256M,2G-4G:320M,4G-8G:384M,8G-:448M"
}
},
"FEATURE": {
"bgp": {
"state": "{% if not DEVICE_RUNTIME_METADATA['ETHERNET_PORTS_PRESENT'] or ('CHASSIS_METADATA' in DEVICE_RUNTIME_METADATA and DEVICE_RUNTIME_METADATA['CHASSIS_METADATA']['module_type'] in ['supervisor']) %}disabled{% else %}enabled{% endif %}",
"has_timer": "False",
"has_global_scope": "True",
"has_per_asic_scope": "True",
"auto_restart": "enabled",
"high_mem_alert": "disabled"
},
"teamd": {
"state": "{% if not DEVICE_RUNTIME_METADATA['ETHERNET_PORTS_PRESENT'] %}disabled{% else %}enabled{% endif %}",
"has_timer": "False",
"has_global_scope": "True",
"has_per_asic_scope": "True",
"auto_restart": "enabled",
"high_mem_alert": "disabled"
},
"lldp": {
"state": "enabled",
"has_timer": "False",
"has_global_scope": "True",
"has_per_asic_scope": "{% if not DEVICE_RUNTIME_METADATA['ETHERNET_PORTS_PRESENT'] or ('CHASSIS_METADATA' in DEVICE_RUNTIME_METADATA and DEVICE_RUNTIME_METADATA['CHASSIS_METADATA']['module_type'] in ['supervisor']) %}False{% else %}True{% endif %}",
"auto_restart": "enabled",
"high_mem_alert": "disabled"
},
},
},
"expected_config_db": {
"FEATURE": {
"bgp": {
"auto_restart": "enabled",
"has_global_scope": "True",
"has_per_asic_scope": "True",
"has_timer": "False",
"high_mem_alert": "disabled",
"state": "disabled"
},
"teamd": {
"auto_restart": "enabled",
"has_global_scope": "True",
"has_per_asic_scope": "True",
"has_timer": "False",
"high_mem_alert": "disabled",
"state": "disabled"
},
"lldp": {
"auto_restart": "enabled",
"has_global_scope": "True",
"has_per_asic_scope": "False",
"has_timer": "False",
"high_mem_alert": "disabled",
"state": "enabled"
},
},
},
"enable_feature_subprocess_calls": [
call("sudo systemctl stop bgp.service", shell=True),
call("sudo systemctl disable bgp.service", shell=True),
call("sudo systemctl mask bgp.service", shell=True),
],
"daemon_reload_subprocess_call": [
call("sudo systemctl daemon-reload", shell=True),
],
"popen_attributes": {
'communicate.return_value': ('output', 'error')
},
},
],
[
"Chassis_LineCard_VOQ",
{
"device_runtime_metadata": {
"DEVICE_RUNTIME_METADATA": {
"CHASSIS_METADATA": {
"module_type": "linecard",
"chassis_type": "voq"
},
"ETHERNET_PORTS_PRESENT":True
}
},
"config_db": {
"DEVICE_METADATA": {
"localhost": {
"type": "SpineRouter",
}
},
"KDUMP": {
"config": {
"enabled": "false",
"num_dumps": "3",
"memory": "0M-2G:256M,2G-4G:320M,4G-8G:384M,8G-:448M"
}
},
"FEATURE": {
"bgp": {
"state": "{% if not DEVICE_RUNTIME_METADATA['ETHERNET_PORTS_PRESENT'] or ('CHASSIS_METADATA' in DEVICE_RUNTIME_METADATA and DEVICE_RUNTIME_METADATA['CHASSIS_METADATA']['module_type'] in ['supervisor']) %}disabled{% else %}enabled{% endif %}",
"has_timer": "False",
"has_global_scope": "True",
"has_per_asic_scope": "True",
"auto_restart": "enabled",
"high_mem_alert": "disabled"
},
"teamd": {
"state": "{% if not DEVICE_RUNTIME_METADATA['ETHERNET_PORTS_PRESENT'] %}disabled{% else %}enabled{% endif %}",
"has_timer": "False",
"has_global_scope": "True",
"has_per_asic_scope": "True",
"auto_restart": "enabled",
"high_mem_alert": "disabled"
},
"lldp": {
"state": "enabled",
"has_timer": "False",
"has_global_scope": "True",
"has_per_asic_scope": "{% if not DEVICE_RUNTIME_METADATA['ETHERNET_PORTS_PRESENT'] or ('CHASSIS_METADATA' in DEVICE_RUNTIME_METADATA and DEVICE_RUNTIME_METADATA['CHASSIS_METADATA']['module_type'] in ['supervisor']) %}False{% else %}True{% endif %}",
"auto_restart": "enabled",
"high_mem_alert": "disabled"
},
},
},
"expected_config_db": {
"FEATURE": {
"bgp": {
"auto_restart": "enabled",
"has_global_scope": "True",
"has_per_asic_scope": "True",
"has_timer": "False",
"high_mem_alert": "disabled",
"state": "enabled"
},
"teamd": {
"auto_restart": "enabled",
"has_global_scope": "True",
"has_per_asic_scope": "True",
"has_timer": "False",
"high_mem_alert": "disabled",
"state": "enabled"
},
"lldp": {
"auto_restart": "enabled",
"has_global_scope": "True",
"has_per_asic_scope": "True",
"has_timer": "False",
"high_mem_alert": "disabled",
"state": "enabled"
},
},
},
"enable_feature_subprocess_calls": [
call("sudo systemctl start bgp.service", shell=True),
call("sudo systemctl enable bgp.service", shell=True),
call("sudo systemctl unmask bgp.service", shell=True),
call("sudo systemctl start teamd.service", shell=True),
call("sudo systemctl enable teamd.service", shell=True),
call("sudo systemctl unmask teamd.service", shell=True),
],
"daemon_reload_subprocess_call": [
call("sudo systemctl daemon-reload", shell=True),
],
"popen_attributes": {
'communicate.return_value': ('output', 'error')
},
},
],
[
"Chassis_LineCard_Packet",
{
"device_runtime_metadata": {
"DEVICE_RUNTIME_METADATA": {
"CHASSIS_METADATA": {
"module_type": "linecard",
"chassis_type": "packet"
},
"ETHERNET_PORTS_PRESENT":True
}
},
"config_db": {
"DEVICE_METADATA": {
"localhost": {
"type": "SpineRouter",
}
},
"KDUMP": {
"config": {
"enabled": "false",
"num_dumps": "3",
"memory": "0M-2G:256M,2G-4G:320M,4G-8G:384M,8G-:448M"
}
},
"FEATURE": {
"bgp": {
"state": "{% if not DEVICE_RUNTIME_METADATA['ETHERNET_PORTS_PRESENT'] or ('CHASSIS_METADATA' in DEVICE_RUNTIME_METADATA and DEVICE_RUNTIME_METADATA['CHASSIS_METADATA']['module_type'] in ['supervisor']) %}disabled{% else %}enabled{% endif %}",
"has_timer": "False",
"has_global_scope": "True",
"has_per_asic_scope": "True",
"auto_restart": "enabled",
"high_mem_alert": "disabled"
},
"teamd": {
"state": "{% if not DEVICE_RUNTIME_METADATA['ETHERNET_PORTS_PRESENT'] %}disabled{% else %}enabled{% endif %}",
"has_timer": "False",
"has_global_scope": "True",
"has_per_asic_scope": "True",
"auto_restart": "enabled",
"high_mem_alert": "disabled"
},
"lldp": {
"state": "enabled",
"has_timer": "False",
"has_global_scope": "True",
"has_per_asic_scope": "{% if not DEVICE_RUNTIME_METADATA['ETHERNET_PORTS_PRESENT'] or ('CHASSIS_METADATA' in DEVICE_RUNTIME_METADATA and DEVICE_RUNTIME_METADATA['CHASSIS_METADATA']['module_type'] in ['supervisor']) %}False{% else %}True{% endif %}",
"auto_restart": "enabled",
"high_mem_alert": "disabled"
},
},
},
"expected_config_db": {
"FEATURE": {
"bgp": {
"auto_restart": "enabled",
"has_global_scope": "True",
"has_per_asic_scope": "True",
"has_timer": "False",
"high_mem_alert": "disabled",
"state": "enabled"
},
"teamd": {
"auto_restart": "enabled",
"has_global_scope": "True",
"has_per_asic_scope": "True",
"has_timer": "False",
"high_mem_alert": "disabled",
"state": "enabled"
},
"lldp": {
"auto_restart": "enabled",
"has_global_scope": "True",
"has_per_asic_scope": "True",
"has_timer": "False",
"high_mem_alert": "disabled",
"state": "enabled"
},
},
},
"enable_feature_subprocess_calls": [
call("sudo systemctl start bgp.service", shell=True),
call("sudo systemctl enable bgp.service", shell=True),
call("sudo systemctl unmask bgp.service", shell=True),
call("sudo systemctl start teamd.service", shell=True),
call("sudo systemctl enable teamd.service", shell=True),
call("sudo systemctl unmask teamd.service", shell=True),
],
"daemon_reload_subprocess_call": [
call("sudo systemctl daemon-reload", shell=True),
],
"popen_attributes": {
'communicate.return_value': ('output', 'error')
},
},
],
[
"Chassis_Supervisor_PACKET_multinpu",
{
"num_npu": 2,
"device_runtime_metadata": {
"DEVICE_RUNTIME_METADATA": {
"CHASSIS_METADATA": {
"module_type": "supervisor",
"chassis_type": "packet"
},
"ETHERNET_PORTS_PRESENT":True
}
},
"config_db": {
"DEVICE_METADATA": {
"localhost": {
"type": "SpineRouter",
}
},
"KDUMP": {
"config": {
"enabled": "false",
"num_dumps": "3",
"memory": "0M-2G:256M,2G-4G:320M,4G-8G:384M,8G-:448M"
}
},
"FEATURE": {
"bgp": {
"state": "{% if not DEVICE_RUNTIME_METADATA['ETHERNET_PORTS_PRESENT'] or ('CHASSIS_METADATA' in DEVICE_RUNTIME_METADATA and DEVICE_RUNTIME_METADATA['CHASSIS_METADATA']['module_type'] in ['supervisor']) %}disabled{% else %}enabled{% endif %}",
"has_timer": "False",
"has_global_scope": "False",
"has_per_asic_scope": "True",
"auto_restart": "enabled",
"high_mem_alert": "disabled"
},
"teamd": {
"state": "{% if not DEVICE_RUNTIME_METADATA['ETHERNET_PORTS_PRESENT'] %}disabled{% else %}enabled{% endif %}",
"has_timer": "False",
"has_global_scope": "False",
"has_per_asic_scope": "True",
"auto_restart": "enabled",
"high_mem_alert": "disabled"
},
"lldp": {
"state": "enabled",
"has_timer": "False",
"has_global_scope": "True",
"has_per_asic_scope": "{% if not DEVICE_RUNTIME_METADATA['ETHERNET_PORTS_PRESENT'] or ('CHASSIS_METADATA' in DEVICE_RUNTIME_METADATA and DEVICE_RUNTIME_METADATA['CHASSIS_METADATA']['module_type'] in ['supervisor']) %}False{% else %}True{% endif %}",
"auto_restart": "enabled",
"high_mem_alert": "disabled"
},
},
},
"expected_config_db": {
"FEATURE": {
"bgp": {
"auto_restart": "enabled",
"has_global_scope": "False",
"has_per_asic_scope": "True",
"has_timer": "False",
"high_mem_alert": "disabled",
"state": "disabled"
},
"teamd": {
"auto_restart": "enabled",
"has_global_scope": "False",
"has_per_asic_scope": "True",
"has_timer": "False",
"high_mem_alert": "disabled",
"state": "enabled"
},
"lldp": {
"auto_restart": "enabled",
"has_global_scope": "True",
"has_per_asic_scope": "False",
"has_timer": "False",
"high_mem_alert": "disabled",
"state": "enabled"
},
},
},
"enable_feature_subprocess_calls": [
call("sudo systemctl stop bgp@0.service", shell=True),
call("sudo systemctl disable bgp@0.service", shell=True),
call("sudo systemctl mask bgp@0.service", shell=True),
call("sudo systemctl stop bgp@1.service", shell=True),
call("sudo systemctl disable bgp@1.service", shell=True),
call("sudo systemctl mask bgp@1.service", shell=True),
call("sudo systemctl start teamd@0.service", shell=True),
call("sudo systemctl enable teamd@0.service", shell=True),
call("sudo systemctl unmask teamd@0.service", shell=True),
call("sudo systemctl start teamd@1.service", shell=True),
call("sudo systemctl enable teamd@1.service", shell=True),
call("sudo systemctl unmask teamd@1.service", shell=True),
call("sudo systemctl stop lldp@0.service", shell=True),
call("sudo systemctl disable lldp@0.service", shell=True),
call("sudo systemctl mask lldp@0.service", shell=True),
call("sudo systemctl stop lldp@1.service", shell=True),
call("sudo systemctl disable lldp@1.service", shell=True),
call("sudo systemctl mask lldp@1.service", shell=True),
],
"daemon_reload_subprocess_call": [
call("sudo systemctl daemon-reload", shell=True),
],
"popen_attributes": {
'communicate.return_value': ('output', 'error')
},
},
],
] ]
HOSTCFG_DAEMON_CFG_DB = { HOSTCFG_DAEMON_CFG_DB = {