Skip to content

Commit 1403951

Browse files
Sangita MaityPraveen Chaudhary
Sangita Maity
authored and
Praveen Chaudhary
committed
[config] Fix 'config reload -l' command to get filename by default (sonic-net#1611)
Fix sonic-net/sonic-buildimage#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]> Conflicts: tests/config_test.py
1 parent 6731849 commit 1403951

File tree

4 files changed

+222
-10
lines changed

4 files changed

+222
-10
lines changed

config/main.py

+19-10
Original file line numberDiff line numberDiff line change
@@ -1325,16 +1325,6 @@ def reload(db, filename, yes, load_sysinfo, disable_validation, verbose, no_serv
13251325
click.echo("Input {} config file(s) separated by comma for multiple files ".format(num_cfg_file))
13261326
return
13271327

1328-
if load_sysinfo:
1329-
command = "{} -j {} -v DEVICE_METADATA.localhost.hwsku".format(SONIC_CFGGEN_PATH, filename)
1330-
proc = subprocess.Popen(command, shell=True, text=True, stdout=subprocess.PIPE)
1331-
cfg_hwsku, err = proc.communicate()
1332-
if err:
1333-
click.echo("Could not get the HWSKU from config file, exiting")
1334-
sys.exit(1)
1335-
else:
1336-
cfg_hwsku = cfg_hwsku.strip()
1337-
13381328
# For dual ToR devices, cache ARP and FDB info
13391329
localhost_metadata = db.cfgdb.get_table('DEVICE_METADATA')['localhost']
13401330
cache_arp_table = not disable_arp_cache and 'subtype' in localhost_metadata and localhost_metadata['subtype'].lower() == 'dualtor'
@@ -1378,6 +1368,25 @@ def reload(db, filename, yes, load_sysinfo, disable_validation, verbose, no_serv
13781368
click.echo("The config_db file {} doesn't exist".format(file))
13791369
continue
13801370

1371+
if load_sysinfo:
1372+
try:
1373+
command = "{} -j {} -v DEVICE_METADATA.localhost.hwsku".format(SONIC_CFGGEN_PATH, file)
1374+
proc = subprocess.Popen(command, shell=True, text=True, stdout=subprocess.PIPE)
1375+
output, err = proc.communicate()
1376+
1377+
except FileNotFoundError as e:
1378+
click.echo("{}".format(str(e)), err=True)
1379+
raise click.Abort()
1380+
except Exception as e:
1381+
click.echo("{}\n{}".format(type(e), str(e)), err=True)
1382+
raise click.Abort()
1383+
1384+
if not output:
1385+
click.secho("Could not get the HWSKU from config file, Exiting!!!", fg='magenta')
1386+
sys.exit(1)
1387+
1388+
cfg_hwsku = output.strip()
1389+
13811390
if namespace is None:
13821391
config_db = ConfigDBConnector()
13831392
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

+73
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,21 @@
1010

1111
from sonic_py_common import device_info
1212
from utilities_common.db import Db
13+
from utilities_common.general import load_module_from_source
14+
15+
import config.main as config
16+
17+
# Add Test, module and script path.
18+
test_path = os.path.dirname(os.path.abspath(__file__))
19+
modules_path = os.path.dirname(test_path)
20+
scripts_path = os.path.join(modules_path, "scripts")
21+
sys.path.insert(0, test_path)
22+
sys.path.insert(0, modules_path)
23+
sys.path.insert(0, scripts_path)
24+
os.environ["PATH"] += os.pathsep + scripts_path
25+
26+
# Config Reload input Path
27+
mock_db_path = os.path.join(test_path, "config_reload_input")
1328

1429
load_minigraph_command_output="""\
1530
Stopping SONiC target ...
@@ -21,6 +36,10 @@
2136
Please note setting loaded from minigraph will be lost after system reboot. To preserve setting, run `config save`.
2237
"""
2338

39+
reload_config_with_sys_info_command_output="""\
40+
Running command: rm -rf /tmp/dropstat-*
41+
Running command: /usr/local/bin/sonic-cfggen -H -k Seastone-DX010-25-50 --write-to-db"""
42+
2443
def mock_run_command_side_effect(*args, **kwargs):
2544
command = args[0]
2645

@@ -36,6 +55,60 @@ def mock_run_command_side_effect(*args, **kwargs):
3655
return ''
3756

3857

58+
# Load sonic-cfggen from source since /usr/local/bin/sonic-cfggen does not have .py extension.
59+
sonic_cfggen = load_module_from_source('sonic_cfggen', '/usr/local/bin/sonic-cfggen')
60+
61+
62+
class TestConfigReload(object):
63+
@classmethod
64+
def setup_class(cls):
65+
os.environ['UTILITIES_UNIT_TESTING'] = "1"
66+
print("SETUP")
67+
68+
from .mock_tables import mock_single_asic
69+
importlib.reload(mock_single_asic)
70+
71+
import config.main
72+
importlib.reload(config.main)
73+
74+
def test_config_reload(self, get_cmd_module, setup_single_broadcom_asic):
75+
with mock.patch("utilities_common.cli.run_command", mock.MagicMock(side_effect=mock_run_command_side_effect)) as mock_run_command:
76+
(config, show) = get_cmd_module
77+
78+
jsonfile_config = os.path.join(mock_db_path, "config_db.json")
79+
jsonfile_init_cfg = os.path.join(mock_db_path, "init_cfg.json")
80+
81+
# create object
82+
config.INIT_CFG_FILE = jsonfile_init_cfg
83+
config.DEFAULT_CONFIG_DB_FILE = jsonfile_config
84+
85+
db = Db()
86+
runner = CliRunner()
87+
obj = {'config_db': db.cfgdb}
88+
89+
# simulate 'config reload' to provoke load_sys_info option
90+
result = runner.invoke(config.config.commands["reload"], ["-l", "-n", "-y"], obj=obj)
91+
92+
print(result.exit_code)
93+
print(result.output)
94+
traceback.print_tb(result.exc_info[2])
95+
96+
assert result.exit_code == 0
97+
98+
assert "\n".join([l.rstrip() for l in result.output.split('\n')][:2]) == reload_config_with_sys_info_command_output
99+
100+
@classmethod
101+
def teardown_class(cls):
102+
print("TEARDOWN")
103+
os.environ['UTILITIES_UNIT_TESTING'] = "0"
104+
105+
# change back to single asic config
106+
from .mock_tables import dbconnector
107+
from .mock_tables import mock_single_asic
108+
importlib.reload(mock_single_asic)
109+
dbconnector.load_namespace_config()
110+
111+
39112
class TestLoadMinigraph(object):
40113
@classmethod
41114
def setup_class(cls):

0 commit comments

Comments
 (0)