Skip to content

Commit 866d1d7

Browse files
authored
[minigraph][port_config] Consume port_config.json while reloading minigraph (#1705)
Signed-off-by: Jing Kan jika@microsoft.com
1 parent 9ae6f6b commit 866d1d7

File tree

2 files changed

+96
-1
lines changed

2 files changed

+96
-1
lines changed

config/main.py

+44
Original file line numberDiff line numberDiff line change
@@ -1441,6 +1441,12 @@ def load_minigraph(db, no_service_restart):
14411441
if os.path.isfile('/etc/sonic/acl.json'):
14421442
clicommon.run_command("acl-loader update full /etc/sonic/acl.json", display_cmd=True)
14431443

1444+
# Load port_config.json
1445+
try:
1446+
load_port_config(db.cfgdb, '/etc/sonic/port_config.json')
1447+
except Exception as e:
1448+
click.secho("Failed to load port_config.json, Error: {}".format(str(e)), fg='magenta')
1449+
14441450
# generate QoS and Buffer configs
14451451
clicommon.run_command("config qos reload --no-dynamic-buffer", display_cmd=True)
14461452

@@ -1463,6 +1469,44 @@ def load_minigraph(db, no_service_restart):
14631469
_restart_services()
14641470
click.echo("Please note setting loaded from minigraph will be lost after system reboot. To preserve setting, run `config save`.")
14651471

1472+
def load_port_config(config_db, port_config_path):
1473+
if not os.path.isfile(port_config_path):
1474+
return
1475+
1476+
try:
1477+
# Load port_config.json
1478+
port_config_input = read_json_file(port_config_path)
1479+
except Exception:
1480+
raise Exception("Bad format: json file broken")
1481+
1482+
# Validate if the input is an array
1483+
if not isinstance(port_config_input, list):
1484+
raise Exception("Bad format: port_config is not an array")
1485+
1486+
if len(port_config_input) == 0 or 'PORT' not in port_config_input[0]:
1487+
raise Exception("Bad format: PORT table not exists")
1488+
1489+
port_config = port_config_input[0]['PORT']
1490+
1491+
# Ensure all ports are exist
1492+
port_table = {}
1493+
for port_name in port_config.keys():
1494+
port_entry = config_db.get_entry('PORT', port_name)
1495+
if not port_entry:
1496+
raise Exception("Port {} is not defined in current device".format(port_name))
1497+
port_table[port_name] = port_entry
1498+
1499+
# Update port state
1500+
for port_name in port_config.keys():
1501+
if 'admin_status' not in port_config[port_name]:
1502+
continue
1503+
if 'admin_status' in port_table[port_name]:
1504+
if port_table[port_name]['admin_status'] == port_config[port_name]['admin_status']:
1505+
continue
1506+
clicommon.run_command('config interface {} {}'.format(
1507+
'startup' if port_config[port_name]['admin_status'] == 'up' else 'shutdown',
1508+
port_name), display_cmd=True)
1509+
return
14661510

14671511
#
14681512
# 'hostname' command

tests/config_test.py

+52-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ def mock_run_command_side_effect(*args, **kwargs):
3737
if kwargs.get('return_cmd'):
3838
return ''
3939

40-
4140
class TestLoadMinigraph(object):
4241
@classmethod
4342
def setup_class(cls):
@@ -58,6 +57,58 @@ def test_load_minigraph(self, get_cmd_module, setup_single_broadcom_asic):
5857
assert "\n".join([l.rstrip() for l in result.output.split('\n')]) == load_minigraph_command_output
5958
assert mock_run_command.call_count == 7
6059

60+
def test_load_minigraph_with_port_config_bad_format(self, setup_single_broadcom_asic):
61+
with mock.patch(
62+
"utilities_common.cli.run_command",
63+
mock.MagicMock(side_effect=mock_run_command_side_effect)) as mock_run_command:
64+
65+
# Not in an array
66+
port_config = {"PORT": {"Ethernet0": {"admin_status": "up"}}}
67+
self.check_port_config(None, port_config, "Failed to load port_config.json, Error: Bad format: port_config is not an array")
68+
69+
# No PORT table
70+
port_config = [{}]
71+
self.check_port_config(None, port_config, "Failed to load port_config.json, Error: Bad format: PORT table not exists")
72+
73+
def test_load_minigraph_with_port_config_inconsistent_port(self, setup_single_broadcom_asic):
74+
with mock.patch(
75+
"utilities_common.cli.run_command",
76+
mock.MagicMock(side_effect=mock_run_command_side_effect)) as mock_run_command:
77+
db = Db()
78+
db.cfgdb.set_entry("PORT", "Ethernet1", {"admin_status": "up"})
79+
port_config = [{"PORT": {"Eth1": {"admin_status": "up"}}}]
80+
self.check_port_config(db, port_config, "Failed to load port_config.json, Error: Port Eth1 is not defined in current device")
81+
82+
def test_load_minigraph_with_port_config(self, setup_single_broadcom_asic):
83+
with mock.patch(
84+
"utilities_common.cli.run_command",
85+
mock.MagicMock(side_effect=mock_run_command_side_effect)) as mock_run_command:
86+
db = Db()
87+
88+
# From up to down
89+
db.cfgdb.set_entry("PORT", "Ethernet0", {"admin_status": "up"})
90+
port_config = [{"PORT": {"Ethernet0": {"admin_status": "down"}}}]
91+
self.check_port_config(db, port_config, "config interface shutdown Ethernet0")
92+
93+
# From down to up
94+
db.cfgdb.set_entry("PORT", "Ethernet0", {"admin_status": "down"})
95+
port_config = [{"PORT": {"Ethernet0": {"admin_status": "up"}}}]
96+
self.check_port_config(db, port_config, "config interface startup Ethernet0")
97+
98+
def check_port_config(self, db, port_config, expected_output):
99+
def read_json_file_side_effect(filename):
100+
return port_config
101+
with mock.patch('config.main.read_json_file', mock.MagicMock(side_effect=read_json_file_side_effect)):
102+
def is_file_side_effect(filename):
103+
return True
104+
with mock.patch('os.path.isfile', mock.MagicMock(side_effect=is_file_side_effect)):
105+
runner = CliRunner()
106+
result = runner.invoke(config.config.commands["load_minigraph"], ["-y"], obj=db)
107+
print(result.exit_code)
108+
print(result.output)
109+
assert result.exit_code == 0
110+
assert expected_output in result.output
111+
61112
@classmethod
62113
def teardown_class(cls):
63114
os.environ['UTILITIES_UNIT_TESTING'] = "0"

0 commit comments

Comments
 (0)