Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[dns] Add support for static DNS configuration. #14549

Merged
merged 5 commits into from
Jun 22, 2023
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
7 changes: 6 additions & 1 deletion build_debian.sh
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,7 @@ sudo LANG=C DEBIAN_FRONTEND=noninteractive chroot $FILESYSTEM_ROOT apt-get -y in
jq \
auditd \
linux-perf \
resolvconf \
lsof \
sysstat

Expand Down Expand Up @@ -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
Expand Down
9 changes: 9 additions & 0 deletions files/build_templates/sonic_debian_extension.j2
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,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
Expand Down
2 changes: 2 additions & 0 deletions files/image_config/interfaces/interfaces-config.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
15 changes: 15 additions & 0 deletions files/image_config/resolv-config/resolv-config.service
Original file line number Diff line number Diff line change
@@ -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 changes: 61 additions & 0 deletions files/image_config/resolv-config/resolv-config.sh
Original file line number Diff line number Diff line change
@@ -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
Empty file.
2 changes: 2 additions & 0 deletions files/image_config/resolv-config/resolv.conf.head
Original file line number Diff line number Diff line change
@@ -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 changes: 3 additions & 0 deletions files/image_config/resolv-config/resolv.conf.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{% for ip in DNS_NAMESERVER|sort %}
nameserver {{ ip }}
{% endfor -%}
7 changes: 7 additions & 0 deletions files/image_config/resolv-config/update-containers
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/bin/bash

for container in $(docker ps -a --format=" {{ .ID }}"); do
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some containers like telemetry will start after 3 minutes.
If we upgrade SONiC image, and start the system for the first time, can we update /etc/resolv.conf in telemetry container?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

docker ps -a command returns all existing containers on the system. And we will update /etc/resolv.conf file for all containers including those that are not running.

For the case when the container is created in the runtime (for example app extension), when the dockerd starts the container it copies /etc/resolv.conf file from the host OS into the container filesystem. So, we will always have a valid DNS configuration inside the containers.

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
9 changes: 7 additions & 2 deletions src/sonic-config-engine/minigraph.py
Original file line number Diff line number Diff line change
Expand Up @@ -993,6 +993,7 @@ def parse_meta(meta, hname):
dhcp_servers = []
dhcpv6_servers = []
ntp_servers = []
dns_nameservers = []
tacacs_servers = []
mgmt_routes = []
erspan_dst = []
Expand Down Expand Up @@ -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":
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -1465,6 +1468,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 = []
Expand Down Expand Up @@ -1520,7 +1524,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")):
Expand Down Expand Up @@ -1961,6 +1965,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
Expand Down
3 changes: 3 additions & 0 deletions src/sonic-config-engine/tests/data/dns/resolv.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
nameserver 1.1.1.1
nameserver 2001:4860:4860::8888

6 changes: 6 additions & 0 deletions src/sonic-config-engine/tests/data/dns/static_dns.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"DNS_NAMESERVER": {
"1.1.1.1": {},
"2001:4860:4860::8888": {}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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"/>
Expand Down
6 changes: 6 additions & 0 deletions src/sonic-config-engine/tests/simple-sample-graph-case.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
5 changes: 5 additions & 0 deletions src/sonic-config-engine/tests/test_cfggen.py
Original file line number Diff line number Diff line change
Expand Up @@ -680,6 +680,11 @@ def test_metadata_ntp(self):
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"]
Expand Down
8 changes: 8 additions & 0 deletions src/sonic-config-engine/tests/test_j2files.py
Original file line number Diff line number Diff line change
Expand Up @@ -695,6 +695,14 @@ def test_backend_acl_template_render(self):
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')
Expand Down
5 changes: 5 additions & 0 deletions src/sonic-config-engine/tests/test_minigraph_case.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,11 @@ def test_metadata_ntp(self):
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)
Expand Down
11 changes: 11 additions & 0 deletions src/sonic-config-engine/tests/test_multinpu_cfggen.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,17 @@ def test_metadata_ntp(self):
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))
Expand Down