Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions config/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -1300,6 +1300,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 @@ -1322,6 +1328,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
57 changes: 56 additions & 1 deletion tests/config_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,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 @@ -51,6 +50,62 @@ 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, get_cmd_module, 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:
(config, show) = get_cmd_module

# Not in an array
port_config = {"PORT": {"Ethernet0": {"admin_status": "up"}}}
self.check_port_config(None, config, 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, config, 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, get_cmd_module, 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:
(config, show) = get_cmd_module

db = Db()
db.cfgdb.set_entry("PORT", "Ethernet1", {"admin_status": "up"})
port_config = [{"PORT": {"Eth1": {"admin_status": "up"}}}]
self.check_port_config(db, config, 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, get_cmd_module, 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:
(config, show) = get_cmd_module
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, config, 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, config, port_config, "config interface startup Ethernet0")

def check_port_config(self, db, config, 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 if 'port_config' in filename else False
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