Skip to content

Commit e9789e0

Browse files
committed
[202012_7_rc_1_20220703] [cherry-pick][202012][config/load_mgmt_config] Support load IPv6 mgmt IP (sonic-net#2206)
1 parent 14fed6a commit e9789e0

File tree

3 files changed

+140
-11
lines changed

3 files changed

+140
-11
lines changed

config/main.py

+19-10
Original file line numberDiff line numberDiff line change
@@ -1283,16 +1283,25 @@ def load_mgmt_config(filename):
12831283
config_data = parse_device_desc_xml(filename)
12841284
hostname = config_data['DEVICE_METADATA']['localhost']['hostname']
12851285
_change_hostname(hostname)
1286-
mgmt_conf = netaddr.IPNetwork(list(config_data['MGMT_INTERFACE'].keys())[0][1])
1287-
gw_addr = list(config_data['MGMT_INTERFACE'].values())[0]['gwaddr']
1288-
command = "ifconfig eth0 {} netmask {}".format(str(mgmt_conf.ip), str(mgmt_conf.netmask))
1289-
clicommon.run_command(command, display_cmd=True)
1290-
command = "ip route add default via {} dev eth0 table default".format(gw_addr)
1291-
clicommon.run_command(command, display_cmd=True, ignore_error=True)
1292-
command = "ip rule add from {} table default".format(str(mgmt_conf.ip))
1293-
clicommon.run_command(command, display_cmd=True, ignore_error=True)
1294-
command = "[ -f /var/run/dhclient.eth0.pid ] && kill `cat /var/run/dhclient.eth0.pid` && rm -f /var/run/dhclient.eth0.pid"
1295-
clicommon.run_command(command, display_cmd=True, ignore_error=True)
1286+
for key in list(config_data['MGMT_INTERFACE'].keys()):
1287+
# key: (eth0, ipprefix)
1288+
# value: { gwaddr: ip }
1289+
mgmt_conf = netaddr.IPNetwork(key[1])
1290+
gw_addr = config_data['MGMT_INTERFACE'][key]['gwaddr']
1291+
if mgmt_conf.version == 4:
1292+
command = "ifconfig eth0 {} netmask {}".format(str(mgmt_conf.ip), str(mgmt_conf.netmask))
1293+
clicommon.run_command(command, display_cmd=True)
1294+
else:
1295+
command = "ifconfig eth0 add {}".format(str(mgmt_conf))
1296+
# Ignore error for IPv6 configuration command due to it not allows config the same IP twice
1297+
clicommon.run_command(command, display_cmd=True, ignore_error=True)
1298+
command = "ip{} route add default via {} dev eth0 table default".format(" -6" if mgmt_conf.version == 6 else "", gw_addr)
1299+
clicommon.run_command(command, display_cmd=True, ignore_error=True)
1300+
command = "ip{} rule add from {} table default".format(" -6" if mgmt_conf.version == 6 else "", str(mgmt_conf.ip))
1301+
clicommon.run_command(command, display_cmd=True, ignore_error=True)
1302+
if len(config_data['MGMT_INTERFACE'].keys()) > 0:
1303+
command = "[ -f /var/run/dhclient.eth0.pid ] && kill `cat /var/run/dhclient.eth0.pid` && rm -f /var/run/dhclient.eth0.pid"
1304+
clicommon.run_command(command, display_cmd=True, ignore_error=True)
12961305
click.echo("Please note loaded setting will be lost after system reboot. To preserve setting, run `config save`.")
12971306

12981307
@config.command("load_minigraph")

doc/Command-Reference.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -4635,7 +4635,7 @@ running on the device.
46354635
46364636
This command is used to reconfigure hostname and mgmt interface based on device description file.
46374637
This command either uses the optional file specified as arguement or looks for the file "/etc/sonic/device_desc.xml".
4638-
If the file does not exist or if the file does not have valid fields for "hostname" and "ManagementAddress", it fails.
4638+
If the file does not exist or if the file does not have valid fields for "hostname" and "ManagementAddress" (or "ManagementAddressV6"), it fails.
46394639
46404640
When user specifies the optional argument "-y" or "--yes", this command forces the loading without prompting the user for confirmation.
46414641
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

+120
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import os
44
import traceback
55
import json
6+
import ipaddress
67
from unittest import mock
78

89
import click
@@ -21,6 +22,42 @@
2122
Please note setting loaded from minigraph will be lost after system reboot. To preserve setting, run `config save`.
2223
"""
2324

25+
load_mgmt_config_command_ipv4_only_output="""\
26+
Running command: /usr/local/bin/sonic-cfggen -M device_desc.xml --write-to-db
27+
parse dummy device_desc.xml
28+
change hostname to dummy
29+
Running command: ifconfig eth0 10.0.0.100 netmask 255.255.255.0
30+
Running command: ip route add default via 10.0.0.1 dev eth0 table default
31+
Running command: ip rule add from 10.0.0.100 table default
32+
Running command: [ -f /var/run/dhclient.eth0.pid ] && kill `cat /var/run/dhclient.eth0.pid` && rm -f /var/run/dhclient.eth0.pid
33+
Please note loaded setting will be lost after system reboot. To preserve setting, run `config save`.
34+
"""
35+
36+
load_mgmt_config_command_ipv6_only_output="""\
37+
Running command: /usr/local/bin/sonic-cfggen -M device_desc.xml --write-to-db
38+
parse dummy device_desc.xml
39+
change hostname to dummy
40+
Running command: ifconfig eth0 add fc00:1::32/64
41+
Running command: ip -6 route add default via fc00:1::1 dev eth0 table default
42+
Running command: ip -6 rule add from fc00:1::32 table default
43+
Running command: [ -f /var/run/dhclient.eth0.pid ] && kill `cat /var/run/dhclient.eth0.pid` && rm -f /var/run/dhclient.eth0.pid
44+
Please note loaded setting will be lost after system reboot. To preserve setting, run `config save`.
45+
"""
46+
47+
load_mgmt_config_command_ipv4_ipv6_output="""\
48+
Running command: /usr/local/bin/sonic-cfggen -M device_desc.xml --write-to-db
49+
parse dummy device_desc.xml
50+
change hostname to dummy
51+
Running command: ifconfig eth0 10.0.0.100 netmask 255.255.255.0
52+
Running command: ip route add default via 10.0.0.1 dev eth0 table default
53+
Running command: ip rule add from 10.0.0.100 table default
54+
Running command: ifconfig eth0 add fc00:1::32/64
55+
Running command: ip -6 route add default via fc00:1::1 dev eth0 table default
56+
Running command: ip -6 rule add from fc00:1::32 table default
57+
Running command: [ -f /var/run/dhclient.eth0.pid ] && kill `cat /var/run/dhclient.eth0.pid` && rm -f /var/run/dhclient.eth0.pid
58+
Please note loaded setting will be lost after system reboot. To preserve setting, run `config save`.
59+
"""
60+
2461
def mock_run_command_side_effect(*args, **kwargs):
2562
command = args[0]
2663

@@ -238,3 +275,86 @@ def teardown_class(cls):
238275
from .mock_tables import mock_single_asic
239276
imp.reload(mock_single_asic)
240277
dbconnector.load_namespace_config()
278+
279+
class TestConfigLoadMgmtConfig(object):
280+
@classmethod
281+
def setup_class(cls):
282+
os.environ['UTILITIES_UNIT_TESTING'] = "1"
283+
print("SETUP")
284+
import config.main
285+
imp.reload(config.main)
286+
287+
def test_config_load_mgmt_config_ipv4_only(self, get_cmd_module, setup_single_broadcom_asic):
288+
device_desc_result = {
289+
'DEVICE_METADATA': {
290+
'localhost': {
291+
'hostname': 'dummy'
292+
}
293+
},
294+
'MGMT_INTERFACE': {
295+
('eth0', '10.0.0.100/24') : {
296+
'gwaddr': ipaddress.ip_address(u'10.0.0.1')
297+
}
298+
}
299+
}
300+
self.check_output(get_cmd_module, device_desc_result, load_mgmt_config_command_ipv4_only_output, 5)
301+
302+
def test_config_load_mgmt_config_ipv6_only(self, get_cmd_module, setup_single_broadcom_asic):
303+
device_desc_result = {
304+
'DEVICE_METADATA': {
305+
'localhost': {
306+
'hostname': 'dummy'
307+
}
308+
},
309+
'MGMT_INTERFACE': {
310+
('eth0', 'FC00:1::32/64') : {
311+
'gwaddr': ipaddress.ip_address(u'fc00:1::1')
312+
}
313+
}
314+
}
315+
self.check_output(get_cmd_module, device_desc_result, load_mgmt_config_command_ipv6_only_output, 5)
316+
317+
def test_config_load_mgmt_config_ipv4_ipv6(self, get_cmd_module, setup_single_broadcom_asic):
318+
device_desc_result = {
319+
'DEVICE_METADATA': {
320+
'localhost': {
321+
'hostname': 'dummy'
322+
}
323+
},
324+
'MGMT_INTERFACE': {
325+
('eth0', '10.0.0.100/24') : {
326+
'gwaddr': ipaddress.ip_address(u'10.0.0.1')
327+
},
328+
('eth0', 'FC00:1::32/64') : {
329+
'gwaddr': ipaddress.ip_address(u'fc00:1::1')
330+
}
331+
}
332+
}
333+
self.check_output(get_cmd_module, device_desc_result, load_mgmt_config_command_ipv4_ipv6_output, 8)
334+
335+
def check_output(self, get_cmd_module, parse_device_desc_xml_result, expected_output, expected_command_call_count):
336+
def parse_device_desc_xml_side_effect(filename):
337+
print("parse dummy device_desc.xml")
338+
return parse_device_desc_xml_result
339+
def change_hostname_side_effect(hostname):
340+
print("change hostname to {}".format(hostname))
341+
with mock.patch("utilities_common.cli.run_command", mock.MagicMock(side_effect=mock_run_command_side_effect)) as mock_run_command:
342+
with mock.patch('config.main.parse_device_desc_xml', mock.MagicMock(side_effect=parse_device_desc_xml_side_effect)):
343+
with mock.patch('config.main._change_hostname', mock.MagicMock(side_effect=change_hostname_side_effect)):
344+
(config, show) = get_cmd_module
345+
runner = CliRunner()
346+
with runner.isolated_filesystem():
347+
with open('device_desc.xml', 'w') as f:
348+
f.write('dummy')
349+
result = runner.invoke(config.config.commands["load_mgmt_config"], ["-y", "device_desc.xml"])
350+
print(result.exit_code)
351+
print(result.output)
352+
traceback.print_tb(result.exc_info[2])
353+
assert result.exit_code == 0
354+
assert "\n".join([l.rstrip() for l in result.output.split('\n')]) == expected_output
355+
assert mock_run_command.call_count == expected_command_call_count
356+
357+
@classmethod
358+
def teardown_class(cls):
359+
os.environ['UTILITIES_UNIT_TESTING'] = "0"
360+
print("TEARDOWN")

0 commit comments

Comments
 (0)