Skip to content

Commit

Permalink
[minigraph][port_config] Consume port_config.json while reloading min…
Browse files Browse the repository at this point in the history
…igraph (sonic-net#1705)

Signed-off-by: Jing Kan jika@microsoft.com
  • Loading branch information
Blueve committed Jul 15, 2021
1 parent 9ae6f6b commit 866d1d7
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 1 deletion.
44 changes: 44 additions & 0 deletions config/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -1441,6 +1441,12 @@ def load_minigraph(db, no_service_restart):
if os.path.isfile('/etc/sonic/acl.json'):
clicommon.run_command("acl-loader update full /etc/sonic/acl.json", display_cmd=True)

# Load port_config.json
try:
load_port_config(db.cfgdb, '/etc/sonic/port_config.json')
except Exception as e:
click.secho("Failed to load port_config.json, Error: {}".format(str(e)), fg='magenta')

# generate QoS and Buffer configs
clicommon.run_command("config qos reload --no-dynamic-buffer", display_cmd=True)

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

def load_port_config(config_db, port_config_path):
if not os.path.isfile(port_config_path):
return

try:
# Load port_config.json
port_config_input = read_json_file(port_config_path)
except Exception:
raise Exception("Bad format: json file broken")

# Validate if the input is an array
if not isinstance(port_config_input, list):
raise Exception("Bad format: port_config is not an array")

if len(port_config_input) == 0 or 'PORT' not in port_config_input[0]:
raise Exception("Bad format: PORT table not exists")

port_config = port_config_input[0]['PORT']

# Ensure all ports are exist
port_table = {}
for port_name in port_config.keys():
port_entry = config_db.get_entry('PORT', port_name)
if not port_entry:
raise Exception("Port {} is not defined in current device".format(port_name))
port_table[port_name] = port_entry

# Update port state
for port_name in port_config.keys():
if 'admin_status' not in port_config[port_name]:
continue
if 'admin_status' in port_table[port_name]:
if port_table[port_name]['admin_status'] == port_config[port_name]['admin_status']:
continue
clicommon.run_command('config interface {} {}'.format(
'startup' if port_config[port_name]['admin_status'] == 'up' else 'shutdown',
port_name), display_cmd=True)
return

#
# 'hostname' command
Expand Down
53 changes: 52 additions & 1 deletion tests/config_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ def mock_run_command_side_effect(*args, **kwargs):
if kwargs.get('return_cmd'):
return ''


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

def test_load_minigraph_with_port_config_bad_format(self, setup_single_broadcom_asic):
with mock.patch(
"utilities_common.cli.run_command",
mock.MagicMock(side_effect=mock_run_command_side_effect)) as mock_run_command:

# Not in an array
port_config = {"PORT": {"Ethernet0": {"admin_status": "up"}}}
self.check_port_config(None, port_config, "Failed to load port_config.json, Error: Bad format: port_config is not an array")

# No PORT table
port_config = [{}]
self.check_port_config(None, port_config, "Failed to load port_config.json, Error: Bad format: PORT table not exists")

def test_load_minigraph_with_port_config_inconsistent_port(self, setup_single_broadcom_asic):
with mock.patch(
"utilities_common.cli.run_command",
mock.MagicMock(side_effect=mock_run_command_side_effect)) as mock_run_command:
db = Db()
db.cfgdb.set_entry("PORT", "Ethernet1", {"admin_status": "up"})
port_config = [{"PORT": {"Eth1": {"admin_status": "up"}}}]
self.check_port_config(db, port_config, "Failed to load port_config.json, Error: Port Eth1 is not defined in current device")

def test_load_minigraph_with_port_config(self, setup_single_broadcom_asic):
with mock.patch(
"utilities_common.cli.run_command",
mock.MagicMock(side_effect=mock_run_command_side_effect)) as mock_run_command:
db = Db()

# From up to down
db.cfgdb.set_entry("PORT", "Ethernet0", {"admin_status": "up"})
port_config = [{"PORT": {"Ethernet0": {"admin_status": "down"}}}]
self.check_port_config(db, port_config, "config interface shutdown Ethernet0")

# From down to up
db.cfgdb.set_entry("PORT", "Ethernet0", {"admin_status": "down"})
port_config = [{"PORT": {"Ethernet0": {"admin_status": "up"}}}]
self.check_port_config(db, port_config, "config interface startup Ethernet0")

def check_port_config(self, db, port_config, expected_output):
def read_json_file_side_effect(filename):
return port_config
with mock.patch('config.main.read_json_file', mock.MagicMock(side_effect=read_json_file_side_effect)):
def is_file_side_effect(filename):
return True
with mock.patch('os.path.isfile', mock.MagicMock(side_effect=is_file_side_effect)):
runner = CliRunner()
result = runner.invoke(config.config.commands["load_minigraph"], ["-y"], obj=db)
print(result.exit_code)
print(result.output)
assert result.exit_code == 0
assert expected_output in result.output

@classmethod
def teardown_class(cls):
os.environ['UTILITIES_UNIT_TESTING'] = "0"
Expand Down

0 comments on commit 866d1d7

Please sign in to comment.