[dns] Add support for static DNS configuration. (#14549)
- Why I did it Add support for static DNS configuration. According to sonic-net/SONiC#1262 HLD. - How I did it Add a new resolv-config.service that is responsible for transferring configuration from Config DB into /etc/resolv.conf file that is consumed by various subsystems in Linux to resolve domain names into IP addresses. - How to verify it Run the image compilation. Each component related to the static DNS feature is covered with the unit tests. Run sonic-mgmt tests. Static DNS feature will be covered with the system tests. Install the image and run manual tests.
This commit is contained in:
parent
78c262ea9f
commit
475fe27c0b
@ -400,6 +400,7 @@ sudo LANG=C DEBIAN_FRONTEND=noninteractive chroot $FILESYSTEM_ROOT apt-get -y in
|
||||
jq \
|
||||
auditd \
|
||||
linux-perf \
|
||||
resolvconf \
|
||||
lsof \
|
||||
sysstat
|
||||
|
||||
@ -762,7 +763,11 @@ sudo rm -f $ONIE_INSTALLER_PAYLOAD $FILESYSTEM_SQUASHFS
|
||||
## Note: -x to skip directories on different file systems, such as /proc
|
||||
sudo du -hsx $FILESYSTEM_ROOT
|
||||
sudo mkdir -p $FILESYSTEM_ROOT/var/lib/docker
|
||||
sudo cp files/image_config/resolv-config/resolv.conf $FILESYSTEM_ROOT/etc/resolv.conf
|
||||
|
||||
## Clear DNS configuration inherited from the build server
|
||||
sudo rm -f $FILESYSTEM_ROOT/etc/resolvconf/resolv.conf.d/original
|
||||
sudo cp files/image_config/resolv-config/resolv.conf.head $FILESYSTEM_ROOT/etc/resolvconf/resolv.conf.d/head
|
||||
|
||||
sudo mksquashfs $FILESYSTEM_ROOT $FILESYSTEM_SQUASHFS -comp zstd -b 1M -e boot -e var/lib/docker -e $PLATFORM_DIR
|
||||
|
||||
# Ensure admin gid is 1000
|
||||
|
@ -430,6 +430,15 @@ j2 files/dhcp/dhclient.conf.j2 | sudo tee $FILESYSTEM_ROOT/etc/dhcp/dhclient.con
|
||||
sudo cp files/dhcp/ifupdown2_policy.json $FILESYSTEM_ROOT/etc/network/ifupdown2/policy.d
|
||||
sudo cp files/dhcp/90-dhcp6-systcl.conf.j2 $FILESYSTEM_ROOT_USR_SHARE_SONIC_TEMPLATES/
|
||||
|
||||
# Copy DNS configuration files and templates
|
||||
sudo cp $IMAGE_CONFIGS/resolv-config/resolv-config.service $FILESYSTEM_ROOT_USR_LIB_SYSTEMD_SYSTEM
|
||||
sudo cp $IMAGE_CONFIGS/resolv-config/resolv-config.sh $FILESYSTEM_ROOT/usr/bin/
|
||||
sudo cp $IMAGE_CONFIGS/resolv-config/resolv.conf.j2 $FILESYSTEM_ROOT_USR_SHARE_SONIC_TEMPLATES/
|
||||
echo "resolv-config.service" | sudo tee -a $GENERATED_SERVICE_FILE
|
||||
sudo LANG=C chroot $FILESYSTEM_ROOT systemctl disable resolvconf.service
|
||||
sudo mkdir -p $FILESYSTEM_ROOT/etc/resolvconf/update-libc.d/
|
||||
sudo cp $IMAGE_CONFIGS/resolv-config/update-containers $FILESYSTEM_ROOT/etc/resolvconf/update-libc.d/
|
||||
|
||||
# Copy initial interfaces configuration file, will be overwritten on first boot
|
||||
sudo cp $IMAGE_CONFIGS/interfaces/init_interfaces $FILESYSTEM_ROOT/etc/network/interfaces
|
||||
sudo mkdir -p $FILESYSTEM_ROOT/etc/network/interfaces.d
|
||||
|
@ -60,6 +60,8 @@ for intf_pid in $(ls -1 /var/run/dhclient*.Ethernet*.pid 2> /dev/null); do
|
||||
[[ -f ${intf_pid} ]] && kill `cat ${intf_pid}` && rm -f ${intf_pid}
|
||||
done
|
||||
|
||||
/usr/bin/resolv-config.sh cleanup
|
||||
|
||||
# Read sysctl conf files again
|
||||
sysctl -p /etc/sysctl.d/90-dhcp6-systcl.conf
|
||||
|
||||
|
15
files/image_config/resolv-config/resolv-config.service
Normal file
15
files/image_config/resolv-config/resolv-config.service
Normal file
@ -0,0 +1,15 @@
|
||||
[Unit]
|
||||
Description=Update DNS configuration
|
||||
Requires=updategraph.service
|
||||
After=updategraph.service
|
||||
BindsTo=sonic.target
|
||||
After=sonic.target
|
||||
StartLimitIntervalSec=0
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
ExecStart=/usr/bin/resolv-config.sh start
|
||||
|
||||
[Install]
|
||||
WantedBy=sonic.target
|
61
files/image_config/resolv-config/resolv-config.sh
Executable file
61
files/image_config/resolv-config/resolv-config.sh
Executable file
@ -0,0 +1,61 @@
|
||||
#!/bin/bash
|
||||
|
||||
WD=/var/run/resolvconf/
|
||||
CONFIG_DIR=${WD}/interface/
|
||||
STATIC_CONFIG_FILE=mgmt.static
|
||||
DYNAMIC_CONFIG_FILE_TEMPLATE=*.dhclient
|
||||
|
||||
update_symlink()
|
||||
{
|
||||
ln -sf /run/resolvconf/resolv.conf /etc/resolv.conf
|
||||
}
|
||||
|
||||
start()
|
||||
{
|
||||
update_symlink
|
||||
|
||||
redis-dump -d 4 -k "DNS_NAMESERVER*" -y > /tmp/dns.json
|
||||
if [[ $? -eq 0 && "$(cat /tmp/dns.json)" != "{}" ]]; then
|
||||
# Apply static DNS configuration and disable updates
|
||||
/sbin/resolvconf --disable-updates
|
||||
pushd ${CONFIG_DIR}
|
||||
# Backup dynamic configuration to restore it when the static configuration is removed
|
||||
mv ${DYNAMIC_CONFIG_FILE_TEMPLATE} ${WD} || true
|
||||
|
||||
sonic-cfggen -d -t /usr/share/sonic/templates/resolv.conf.j2,${STATIC_CONFIG_FILE}
|
||||
|
||||
/sbin/resolvconf --enable-updates
|
||||
/sbin/resolvconf -u
|
||||
/sbin/resolvconf --disable-updates
|
||||
popd
|
||||
else
|
||||
# Dynamic DNS configuration. Enable updates. It is expected to receive configuraution for DHCP server
|
||||
/sbin/resolvconf --disable-updates
|
||||
pushd ${CONFIG_DIR}
|
||||
rm -f ${STATIC_CONFIG_FILE}
|
||||
# Restore dynamic configuration if it exists
|
||||
mv ${WD}/${DYNAMIC_CONFIG_FILE_TEMPLATE} ${CONFIG_DIR} || true
|
||||
|
||||
/sbin/resolvconf --enable-updates
|
||||
/sbin/resolvconf -u
|
||||
fi
|
||||
}
|
||||
|
||||
clean-dynamic-conf()
|
||||
{
|
||||
rm -f ${WD}/${DYNAMIC_CONFIG_FILE_TEMPLATE}
|
||||
rm -f ${WD}/postponed-update
|
||||
}
|
||||
|
||||
case $1 in
|
||||
start)
|
||||
start
|
||||
;;
|
||||
cleanup)
|
||||
clean-dynamic-conf
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {start|clean-dynamic-conf}"
|
||||
exit 2
|
||||
;;
|
||||
esac
|
2
files/image_config/resolv-config/resolv.conf.head
Normal file
2
files/image_config/resolv-config/resolv.conf.head
Normal file
@ -0,0 +1,2 @@
|
||||
# Dynamic resolv.conf(5) file generated by resolvconf(8)
|
||||
# The content of this file may be overwritten during a config reload.
|
3
files/image_config/resolv-config/resolv.conf.j2
Normal file
3
files/image_config/resolv-config/resolv.conf.j2
Normal file
@ -0,0 +1,3 @@
|
||||
{% for ip in DNS_NAMESERVER|sort %}
|
||||
nameserver {{ ip }}
|
||||
{% endfor -%}
|
7
files/image_config/resolv-config/update-containers
Executable file
7
files/image_config/resolv-config/update-containers
Executable file
@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
for container in $(docker ps -a --format=" {{ .ID }}"); do
|
||||
docker cp -L /etc/resolv.conf ${container}:/_resolv.conf
|
||||
docker exec -t ${container} bash -c "cat /_resolv.conf > /etc/resolv.conf"
|
||||
docker exec -t ${container} bash -c "rm /_resolv.conf"
|
||||
done
|
@ -993,6 +993,7 @@ def parse_meta(meta, hname):
|
||||
dhcp_servers = []
|
||||
dhcpv6_servers = []
|
||||
ntp_servers = []
|
||||
dns_nameservers = []
|
||||
tacacs_servers = []
|
||||
mgmt_routes = []
|
||||
erspan_dst = []
|
||||
@ -1023,6 +1024,8 @@ def parse_meta(meta, hname):
|
||||
dhcp_servers = value_group
|
||||
elif name == "NtpResources":
|
||||
ntp_servers = value_group
|
||||
elif name == "DnsNameserverResources":
|
||||
dns_nameservers = value_group
|
||||
elif name == "SyslogResources":
|
||||
syslog_servers = value_group
|
||||
elif name == "TacacsServer":
|
||||
@ -1061,7 +1064,7 @@ def parse_meta(meta, hname):
|
||||
qos_profile = value
|
||||
elif name == "RackMgmtMap":
|
||||
rack_mgmt_map = value
|
||||
return syslog_servers, dhcp_servers, dhcpv6_servers, ntp_servers, tacacs_servers, mgmt_routes, erspan_dst, deployment_id, region, cloudtype, resource_type, downstream_subrole, switch_id, switch_type, max_cores, kube_data, macsec_profile, downstream_redundancy_types, redundancy_type, qos_profile, rack_mgmt_map
|
||||
return syslog_servers, dhcp_servers, dhcpv6_servers, ntp_servers, dns_nameservers, tacacs_servers, mgmt_routes, erspan_dst, deployment_id, region, cloudtype, resource_type, downstream_subrole, switch_id, switch_type, max_cores, kube_data, macsec_profile, downstream_redundancy_types, redundancy_type, qos_profile, rack_mgmt_map
|
||||
|
||||
|
||||
def parse_linkmeta(meta, hname):
|
||||
@ -1488,6 +1491,7 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None, hw
|
||||
dhcp_servers = []
|
||||
dhcpv6_servers = []
|
||||
ntp_servers = []
|
||||
dns_nameservers = []
|
||||
tacacs_servers = []
|
||||
mgmt_routes = []
|
||||
erspan_dst = []
|
||||
@ -1543,7 +1547,7 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None, hw
|
||||
elif child.tag == str(QName(ns, "UngDec")):
|
||||
(u_neighbors, u_devices, _, _, _, _, _, _) = parse_png(child, hostname, None)
|
||||
elif child.tag == str(QName(ns, "MetadataDeclaration")):
|
||||
(syslog_servers, dhcp_servers, dhcpv6_servers, ntp_servers, tacacs_servers, mgmt_routes, erspan_dst, deployment_id, region, cloudtype, resource_type, downstream_subrole, switch_id, switch_type, max_cores, kube_data, macsec_profile, downstream_redundancy_types, redundancy_type, qos_profile, rack_mgmt_map) = parse_meta(child, hostname)
|
||||
(syslog_servers, dhcp_servers, dhcpv6_servers, ntp_servers, dns_nameservers, tacacs_servers, mgmt_routes, erspan_dst, deployment_id, region, cloudtype, resource_type, downstream_subrole, switch_id, switch_type, max_cores, kube_data, macsec_profile, downstream_redundancy_types, redundancy_type, qos_profile, rack_mgmt_map) = parse_meta(child, hostname)
|
||||
elif child.tag == str(QName(ns, "LinkMetadataDeclaration")):
|
||||
linkmetas = parse_linkmeta(child, hostname)
|
||||
elif child.tag == str(QName(ns, "DeviceInfos")):
|
||||
@ -1995,6 +1999,7 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None, hw
|
||||
results['DHCP_SERVER'] = dict((item, {}) for item in dhcp_servers)
|
||||
results['DHCP_RELAY'] = dhcp_relay_table
|
||||
results['NTP_SERVER'] = dict((item, {}) for item in ntp_servers)
|
||||
results['DNS_NAMESERVER'] = dict((item, {}) for item in dns_nameservers)
|
||||
results['TACPLUS_SERVER'] = dict((item, {'priority': '1', 'tcp_port': '49'}) for item in tacacs_servers)
|
||||
if len(acl_table_types) > 0:
|
||||
results['ACL_TABLE_TYPE'] = acl_table_types
|
||||
|
3
src/sonic-config-engine/tests/data/dns/resolv.conf
Normal file
3
src/sonic-config-engine/tests/data/dns/resolv.conf
Normal file
@ -0,0 +1,3 @@
|
||||
nameserver 1.1.1.1
|
||||
nameserver 2001:4860:4860::8888
|
||||
|
6
src/sonic-config-engine/tests/data/dns/static_dns.json
Normal file
6
src/sonic-config-engine/tests/data/dns/static_dns.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"DNS_NAMESERVER": {
|
||||
"1.1.1.1": {},
|
||||
"2001:4860:4860::8888": {}
|
||||
}
|
||||
}
|
@ -1425,6 +1425,11 @@
|
||||
<a:Reference i:nil="true"/>
|
||||
<a:Value>17.39.1.129;17.39.1.130</a:Value>
|
||||
</a:DeviceProperty>
|
||||
<a:DeviceProperty>
|
||||
<a:Name>DnsNameserverResources</a:Name>
|
||||
<a:Reference i:nil="true"/>
|
||||
<a:Value>1.1.1.1;8.8.8.8</a:Value>
|
||||
</a:DeviceProperty>
|
||||
<a:DeviceProperty>
|
||||
<a:Name>SnmpResources</a:Name>
|
||||
<a:Reference i:nil="true"/>
|
||||
|
@ -500,6 +500,12 @@
|
||||
10.0.10.1;10.0.10.2
|
||||
</a:Value>
|
||||
</a:DeviceProperty>
|
||||
<a:DeviceProperty>
|
||||
<a:Name>DnsNameserverResources</a:Name>
|
||||
<a:Value>
|
||||
1.1.1.1;8.8.8.8
|
||||
</a:Value>
|
||||
</a:DeviceProperty>
|
||||
<a:DeviceProperty>
|
||||
<a:Name>SnmpResources</a:Name>
|
||||
<a:Value>
|
||||
|
@ -236,6 +236,12 @@
|
||||
10.0.10.1;10.0.10.2
|
||||
</a:Value>
|
||||
</a:DeviceProperty>
|
||||
<a:DeviceProperty>
|
||||
<a:Name>DnsNameserverResources</a:Name>
|
||||
<a:Value>
|
||||
20.2.2.2;30.3.3.3
|
||||
</a:Value>
|
||||
</a:DeviceProperty>
|
||||
<a:DeviceProperty>
|
||||
<a:Name>SnmpResources</a:Name>
|
||||
<a:Value>
|
||||
|
@ -696,6 +696,11 @@ class TestCfgGen(TestCase):
|
||||
output = self.run_script(argument)
|
||||
self.assertEqual(utils.to_dict(output.strip()), utils.to_dict("{'10.0.10.1': {}, '10.0.10.2': {}}"))
|
||||
|
||||
def test_metadata_dns_nameserver(self):
|
||||
argument = ['-m', self.sample_graph_metadata, '-p', self.port_config, '-v', "DNS_NAMESERVER"]
|
||||
output = self.run_script(argument)
|
||||
self.assertEqual(utils.to_dict(output.strip()), utils.to_dict("{'20.2.2.2': {}, '30.3.3.3': {}}"))
|
||||
|
||||
def test_minigraph_vnet(self, **kwargs):
|
||||
graph_file = kwargs.get('graph_file', self.sample_graph_simple)
|
||||
argument = ['-m', graph_file, '-p', self.port_config, '-v', "VNET"]
|
||||
|
@ -695,6 +695,14 @@ class TestJ2Files(TestCase):
|
||||
self.run_script(argument, output_file=self.output_file)
|
||||
assert utils.cmp(sample_output_file, self.output_file), self.run_diff(sample_output_file, self.output_file)
|
||||
|
||||
def test_dns_template_render(self):
|
||||
conf_template = os.path.join(self.test_dir, '..', '..', '..', 'files', 'image_config', 'resolv-config', 'resolv.conf.j2')
|
||||
static_dns_conf = os.path.join(self.test_dir, "data", "dns", "static_dns.json")
|
||||
expected = os.path.join(self.test_dir, "data", "dns", "resolv.conf")
|
||||
|
||||
argument = ['-j', static_dns_conf, '-t', conf_template]
|
||||
self.run_script(argument, output_file=self.output_file)
|
||||
assert utils.cmp(expected, self.output_file), self.run_diff(expected, self.output_file)
|
||||
|
||||
def test_buffers_edgezone_aggregator_render_template(self):
|
||||
self._test_buffers_render_template('arista', 'x86_64-arista_7060_cx32s', 'Arista-7060CX-32S-D48C8', 'sample-arista-7060-t0-minigraph.xml', 'buffers.json.j2', 'buffer-arista7060-t0.json')
|
||||
|
@ -277,6 +277,11 @@ class TestCfgGenCaseInsensitive(TestCase):
|
||||
output = self.run_script(argument)
|
||||
self.assertEqual(output.strip(), "{'10.0.10.1': {}, '10.0.10.2': {}}")
|
||||
|
||||
def test_metadata_dns_nameserver(self):
|
||||
argument = ['-m', self.sample_graph, '-p', self.port_config, '-v', "DNS_NAMESERVER"]
|
||||
output = self.run_script(argument)
|
||||
self.assertEqual(output.strip(), "{'1.1.1.1': {}, '8.8.8.8': {}}")
|
||||
|
||||
def test_minigraph_vnet(self):
|
||||
argument = ['-m', self.sample_graph, '-p', self.port_config, '-v', "VNET"]
|
||||
output = self.run_script(argument)
|
||||
|
@ -150,6 +150,17 @@ class TestMultiNpuCfgGen(TestCase):
|
||||
print("Log:asic{} sku {}".format(asic,output))
|
||||
self.assertDictEqual(output, {})
|
||||
|
||||
def test_metadata_dns_nameserver(self):
|
||||
argument = ['-m', self.sample_graph, '-p', self.sample_port_config, '--var-json', "DNS_NAMESERVER"]
|
||||
output = json.loads(self.run_script(argument))
|
||||
self.assertDictEqual(output, {'1.1.1.1': {}, '8.8.8.8': {}})
|
||||
#DNS_NAMESERVER data is present only in the host config
|
||||
argument = ['-m', self.sample_graph, '--var-json', "DNS_NAMESERVER"]
|
||||
for asic in range(NUM_ASIC):
|
||||
output = json.loads(self.run_script_for_asic(argument, asic, self.port_config[asic]))
|
||||
print("Log:asic{} sku {}".format(asic,output))
|
||||
self.assertDictEqual(output, {})
|
||||
|
||||
def test_mgmt_port(self):
|
||||
argument = ['-m', self.sample_graph, '-p', self.sample_port_config, '--var-json', "MGMT_PORT"]
|
||||
output = json.loads(self.run_script(argument))
|
||||
|
Loading…
Reference in New Issue
Block a user