Skip to content

Commit 3ec95e1

Browse files
msosyakjleveque
authored andcommitted
[build_templates] [hostcfgd] Keep containers hostname up to date (#2924)
* Add updateHostName function to docker_image_ctl.j2 * Add hostname specification on container creating step * Add listener for hostname changes in hostcfgd Signed-off-by: Myron Sosyak <[email protected]>
1 parent 4f85c7c commit 3ec95e1

File tree

2 files changed

+87
-3
lines changed

2 files changed

+87
-3
lines changed

files/build_templates/docker_image_ctl.j2

+37-3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,32 @@ function getMountPoint()
55
echo $1 | python -c "import sys, json, os; mnts = [x for x in json.load(sys.stdin)[0]['Mounts'] if x['Destination'] == '/usr/share/sonic/hwsku']; print '' if len(mnts) == 0 else os.path.basename(mnts[0]['Source'])" 2>/dev/null
66
}
77

8+
function updateHostName()
9+
{
10+
HOSTS=/etc/hosts
11+
HOSTS_TMP=/etc/hosts.tmp
12+
13+
EXEC="docker exec -i {{docker_container_name}} bash -c"
14+
15+
NEW_HOSTNAME="$1"
16+
HOSTNAME=`$EXEC "hostname"`
17+
if ! [[ $HOSTNAME =~ ^[a-zA-Z0-9.\-]*$ ]]; then
18+
HOSTNAME=`hostname`
19+
fi
20+
21+
# copy HOSTS to HOSTS_TMP
22+
$EXEC "cp $HOSTS $HOSTS_TMP"
23+
# remove entry with hostname
24+
$EXEC "sed -i \"/$HOSTNAME$/d\" $HOSTS_TMP"
25+
# add entry with new hostname
26+
$EXEC "echo -e \"127.0.0.1\t$NEW_HOSTNAME\" >> $HOSTS_TMP"
27+
28+
echo "Set hostname in {{docker_container_name}} container"
29+
$EXEC "hostname '$NEW_HOSTNAME'"
30+
$EXEC "cat $HOSTS_TMP > $HOSTS"
31+
$EXEC "rm -f $HOSTS_TMP"
32+
}
33+
834
function getBootType()
935
{
1036
local BOOT_TYPE
@@ -105,6 +131,10 @@ start() {
105131
# Obtain our HWSKU as we will mount directories with these names in each docker
106132
HWSKU=`sonic-cfggen -d -v 'DEVICE_METADATA["localhost"]["hwsku"]'`
107133
{%- endif %}
134+
HOSTNAME=`sonic-cfggen -m -v 'DEVICE_METADATA["localhost"]["hostname"]'`
135+
if [ -z "$HOSTNAME" ] || ! [[ $HOSTNAME =~ ^[a-zA-Z0-9.\-]*$ ]]; then
136+
HOSTNAME=`hostname`
137+
fi
108138

109139
DOCKERCHECK=`docker inspect --type container {{docker_container_name}} 2>/dev/null`
110140
if [ "$?" -eq "0" ]; then
@@ -121,6 +151,7 @@ start() {
121151
{%- endif %}
122152
preStartAction
123153
docker start {{docker_container_name}}
154+
updateHostName "$HOSTNAME"
124155
postStartAction
125156
exit $?
126157
fi
@@ -167,6 +198,7 @@ start() {
167198
--tmpfs /tmp \
168199
{%- endif %}
169200
--tmpfs /var/tmp \
201+
--hostname "$HOSTNAME" \
170202
--name={{docker_container_name}} {{docker_image_name}}:latest || {
171203
echo "Failed to docker run" >&1
172204
exit 4
@@ -186,11 +218,13 @@ stop() {
186218
}
187219

188220
case "$1" in
189-
start|wait|stop)
190-
$1
221+
start|wait|stop|updateHostName)
222+
cmd=$1
223+
shift
224+
$cmd $@
191225
;;
192226
*)
193-
echo "Usage: $0 {start|wait|stop}"
227+
echo "Usage: $0 {start|wait|stop|updateHostName new_hostname}"
194228
exit 1
195229
;;
196230
esac

files/image_config/hostcfgd/hostcfgd

+50
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# -*- coding: utf-8 -*-
33

44
import os
5+
import re
56
import sys
67
import subprocess
78
import syslog
@@ -22,6 +23,15 @@ TACPLUS_SERVER_TIMEOUT_DEFAULT = "5"
2223
TACPLUS_SERVER_AUTH_TYPE_DEFAULT = "pap"
2324

2425

26+
def is_valid_hostname(name):
27+
if hostname[-1] == ".":
28+
hostname = hostname[:-1] # strip exactly one dot from the right, if present
29+
if len(hostname) > 253:
30+
return False
31+
allowed = re.compile("(?!-)[A-Z\d-]{1,63}(?<!-)$", re.IGNORECASE)
32+
return all(allowed.match(x) for x in hostname.split("."))
33+
34+
2535
def is_true(val):
2636
if val == 'True' or val == 'true':
2737
return True
@@ -148,6 +158,7 @@ class HostConfigDaemon:
148158
tacacs_server = self.config_db.get_table('TACPLUS_SERVER')
149159
self.aaacfg = AaaCfg()
150160
self.aaacfg.load(aaa, tacacs_global, tacacs_server)
161+
self.hostname_cache=""
151162

152163
def aaa_handler(self, key, data):
153164
self.aaacfg.aaa_update(key, data)
@@ -166,10 +177,49 @@ class HostConfigDaemon:
166177
log_data['passkey'] = obfuscate(log_data['passkey'])
167178
syslog.syslog(syslog.LOG_INFO, 'value of {} changed to {}'.format(key, log_data))
168179

180+
def hostname_handler(self, key, data):
181+
if key != "localhost":
182+
return
183+
184+
hostname = data.get("hostname")
185+
186+
if not hostname:
187+
syslog.syslog(syslog.LOG_WARNING, "hostname key is missing")
188+
return
189+
if not is_valid_hostname(hostname):
190+
return
191+
if hostname == self.hostname_cache:
192+
return
193+
194+
syslog.syslog(syslog.LOG_INFO, "Get all running containers")
195+
cmd = 'docker ps --format "{{.Names}}"'
196+
try:
197+
containers = subprocess.check_output(cmd, shell=True).split("\n")[:-1]
198+
except subprocess.CalledProcessError as err:
199+
syslog.syslog(syslog.LOG_ERR, "{} - failed: return code - {}, output:\n{}"
200+
.format(err.cmd, err.returncode, err.output))
201+
202+
for name in containers:
203+
script = '/usr/bin/{}.sh'.format(name)
204+
exists = os.path.isfile(script)
205+
if not exists:
206+
syslog.syslog(syslog.LOG_ERR, "Can't find control script for {}".format(name))
207+
continue
208+
209+
cmd = "{} updateHostName {}".format(script, hostname)
210+
try:
211+
subprocess.check_call(cmd, shell=True)
212+
except subprocess.CalledProcessError as err:
213+
syslog.syslog(syslog.LOG_ERR, "{} - failed: return code - {}, output:\n{}"
214+
.format(err.cmd, err.returncode, err.output))
215+
216+
self.hostname_cache = hostname
217+
169218
def start(self):
170219
self.config_db.subscribe('AAA', lambda table, key, data: self.aaa_handler(key, data))
171220
self.config_db.subscribe('TACPLUS_SERVER', lambda table, key, data: self.tacacs_server_handler(key, data))
172221
self.config_db.subscribe('TACPLUS', lambda table, key, data: self.tacacs_global_handler(key, data))
222+
self.config_db.subscribe('DEVICE_METADATA', lambda table, key, data: self.hostname_handler(key, data))
173223
self.config_db.listen()
174224

175225

0 commit comments

Comments
 (0)