Skip to content

Commit

Permalink
[build_templates] [hostcfgd] Keep containers hostname up to date (#2924)
Browse files Browse the repository at this point in the history
* 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]>
  • Loading branch information
msosyak authored and jleveque committed Jun 6, 2019
1 parent 4f85c7c commit 3ec95e1
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 3 deletions.
40 changes: 37 additions & 3 deletions files/build_templates/docker_image_ctl.j2
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,32 @@ function getMountPoint()
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
}

function updateHostName()
{
HOSTS=/etc/hosts
HOSTS_TMP=/etc/hosts.tmp

EXEC="docker exec -i {{docker_container_name}} bash -c"

NEW_HOSTNAME="$1"
HOSTNAME=`$EXEC "hostname"`
if ! [[ $HOSTNAME =~ ^[a-zA-Z0-9.\-]*$ ]]; then
HOSTNAME=`hostname`
fi

# copy HOSTS to HOSTS_TMP
$EXEC "cp $HOSTS $HOSTS_TMP"
# remove entry with hostname
$EXEC "sed -i \"/$HOSTNAME$/d\" $HOSTS_TMP"
# add entry with new hostname
$EXEC "echo -e \"127.0.0.1\t$NEW_HOSTNAME\" >> $HOSTS_TMP"

echo "Set hostname in {{docker_container_name}} container"
$EXEC "hostname '$NEW_HOSTNAME'"
$EXEC "cat $HOSTS_TMP > $HOSTS"
$EXEC "rm -f $HOSTS_TMP"
}

function getBootType()
{
local BOOT_TYPE
Expand Down Expand Up @@ -105,6 +131,10 @@ start() {
# Obtain our HWSKU as we will mount directories with these names in each docker
HWSKU=`sonic-cfggen -d -v 'DEVICE_METADATA["localhost"]["hwsku"]'`
{%- endif %}
HOSTNAME=`sonic-cfggen -m -v 'DEVICE_METADATA["localhost"]["hostname"]'`
if [ -z "$HOSTNAME" ] || ! [[ $HOSTNAME =~ ^[a-zA-Z0-9.\-]*$ ]]; then
HOSTNAME=`hostname`
fi

DOCKERCHECK=`docker inspect --type container {{docker_container_name}} 2>/dev/null`
if [ "$?" -eq "0" ]; then
Expand All @@ -121,6 +151,7 @@ start() {
{%- endif %}
preStartAction
docker start {{docker_container_name}}
updateHostName "$HOSTNAME"
postStartAction
exit $?
fi
Expand Down Expand Up @@ -167,6 +198,7 @@ start() {
--tmpfs /tmp \
{%- endif %}
--tmpfs /var/tmp \
--hostname "$HOSTNAME" \
--name={{docker_container_name}} {{docker_image_name}}:latest || {
echo "Failed to docker run" >&1
exit 4
Expand All @@ -186,11 +218,13 @@ stop() {
}

case "$1" in
start|wait|stop)
$1
start|wait|stop|updateHostName)
cmd=$1
shift
$cmd $@
;;
*)
echo "Usage: $0 {start|wait|stop}"
echo "Usage: $0 {start|wait|stop|updateHostName new_hostname}"
exit 1
;;
esac
50 changes: 50 additions & 0 deletions files/image_config/hostcfgd/hostcfgd
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# -*- coding: utf-8 -*-

import os
import re
import sys
import subprocess
import syslog
Expand All @@ -22,6 +23,15 @@ TACPLUS_SERVER_TIMEOUT_DEFAULT = "5"
TACPLUS_SERVER_AUTH_TYPE_DEFAULT = "pap"


def is_valid_hostname(name):
if hostname[-1] == ".":
hostname = hostname[:-1] # strip exactly one dot from the right, if present
if len(hostname) > 253:
return False
allowed = re.compile("(?!-)[A-Z\d-]{1,63}(?<!-)$", re.IGNORECASE)
return all(allowed.match(x) for x in hostname.split("."))


def is_true(val):
if val == 'True' or val == 'true':
return True
Expand Down Expand Up @@ -148,6 +158,7 @@ class HostConfigDaemon:
tacacs_server = self.config_db.get_table('TACPLUS_SERVER')
self.aaacfg = AaaCfg()
self.aaacfg.load(aaa, tacacs_global, tacacs_server)
self.hostname_cache=""

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

def hostname_handler(self, key, data):
if key != "localhost":
return

hostname = data.get("hostname")

if not hostname:
syslog.syslog(syslog.LOG_WARNING, "hostname key is missing")
return
if not is_valid_hostname(hostname):
return
if hostname == self.hostname_cache:
return

syslog.syslog(syslog.LOG_INFO, "Get all running containers")
cmd = 'docker ps --format "{{.Names}}"'
try:
containers = subprocess.check_output(cmd, shell=True).split("\n")[:-1]
except subprocess.CalledProcessError as err:
syslog.syslog(syslog.LOG_ERR, "{} - failed: return code - {}, output:\n{}"
.format(err.cmd, err.returncode, err.output))

for name in containers:
script = '/usr/bin/{}.sh'.format(name)
exists = os.path.isfile(script)
if not exists:
syslog.syslog(syslog.LOG_ERR, "Can't find control script for {}".format(name))
continue

cmd = "{} updateHostName {}".format(script, hostname)
try:
subprocess.check_call(cmd, shell=True)
except subprocess.CalledProcessError as err:
syslog.syslog(syslog.LOG_ERR, "{} - failed: return code - {}, output:\n{}"
.format(err.cmd, err.returncode, err.output))

self.hostname_cache = hostname

def start(self):
self.config_db.subscribe('AAA', lambda table, key, data: self.aaa_handler(key, data))
self.config_db.subscribe('TACPLUS_SERVER', lambda table, key, data: self.tacacs_server_handler(key, data))
self.config_db.subscribe('TACPLUS', lambda table, key, data: self.tacacs_global_handler(key, data))
self.config_db.subscribe('DEVICE_METADATA', lambda table, key, data: self.hostname_handler(key, data))
self.config_db.listen()


Expand Down

0 comments on commit 3ec95e1

Please sign in to comment.