Skip to content

Commit

Permalink
[config/load_mgmt_config] Support load IPv6 mgmt IP (#2206)
Browse files Browse the repository at this point in the history
* [config/load_mgmt_config] Support load IPv6 mgmt IP

Signed-off-by: Jing Kan [email protected]
  • Loading branch information
Blueve authored Jul 1, 2022
1 parent 3274b0e commit b5d6659
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 11 deletions.
29 changes: 19 additions & 10 deletions config/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -1625,16 +1625,25 @@ def load_mgmt_config(filename):
config_data = parse_device_desc_xml(filename)
hostname = config_data['DEVICE_METADATA']['localhost']['hostname']
_change_hostname(hostname)
mgmt_conf = netaddr.IPNetwork(list(config_data['MGMT_INTERFACE'].keys())[0][1])
gw_addr = list(config_data['MGMT_INTERFACE'].values())[0]['gwaddr']
command = "ifconfig eth0 {} netmask {}".format(str(mgmt_conf.ip), str(mgmt_conf.netmask))
clicommon.run_command(command, display_cmd=True)
command = "ip route add default via {} dev eth0 table default".format(gw_addr)
clicommon.run_command(command, display_cmd=True, ignore_error=True)
command = "ip rule add from {} table default".format(str(mgmt_conf.ip))
clicommon.run_command(command, display_cmd=True, ignore_error=True)
command = "[ -f /var/run/dhclient.eth0.pid ] && kill `cat /var/run/dhclient.eth0.pid` && rm -f /var/run/dhclient.eth0.pid"
clicommon.run_command(command, display_cmd=True, ignore_error=True)
for key in list(config_data['MGMT_INTERFACE'].keys()):
# key: (eth0, ipprefix)
# value: { gwaddr: ip }
mgmt_conf = netaddr.IPNetwork(key[1])
gw_addr = config_data['MGMT_INTERFACE'][key]['gwaddr']
if mgmt_conf.version == 4:
command = "ifconfig eth0 {} netmask {}".format(str(mgmt_conf.ip), str(mgmt_conf.netmask))
clicommon.run_command(command, display_cmd=True)
else:
command = "ifconfig eth0 add {}".format(str(mgmt_conf))
# Ignore error for IPv6 configuration command due to it not allows config the same IP twice
clicommon.run_command(command, display_cmd=True, ignore_error=True)
command = "ip{} route add default via {} dev eth0 table default".format(" -6" if mgmt_conf.version == 6 else "", gw_addr)
clicommon.run_command(command, display_cmd=True, ignore_error=True)
command = "ip{} rule add from {} table default".format(" -6" if mgmt_conf.version == 6 else "", str(mgmt_conf.ip))
clicommon.run_command(command, display_cmd=True, ignore_error=True)
if len(config_data['MGMT_INTERFACE'].keys()) > 0:
command = "[ -f /var/run/dhclient.eth0.pid ] && kill `cat /var/run/dhclient.eth0.pid` && rm -f /var/run/dhclient.eth0.pid"
clicommon.run_command(command, display_cmd=True, ignore_error=True)
click.echo("Please note loaded setting will be lost after system reboot. To preserve setting, run `config save`.")

@config.command("load_minigraph")
Expand Down
2 changes: 1 addition & 1 deletion doc/Command-Reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -5192,7 +5192,7 @@ When user specifies the optional argument "-f" or "--force", this command ignore
This command is used to reconfigure hostname and mgmt interface based on device description file.
This command either uses the optional file specified as arguement or looks for the file "/etc/sonic/device_desc.xml".
If the file does not exist or if the file does not have valid fields for "hostname" and "ManagementAddress", it fails.
If the file does not exist or if the file does not have valid fields for "hostname" and "ManagementAddress" (or "ManagementAddressV6"), it fails.
When user specifies the optional argument "-y" or "--yes", this command forces the loading without prompting the user for confirmation.
If the argument is not specified, it prompts the user to confirm whether user really wants to load this configuration file.
Expand Down
130 changes: 130 additions & 0 deletions tests/config_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import jsonpatch
import sys
import unittest
import ipaddress
from unittest import mock

import click
Expand Down Expand Up @@ -42,6 +43,41 @@
Please note setting loaded from minigraph will be lost after system reboot. To preserve setting, run `config save`.
"""

load_mgmt_config_command_ipv4_only_output="""\
Running command: /usr/local/bin/sonic-cfggen -M device_desc.xml --write-to-db
parse dummy device_desc.xml
change hostname to dummy
Running command: ifconfig eth0 10.0.0.100 netmask 255.255.255.0
Running command: ip route add default via 10.0.0.1 dev eth0 table default
Running command: ip rule add from 10.0.0.100 table default
Running command: [ -f /var/run/dhclient.eth0.pid ] && kill `cat /var/run/dhclient.eth0.pid` && rm -f /var/run/dhclient.eth0.pid
Please note loaded setting will be lost after system reboot. To preserve setting, run `config save`.
"""

load_mgmt_config_command_ipv6_only_output="""\
Running command: /usr/local/bin/sonic-cfggen -M device_desc.xml --write-to-db
parse dummy device_desc.xml
change hostname to dummy
Running command: ifconfig eth0 add fc00:1::32/64
Running command: ip -6 route add default via fc00:1::1 dev eth0 table default
Running command: ip -6 rule add from fc00:1::32 table default
Running command: [ -f /var/run/dhclient.eth0.pid ] && kill `cat /var/run/dhclient.eth0.pid` && rm -f /var/run/dhclient.eth0.pid
Please note loaded setting will be lost after system reboot. To preserve setting, run `config save`.
"""

load_mgmt_config_command_ipv4_ipv6_output="""\
Running command: /usr/local/bin/sonic-cfggen -M device_desc.xml --write-to-db
parse dummy device_desc.xml
change hostname to dummy
Running command: ifconfig eth0 10.0.0.100 netmask 255.255.255.0
Running command: ip route add default via 10.0.0.1 dev eth0 table default
Running command: ip rule add from 10.0.0.100 table default
Running command: ifconfig eth0 add fc00:1::32/64
Running command: ip -6 route add default via fc00:1::1 dev eth0 table default
Running command: ip -6 rule add from fc00:1::32 table default
Running command: [ -f /var/run/dhclient.eth0.pid ] && kill `cat /var/run/dhclient.eth0.pid` && rm -f /var/run/dhclient.eth0.pid
Please note loaded setting will be lost after system reboot. To preserve setting, run `config save`.
"""

RELOAD_CONFIG_DB_OUTPUT = """\
Running command: rm -rf /tmp/dropstat-*
Expand Down Expand Up @@ -1356,3 +1392,97 @@ def validate_list_checkpoints_optional_parameter(self, param_args, expected_call
self.assertTrue(expected_output in result.output)
mock_generic_updater.list_checkpoints.assert_called_once()
mock_generic_updater.list_checkpoints.assert_has_calls([expected_call])


class TestConfigLoadMgmtConfig(object):
@classmethod
def setup_class(cls):
os.environ['UTILITIES_UNIT_TESTING'] = "1"
print("SETUP")

from .mock_tables import mock_single_asic
importlib.reload(mock_single_asic)

import config.main
importlib.reload(config.main)

def test_config_load_mgmt_config_ipv4_only(self, get_cmd_module, setup_single_broadcom_asic):
device_desc_result = {
'DEVICE_METADATA': {
'localhost': {
'hostname': 'dummy'
}
},
'MGMT_INTERFACE': {
('eth0', '10.0.0.100/24') : {
'gwaddr': ipaddress.ip_address(u'10.0.0.1')
}
}
}
self.check_output(get_cmd_module, device_desc_result, load_mgmt_config_command_ipv4_only_output, 5)

def test_config_load_mgmt_config_ipv6_only(self, get_cmd_module, setup_single_broadcom_asic):
device_desc_result = {
'DEVICE_METADATA': {
'localhost': {
'hostname': 'dummy'
}
},
'MGMT_INTERFACE': {
('eth0', 'FC00:1::32/64') : {
'gwaddr': ipaddress.ip_address(u'fc00:1::1')
}
}
}
self.check_output(get_cmd_module, device_desc_result, load_mgmt_config_command_ipv6_only_output, 5)

def test_config_load_mgmt_config_ipv4_ipv6(self, get_cmd_module, setup_single_broadcom_asic):
device_desc_result = {
'DEVICE_METADATA': {
'localhost': {
'hostname': 'dummy'
}
},
'MGMT_INTERFACE': {
('eth0', '10.0.0.100/24') : {
'gwaddr': ipaddress.ip_address(u'10.0.0.1')
},
('eth0', 'FC00:1::32/64') : {
'gwaddr': ipaddress.ip_address(u'fc00:1::1')
}
}
}
self.check_output(get_cmd_module, device_desc_result, load_mgmt_config_command_ipv4_ipv6_output, 8)

def check_output(self, get_cmd_module, parse_device_desc_xml_result, expected_output, expected_command_call_count):
def parse_device_desc_xml_side_effect(filename):
print("parse dummy device_desc.xml")
return parse_device_desc_xml_result
def change_hostname_side_effect(hostname):
print("change hostname to {}".format(hostname))
with mock.patch("utilities_common.cli.run_command", mock.MagicMock(side_effect=mock_run_command_side_effect)) as mock_run_command:
with mock.patch('config.main.parse_device_desc_xml', mock.MagicMock(side_effect=parse_device_desc_xml_side_effect)):
with mock.patch('config.main._change_hostname', mock.MagicMock(side_effect=change_hostname_side_effect)):
(config, show) = get_cmd_module
runner = CliRunner()
with runner.isolated_filesystem():
with open('device_desc.xml', 'w') as f:
f.write('dummy')
result = runner.invoke(config.config.commands["load_mgmt_config"], ["-y", "device_desc.xml"])
print(result.exit_code)
print(result.output)
traceback.print_tb(result.exc_info[2])
assert result.exit_code == 0
assert "\n".join([l.rstrip() for l in result.output.split('\n')]) == expected_output
assert mock_run_command.call_count == expected_command_call_count

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

# change back to single asic config
from .mock_tables import dbconnector
from .mock_tables import mock_single_asic
importlib.reload(mock_single_asic)
dbconnector.load_namespace_config()

0 comments on commit b5d6659

Please sign in to comment.