Skip to content

Commit 1a2a9a3

Browse files
author
Sangita Maity
authored
[config] Fix 'config reload -l' command to get filename by default (sonic-net#1611)
Fix sonic-net#7433 Right now config reload -l is getting failed due to an error. I guess the problem is here in sonic-utilities repo. If user does not provide filename with config reload -l, command = "{} -j {} -v DEVICE_METADATA.localhost.hwsku".format(SONIC_CFGGEN_PATH, filename) will not provide cfg_hwsku i.e. hwsku parameter as it should which will later cause problem here command = "{} -H -k {} --write-to-db".format(SONIC_CFGGEN_PATH, cfg_hwsku) as hwsku is not available around that time. that's why we notice errors like No such file or directory: 'None' as pasted in this issue. - How I did it To Fix the issue, moved the part where the code gets cfg_hwsku command = "{} -j {} -v DEVICE_METADATA.localhost.hwsku".format(SONIC_CFGGEN_PATH, file) to the same location it needed as we get filename by default. - How to verify it 'sudo config reload -l' Added test cases. Signed-off-by: Sangita Maity <[email protected]>
1 parent ed2fa69 commit 1a2a9a3

File tree

4 files changed

+221
-10
lines changed

4 files changed

+221
-10
lines changed

config/main.py

+19-10
Original file line numberDiff line numberDiff line change
@@ -1376,16 +1376,6 @@ def reload(db, filename, yes, load_sysinfo, no_service_restart, disable_arp_cach
13761376
click.echo("Input {} config file(s) separated by comma for multiple files ".format(num_cfg_file))
13771377
return
13781378

1379-
if load_sysinfo:
1380-
command = "{} -j {} -v DEVICE_METADATA.localhost.hwsku".format(SONIC_CFGGEN_PATH, filename)
1381-
proc = subprocess.Popen(command, shell=True, text=True, stdout=subprocess.PIPE)
1382-
cfg_hwsku, err = proc.communicate()
1383-
if err:
1384-
click.echo("Could not get the HWSKU from config file, exiting")
1385-
sys.exit(1)
1386-
else:
1387-
cfg_hwsku = cfg_hwsku.strip()
1388-
13891379
# For dual ToR devices, cache ARP and FDB info
13901380
localhost_metadata = db.cfgdb.get_table('DEVICE_METADATA')['localhost']
13911381
cache_arp_table = not disable_arp_cache and 'subtype' in localhost_metadata and localhost_metadata['subtype'].lower() == 'dualtor'
@@ -1427,6 +1417,25 @@ def reload(db, filename, yes, load_sysinfo, no_service_restart, disable_arp_cach
14271417
click.echo("The config file {} doesn't exist".format(file))
14281418
continue
14291419

1420+
if load_sysinfo:
1421+
try:
1422+
command = "{} -j {} -v DEVICE_METADATA.localhost.hwsku".format(SONIC_CFGGEN_PATH, file)
1423+
proc = subprocess.Popen(command, shell=True, text=True, stdout=subprocess.PIPE)
1424+
output, err = proc.communicate()
1425+
1426+
except FileNotFoundError as e:
1427+
click.echo("{}".format(str(e)), err=True)
1428+
raise click.Abort()
1429+
except Exception as e:
1430+
click.echo("{}\n{}".format(type(e), str(e)), err=True)
1431+
raise click.Abort()
1432+
1433+
if not output:
1434+
click.secho("Could not get the HWSKU from config file, Exiting!!!", fg='magenta')
1435+
sys.exit(1)
1436+
1437+
cfg_hwsku = output.strip()
1438+
14301439
if namespace is None:
14311440
config_db = ConfigDBConnector()
14321441
else:
+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
{
2+
"DEVICE_METADATA": {
3+
"localhost": {
4+
"docker_routing_config_mode": "split",
5+
"hostname": "sonic",
6+
"hwsku": "Seastone-DX010-25-50",
7+
"mac": "00:e0:ec:89:6e:48",
8+
"platform": "x86_64-cel_seastone-r0",
9+
"type": "ToRRouter"
10+
}
11+
},
12+
"VLAN_MEMBER": {
13+
"Vlan1000|Ethernet0": {
14+
"tagging_mode": "untagged"
15+
},
16+
"Vlan1000|Ethernet4": {
17+
"tagging_mode": "untagged"
18+
},
19+
"Vlan1000|Ethernet8": {
20+
"tagging_mode": "untagged"
21+
}
22+
},
23+
"VLAN": {
24+
"Vlan1000": {
25+
"vlanid": "1000",
26+
"dhcp_servers": [
27+
"192.0.0.1",
28+
"192.0.0.2",
29+
"192.0.0.3",
30+
"192.0.0.4"
31+
]
32+
}
33+
},
34+
"PORT": {
35+
"Ethernet0": {
36+
"alias": "Eth1",
37+
"lanes": "65, 66, 67, 68",
38+
"description": "Ethernet0 100G link",
39+
"speed": "100000"
40+
},
41+
"Ethernet4": {
42+
"admin_status": "up",
43+
"alias": "fortyGigE0/4",
44+
"description": "Servers0:eth0",
45+
"index": "1",
46+
"lanes": "29,30,31,32",
47+
"mtu": "9100",
48+
"pfc_asym": "off",
49+
"speed": "40000"
50+
},
51+
"Ethernet8": {
52+
"admin_status": "up",
53+
"alias": "fortyGigE0/8",
54+
"description": "Servers1:eth0",
55+
"index": "2",
56+
"lanes": "33,34,35,36",
57+
"mtu": "9100",
58+
"pfc_asym": "off",
59+
"speed": "40000"
60+
}
61+
}
62+
}
+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
{
2+
"DEVICE_METADATA": {
3+
"localhost": {
4+
"buffer_model": "traditional",
5+
"default_bgp_status": "up",
6+
"default_pfcwd_status": "disable"
7+
}
8+
},
9+
"CRM": {
10+
"Config": {
11+
"polling_interval": "300",
12+
"ipv4_route_threshold_type": "percentage",
13+
"ipv4_route_low_threshold": "70",
14+
"ipv4_route_high_threshold": "85",
15+
"ipv6_route_threshold_type": "percentage",
16+
"ipv6_route_low_threshold": "70",
17+
"ipv6_route_high_threshold": "85",
18+
"ipv4_nexthop_threshold_type": "percentage",
19+
"ipv4_nexthop_low_threshold": "70",
20+
"ipv4_nexthop_high_threshold": "85",
21+
"ipv6_nexthop_threshold_type": "percentage",
22+
"ipv6_nexthop_low_threshold": "70",
23+
"ipv6_nexthop_high_threshold": "85",
24+
"ipv4_neighbor_threshold_type": "percentage",
25+
"ipv4_neighbor_low_threshold": "70",
26+
"ipv4_neighbor_high_threshold": "85",
27+
"ipv6_neighbor_threshold_type": "percentage",
28+
"ipv6_neighbor_low_threshold": "70",
29+
"ipv6_neighbor_high_threshold": "85",
30+
"nexthop_group_member_threshold_type": "percentage",
31+
"nexthop_group_member_low_threshold": "70",
32+
"nexthop_group_member_high_threshold": "85",
33+
"nexthop_group_threshold_type": "percentage",
34+
"nexthop_group_low_threshold": "70",
35+
"nexthop_group_high_threshold": "85",
36+
"acl_table_threshold_type": "percentage",
37+
"acl_table_low_threshold": "70",
38+
"acl_table_high_threshold": "85",
39+
"acl_group_threshold_type": "percentage",
40+
"acl_group_low_threshold": "70",
41+
"acl_group_high_threshold": "85",
42+
"acl_entry_threshold_type": "percentage",
43+
"acl_entry_low_threshold": "70",
44+
"acl_entry_high_threshold": "85",
45+
"acl_counter_threshold_type": "percentage",
46+
"acl_counter_low_threshold": "70",
47+
"acl_counter_high_threshold": "85",
48+
"fdb_entry_threshold_type": "percentage",
49+
"fdb_entry_low_threshold": "70",
50+
"fdb_entry_high_threshold": "85",
51+
"snat_entry_threshold_type": "percentage",
52+
"snat_entry_low_threshold": "70",
53+
"snat_entry_high_threshold": "85",
54+
"dnat_entry_threshold_type": "percentage",
55+
"dnat_entry_low_threshold": "70",
56+
"dnat_entry_high_threshold": "85",
57+
"ipmc_entry_threshold_type": "percentage",
58+
"ipmc_entry_low_threshold": "70",
59+
"ipmc_entry_high_threshold": "85",
60+
"mpls_inseg_threshold_type": "percentage",
61+
"mpls_inseg_low_threshold": "70",
62+
"mpls_inseg_high_threshold": "85",
63+
"mpls_nexthop_threshold_type": "percentage",
64+
"mpls_nexthop_low_threshold": "70",
65+
"mpls_nexthop_high_threshold": "85"
66+
}
67+
}
68+
}

tests/config_test.py

+72
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,25 @@
1313

1414
from sonic_py_common import device_info
1515
from utilities_common.db import Db
16+
from utilities_common.general import load_module_from_source
1617

1718
from generic_config_updater.generic_updater import ConfigFormat
1819

1920
import config.main as config
2021

22+
# Add Test, module and script path.
23+
test_path = os.path.dirname(os.path.abspath(__file__))
24+
modules_path = os.path.dirname(test_path)
25+
scripts_path = os.path.join(modules_path, "scripts")
26+
sys.path.insert(0, test_path)
27+
sys.path.insert(0, modules_path)
28+
sys.path.insert(0, scripts_path)
29+
os.environ["PATH"] += os.pathsep + scripts_path
30+
31+
# Config Reload input Path
32+
mock_db_path = os.path.join(test_path, "config_reload_input")
33+
34+
2135
load_minigraph_command_output="""\
2236
Stopping SONiC target ...
2337
Running command: /usr/local/bin/sonic-cfggen -H -m --write-to-db
@@ -55,6 +69,10 @@
5569
Reloading Monit configuration ...
5670
"""
5771

72+
reload_config_with_sys_info_command_output="""\
73+
Running command: rm -rf /tmp/dropstat-*
74+
Running command: /usr/local/bin/sonic-cfggen -H -k Seastone-DX010-25-50 --write-to-db"""
75+
5876
def mock_run_command_side_effect(*args, **kwargs):
5977
command = args[0]
6078

@@ -70,6 +88,60 @@ def mock_run_command_side_effect(*args, **kwargs):
7088
return ''
7189

7290

91+
# Load sonic-cfggen from source since /usr/local/bin/sonic-cfggen does not have .py extension.
92+
sonic_cfggen = load_module_from_source('sonic_cfggen', '/usr/local/bin/sonic-cfggen')
93+
94+
95+
class TestConfigReload(object):
96+
@classmethod
97+
def setup_class(cls):
98+
os.environ['UTILITIES_UNIT_TESTING'] = "1"
99+
print("SETUP")
100+
101+
from .mock_tables import mock_single_asic
102+
importlib.reload(mock_single_asic)
103+
104+
import config.main
105+
importlib.reload(config.main)
106+
107+
def test_config_reload(self, get_cmd_module, setup_single_broadcom_asic):
108+
with mock.patch("utilities_common.cli.run_command", mock.MagicMock(side_effect=mock_run_command_side_effect)) as mock_run_command:
109+
(config, show) = get_cmd_module
110+
111+
jsonfile_config = os.path.join(mock_db_path, "config_db.json")
112+
jsonfile_init_cfg = os.path.join(mock_db_path, "init_cfg.json")
113+
114+
# create object
115+
config.INIT_CFG_FILE = jsonfile_init_cfg
116+
config.DEFAULT_CONFIG_DB_FILE = jsonfile_config
117+
118+
db = Db()
119+
runner = CliRunner()
120+
obj = {'config_db': db.cfgdb}
121+
122+
# simulate 'config reload' to provoke load_sys_info option
123+
result = runner.invoke(config.config.commands["reload"], ["-l", "-n", "-y"], obj=obj)
124+
125+
print(result.exit_code)
126+
print(result.output)
127+
traceback.print_tb(result.exc_info[2])
128+
129+
assert result.exit_code == 0
130+
131+
assert "\n".join([l.rstrip() for l in result.output.split('\n')][:2]) == reload_config_with_sys_info_command_output
132+
133+
@classmethod
134+
def teardown_class(cls):
135+
print("TEARDOWN")
136+
os.environ['UTILITIES_UNIT_TESTING'] = "0"
137+
138+
# change back to single asic config
139+
from .mock_tables import dbconnector
140+
from .mock_tables import mock_single_asic
141+
importlib.reload(mock_single_asic)
142+
dbconnector.load_namespace_config()
143+
144+
73145
class TestLoadMinigraph(object):
74146
@classmethod
75147
def setup_class(cls):

0 commit comments

Comments
 (0)