Skip to content

Commit b5d6659

Browse files
authored
[config/load_mgmt_config] Support load IPv6 mgmt IP (sonic-net#2206)
* [config/load_mgmt_config] Support load IPv6 mgmt IP Signed-off-by: Jing Kan [email protected]
1 parent 3274b0e commit b5d6659

File tree

3 files changed

+150
-11
lines changed

3 files changed

+150
-11
lines changed

config/main.py

+19-10
Original file line numberDiff line numberDiff line change
@@ -1625,16 +1625,25 @@ def load_mgmt_config(filename):
16251625
config_data = parse_device_desc_xml(filename)
16261626
hostname = config_data['DEVICE_METADATA']['localhost']['hostname']
16271627
_change_hostname(hostname)
1628-
mgmt_conf = netaddr.IPNetwork(list(config_data['MGMT_INTERFACE'].keys())[0][1])
1629-
gw_addr = list(config_data['MGMT_INTERFACE'].values())[0]['gwaddr']
1630-
command = "ifconfig eth0 {} netmask {}".format(str(mgmt_conf.ip), str(mgmt_conf.netmask))
1631-
clicommon.run_command(command, display_cmd=True)
1632-
command = "ip route add default via {} dev eth0 table default".format(gw_addr)
1633-
clicommon.run_command(command, display_cmd=True, ignore_error=True)
1634-
command = "ip rule add from {} table default".format(str(mgmt_conf.ip))
1635-
clicommon.run_command(command, display_cmd=True, ignore_error=True)
1636-
command = "[ -f /var/run/dhclient.eth0.pid ] && kill `cat /var/run/dhclient.eth0.pid` && rm -f /var/run/dhclient.eth0.pid"
1637-
clicommon.run_command(command, display_cmd=True, ignore_error=True)
1628+
for key in list(config_data['MGMT_INTERFACE'].keys()):
1629+
# key: (eth0, ipprefix)
1630+
# value: { gwaddr: ip }
1631+
mgmt_conf = netaddr.IPNetwork(key[1])
1632+
gw_addr = config_data['MGMT_INTERFACE'][key]['gwaddr']
1633+
if mgmt_conf.version == 4:
1634+
command = "ifconfig eth0 {} netmask {}".format(str(mgmt_conf.ip), str(mgmt_conf.netmask))
1635+
clicommon.run_command(command, display_cmd=True)
1636+
else:
1637+
command = "ifconfig eth0 add {}".format(str(mgmt_conf))
1638+
# Ignore error for IPv6 configuration command due to it not allows config the same IP twice
1639+
clicommon.run_command(command, display_cmd=True, ignore_error=True)
1640+
command = "ip{} route add default via {} dev eth0 table default".format(" -6" if mgmt_conf.version == 6 else "", gw_addr)
1641+
clicommon.run_command(command, display_cmd=True, ignore_error=True)
1642+
command = "ip{} rule add from {} table default".format(" -6" if mgmt_conf.version == 6 else "", str(mgmt_conf.ip))
1643+
clicommon.run_command(command, display_cmd=True, ignore_error=True)
1644+
if len(config_data['MGMT_INTERFACE'].keys()) > 0:
1645+
command = "[ -f /var/run/dhclient.eth0.pid ] && kill `cat /var/run/dhclient.eth0.pid` && rm -f /var/run/dhclient.eth0.pid"
1646+
clicommon.run_command(command, display_cmd=True, ignore_error=True)
16381647
click.echo("Please note loaded setting will be lost after system reboot. To preserve setting, run `config save`.")
16391648

16401649
@config.command("load_minigraph")

doc/Command-Reference.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -5192,7 +5192,7 @@ When user specifies the optional argument "-f" or "--force", this command ignore
51925192
51935193
This command is used to reconfigure hostname and mgmt interface based on device description file.
51945194
This command either uses the optional file specified as arguement or looks for the file "/etc/sonic/device_desc.xml".
5195-
If the file does not exist or if the file does not have valid fields for "hostname" and "ManagementAddress", it fails.
5195+
If the file does not exist or if the file does not have valid fields for "hostname" and "ManagementAddress" (or "ManagementAddressV6"), it fails.
51965196
51975197
When user specifies the optional argument "-y" or "--yes", this command forces the loading without prompting the user for confirmation.
51985198
If the argument is not specified, it prompts the user to confirm whether user really wants to load this configuration file.

tests/config_test.py

+130
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import jsonpatch
77
import sys
88
import unittest
9+
import ipaddress
910
from unittest import mock
1011

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

46+
load_mgmt_config_command_ipv4_only_output="""\
47+
Running command: /usr/local/bin/sonic-cfggen -M device_desc.xml --write-to-db
48+
parse dummy device_desc.xml
49+
change hostname to dummy
50+
Running command: ifconfig eth0 10.0.0.100 netmask 255.255.255.0
51+
Running command: ip route add default via 10.0.0.1 dev eth0 table default
52+
Running command: ip rule add from 10.0.0.100 table default
53+
Running command: [ -f /var/run/dhclient.eth0.pid ] && kill `cat /var/run/dhclient.eth0.pid` && rm -f /var/run/dhclient.eth0.pid
54+
Please note loaded setting will be lost after system reboot. To preserve setting, run `config save`.
55+
"""
56+
57+
load_mgmt_config_command_ipv6_only_output="""\
58+
Running command: /usr/local/bin/sonic-cfggen -M device_desc.xml --write-to-db
59+
parse dummy device_desc.xml
60+
change hostname to dummy
61+
Running command: ifconfig eth0 add fc00:1::32/64
62+
Running command: ip -6 route add default via fc00:1::1 dev eth0 table default
63+
Running command: ip -6 rule add from fc00:1::32 table default
64+
Running command: [ -f /var/run/dhclient.eth0.pid ] && kill `cat /var/run/dhclient.eth0.pid` && rm -f /var/run/dhclient.eth0.pid
65+
Please note loaded setting will be lost after system reboot. To preserve setting, run `config save`.
66+
"""
67+
68+
load_mgmt_config_command_ipv4_ipv6_output="""\
69+
Running command: /usr/local/bin/sonic-cfggen -M device_desc.xml --write-to-db
70+
parse dummy device_desc.xml
71+
change hostname to dummy
72+
Running command: ifconfig eth0 10.0.0.100 netmask 255.255.255.0
73+
Running command: ip route add default via 10.0.0.1 dev eth0 table default
74+
Running command: ip rule add from 10.0.0.100 table default
75+
Running command: ifconfig eth0 add fc00:1::32/64
76+
Running command: ip -6 route add default via fc00:1::1 dev eth0 table default
77+
Running command: ip -6 rule add from fc00:1::32 table default
78+
Running command: [ -f /var/run/dhclient.eth0.pid ] && kill `cat /var/run/dhclient.eth0.pid` && rm -f /var/run/dhclient.eth0.pid
79+
Please note loaded setting will be lost after system reboot. To preserve setting, run `config save`.
80+
"""
4581

4682
RELOAD_CONFIG_DB_OUTPUT = """\
4783
Running command: rm -rf /tmp/dropstat-*
@@ -1356,3 +1392,97 @@ def validate_list_checkpoints_optional_parameter(self, param_args, expected_call
13561392
self.assertTrue(expected_output in result.output)
13571393
mock_generic_updater.list_checkpoints.assert_called_once()
13581394
mock_generic_updater.list_checkpoints.assert_has_calls([expected_call])
1395+
1396+
1397+
class TestConfigLoadMgmtConfig(object):
1398+
@classmethod
1399+
def setup_class(cls):
1400+
os.environ['UTILITIES_UNIT_TESTING'] = "1"
1401+
print("SETUP")
1402+
1403+
from .mock_tables import mock_single_asic
1404+
importlib.reload(mock_single_asic)
1405+
1406+
import config.main
1407+
importlib.reload(config.main)
1408+
1409+
def test_config_load_mgmt_config_ipv4_only(self, get_cmd_module, setup_single_broadcom_asic):
1410+
device_desc_result = {
1411+
'DEVICE_METADATA': {
1412+
'localhost': {
1413+
'hostname': 'dummy'
1414+
}
1415+
},
1416+
'MGMT_INTERFACE': {
1417+
('eth0', '10.0.0.100/24') : {
1418+
'gwaddr': ipaddress.ip_address(u'10.0.0.1')
1419+
}
1420+
}
1421+
}
1422+
self.check_output(get_cmd_module, device_desc_result, load_mgmt_config_command_ipv4_only_output, 5)
1423+
1424+
def test_config_load_mgmt_config_ipv6_only(self, get_cmd_module, setup_single_broadcom_asic):
1425+
device_desc_result = {
1426+
'DEVICE_METADATA': {
1427+
'localhost': {
1428+
'hostname': 'dummy'
1429+
}
1430+
},
1431+
'MGMT_INTERFACE': {
1432+
('eth0', 'FC00:1::32/64') : {
1433+
'gwaddr': ipaddress.ip_address(u'fc00:1::1')
1434+
}
1435+
}
1436+
}
1437+
self.check_output(get_cmd_module, device_desc_result, load_mgmt_config_command_ipv6_only_output, 5)
1438+
1439+
def test_config_load_mgmt_config_ipv4_ipv6(self, get_cmd_module, setup_single_broadcom_asic):
1440+
device_desc_result = {
1441+
'DEVICE_METADATA': {
1442+
'localhost': {
1443+
'hostname': 'dummy'
1444+
}
1445+
},
1446+
'MGMT_INTERFACE': {
1447+
('eth0', '10.0.0.100/24') : {
1448+
'gwaddr': ipaddress.ip_address(u'10.0.0.1')
1449+
},
1450+
('eth0', 'FC00:1::32/64') : {
1451+
'gwaddr': ipaddress.ip_address(u'fc00:1::1')
1452+
}
1453+
}
1454+
}
1455+
self.check_output(get_cmd_module, device_desc_result, load_mgmt_config_command_ipv4_ipv6_output, 8)
1456+
1457+
def check_output(self, get_cmd_module, parse_device_desc_xml_result, expected_output, expected_command_call_count):
1458+
def parse_device_desc_xml_side_effect(filename):
1459+
print("parse dummy device_desc.xml")
1460+
return parse_device_desc_xml_result
1461+
def change_hostname_side_effect(hostname):
1462+
print("change hostname to {}".format(hostname))
1463+
with mock.patch("utilities_common.cli.run_command", mock.MagicMock(side_effect=mock_run_command_side_effect)) as mock_run_command:
1464+
with mock.patch('config.main.parse_device_desc_xml', mock.MagicMock(side_effect=parse_device_desc_xml_side_effect)):
1465+
with mock.patch('config.main._change_hostname', mock.MagicMock(side_effect=change_hostname_side_effect)):
1466+
(config, show) = get_cmd_module
1467+
runner = CliRunner()
1468+
with runner.isolated_filesystem():
1469+
with open('device_desc.xml', 'w') as f:
1470+
f.write('dummy')
1471+
result = runner.invoke(config.config.commands["load_mgmt_config"], ["-y", "device_desc.xml"])
1472+
print(result.exit_code)
1473+
print(result.output)
1474+
traceback.print_tb(result.exc_info[2])
1475+
assert result.exit_code == 0
1476+
assert "\n".join([l.rstrip() for l in result.output.split('\n')]) == expected_output
1477+
assert mock_run_command.call_count == expected_command_call_count
1478+
1479+
@classmethod
1480+
def teardown_class(cls):
1481+
print("TEARDOWN")
1482+
os.environ['UTILITIES_UNIT_TESTING'] = "0"
1483+
1484+
# change back to single asic config
1485+
from .mock_tables import dbconnector
1486+
from .mock_tables import mock_single_asic
1487+
importlib.reload(mock_single_asic)
1488+
dbconnector.load_namespace_config()

0 commit comments

Comments
 (0)