sonic-buildimage/files/build_templates/docker_image_ctl.j2
arlakshm b6b1f3fac8 syslog changes Multi ASIC platforms (#4738)
Add changes for syslog support for containers running in namespaces on multi ASIC platforms.
On Multi ASIC platforms

Rsyslog service is only running on the host. There is no rsyslog service running in each namespace.
On multi ASIC platforms the rsyslog service on the host will be listening on the docker0 ip address instead of loopback address.
The rsyslog.conf on the containers is modified to have omfwd target ip to be docker0 ipaddress instead of loopback ip

Signed-off-by: Arvindsrinivasan Lakshmi Narasimhan <arlakshm@microsoft.com>
2020-07-05 15:19:22 -07:00

371 lines
14 KiB
Django/Jinja

#!/bin/bash
# single instance containers are still supported (even though it might not look like it)
# if no instance number is passed to this script, $DEV will simply be unset, resulting in docker
# commands being sent to the base container name. E.g. `docker start database$DEV` simply starts
# the container `database` if no instance number is passed since `$DEV` is not defined
{%- if docker_container_name == "database" %}
link_namespace() {
# Makes namespace of a docker container available in
# /var/run/netns so it can be managed with iproute2
mkdir -p /var/run/netns
PID="$(docker inspect -f {{"'{{.State.Pid}}'"}} "{{docker_container_name}}$DEV")"
PIDS=`ip netns pids "$NET_NS" 2>/dev/null`
if [ "$?" -eq "0" ]; then # namespace exists
if `echo $PIDS | grep --quiet -w $PID`; then # namespace is correctly linked
return 0
else # if it's incorrectly linked remove it
ip netns delete $NET_NS
fi
fi
ln -s /proc/$PID/ns/net /var/run/netns/$NET_NS
}
{%- endif %}
function updateSyslogConf()
{
# On multiNPU platforms, change the syslog target ip to docker0 ip to allow logs from containers
# running on the namespace to reach the rsyslog service running on the host
# Also update the container name
if [[ ($NUM_ASIC -gt 1) ]]; then
TARGET_IP=$(docker network inspect bridge --format={{ "'{{(index .IPAM.Config 0).Gateway}}'" }})
CONTAINER_NAME="{{docker_container_name}}$DEV"
TMP_FILE="/tmp/rsyslog.$CONTAINER_NAME.conf"
sonic-cfggen -t /usr/share/sonic/templates/rsyslog-container.conf.j2 -a "{\"target_ip\": \"$TARGET_IP\", \"container_name\": \"$CONTAINER_NAME\" }" > $TMP_FILE
docker cp $TMP_FILE {{docker_container_name}}$DEV:/etc/rsyslog.conf
rm -rf $TMP_FILE
fi
}
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 getBootType()
{
# same code snippet in files/scripts/syncd.sh
case "$(cat /proc/cmdline)" in
*SONIC_BOOT_TYPE=warm*)
TYPE='warm'
;;
*SONIC_BOOT_TYPE=fastfast*)
TYPE='fastfast'
;;
*SONIC_BOOT_TYPE=fast*|*fast-reboot*)
TYPE='fast'
;;
*)
TYPE='cold'
esac
echo "${TYPE}"
}
function preStartAction()
{
{%- if docker_container_name == "database" %}
WARM_DIR=/host/warmboot
if [[ ("$BOOT_TYPE" == "warm" || "$BOOT_TYPE" == "fastfast") && -f $WARM_DIR/dump.rdb ]]; then
# Load redis content from /host/warmboot/dump.rdb
docker cp $WARM_DIR/dump.rdb database$DEV:/var/lib/redis/dump.rdb
else
# Create an emtpy file and overwrite any RDB if already there
echo -n > /tmp/dump.rdb
docker cp /tmp/dump.rdb database$DEV:/var/lib/redis/
fi
{%- elif docker_container_name == "snmp" %}
$SONIC_DB_CLI STATE_DB HSET 'DEVICE_METADATA|localhost' chassis_serial_number $(decode-syseeprom -s)
{%- else %}
: # nothing
{%- endif %}
updateSyslogConf
}
function postStartAction()
{
{%- if docker_container_name == "database" %}
if [ "$DEV" ]; then
docker exec -i database$DEV sysctl -w net.ipv6.conf.all.disable_ipv6=0
link_namespace $DEV
fi
# Wait until redis starts
until [[ $($SONIC_DB_CLI PING | grep -c PONG) -gt 0 ]]; do
sleep 1;
done
if [[ ("$BOOT_TYPE" == "warm" || "$BOOT_TYPE" == "fastfast") && -f $WARM_DIR/dump.rdb ]]; then
rm -f $WARM_DIR/dump.rdb
else
# If there is a config_db.json dump file, load it.
if [ -r /etc/sonic/config_db$DEV.json ]; then
if [ -r /etc/sonic/init_cfg.json ]; then
$SONIC_CFGGEN -j /etc/sonic/init_cfg.json -j /etc/sonic/config_db$DEV.json --write-to-db
else
$SONIC_CFGGEN -j /etc/sonic/config_db$DEV.json --write-to-db
fi
fi
if [[ "$BOOT_TYPE" == "fast" ]]; then
# set the key to expire in 3 minutes
$SONIC_DB_CLI STATE_DB SET "FAST_REBOOT|system" "1" "EX" "180"
fi
$SONIC_DB_CLI CONFIG_DB SET "CONFIG_DB_INITIALIZED" "1"
fi
if [[ -x /usr/bin/db_migrator.py ]]; then
# Migrate the DB to the latest schema version if needed
if [ -z "$DEV" ]; then
/usr/bin/db_migrator.py -o migrate
fi
fi
{%- elif docker_container_name == "swss" %}
docker exec swss$DEV rm -f /ready # remove cruft
if [[ "$BOOT_TYPE" == "fast" ]] && [[ -d /host/fast-reboot ]]; then
test -e /host/fast-reboot/fdb.json && docker cp /host/fast-reboot/fdb.json swss$DEV:/
test -e /host/fast-reboot/arp.json && docker cp /host/fast-reboot/arp.json swss$DEV:/
test -e /host/fast-reboot/default_routes.json && docker cp /host/fast-reboot/default_routes.json swss$DEV:/
rm -fr /host/fast-reboot
fi
docker exec swss$DEV touch /ready # signal swssconfig.sh to go
{%- elif docker_container_name == "pmon" %}
DEVPATH="/usr/share/sonic/device"
REBOOT="platform_reboot"
PSENSOR="/usr/local/bin/platform_sensors.py"
if [ -d ${DEVPATH}/${PLATFORM} ] && [ -f $PSENSOR ]; then
exist=`docker exec -i pmon ls /usr/bin/platform_sensors.py "$@" 2>/dev/null`
if [ -z "$exist" ]; then
docker cp $PSENSOR pmon:/usr/bin/
fi
fi
{%- else %}
: # nothing
{%- endif %}
}
start() {
# Obtain boot type from kernel arguments
BOOT_TYPE=`getBootType`
# Obtain our platform as we will mount directories with these names in each docker
PLATFORM=`$SONIC_CFGGEN -H -v DEVICE_METADATA.localhost.platform`
# Parse the device specific asic conf file, if it exists
ASIC_CONF=/usr/share/sonic/device/$PLATFORM/asic.conf
if [ -f "$ASIC_CONF" ]; then
source $ASIC_CONF
fi
{%- if docker_container_name == "database" %}
# Don't mount HWSKU in {{docker_container_name}} container.
HWSKU=""
{%- else %}
# Obtain our HWSKU as we will mount directories with these names in each docker
HWSKU=`$SONIC_CFGGEN -d -v 'DEVICE_METADATA["localhost"]["hwsku"]'`
{%- endif %}
DOCKERCHECK=`docker inspect --type container {{docker_container_name}}$DEV 2>/dev/null`
if [ "$?" -eq "0" ]; then
{%- if docker_container_name == "database" %}
DOCKERMOUNT=""
{%- else %}
DOCKERMOUNT=`getMountPoint "$DOCKERCHECK"`
{%- endif %}
if [ x"$DOCKERMOUNT" == x"$HWSKU" ]; then
{%- if docker_container_name == "database" %}
echo "Starting existing {{docker_container_name}}$DEV container"
{%- else %}
echo "Starting existing {{docker_container_name}}$DEV container with HWSKU $HWSKU"
{%- endif %}
preStartAction
docker start {{docker_container_name}}$DEV
postStartAction
exit $?
fi
# docker created with a different HWSKU, remove and recreate
echo "Removing obsolete {{docker_container_name}}$DEV container with HWSKU $DOCKERMOUNT"
docker rm -f {{docker_container_name}}$DEV
fi
{%- if docker_container_name == "database" %}
echo "Creating new {{docker_container_name}}$DEV container"
if [ -z "$DEV" ]; then
# if database_global exists in old_config, use it; otherwise use the default one in new image
if [ -f /etc/sonic/old_config/database_global.json ]; then
echo "Use database_global.json from old system..."
mv /etc/sonic/old_config/database_global.json /etc/sonic/
fi
fi
# if database_config exists in old_config, use it; otherwise use the default one in new image
if [ -f /etc/sonic/old_config/database_config$DEV.json ]; then
echo "Use database_config.json from old system..."
mv /etc/sonic/old_config/database_config$DEV.json /etc/sonic/
fi
{%- else %}
echo "Creating new {{docker_container_name}}$DEV container with HWSKU $HWSKU"
{%- endif %}
# In Multi ASIC platforms the global database config file database_global.json will exist.
# Parse the file and get the include path for the database_config.json files used in
# various namesapces. The database_config paths are relative to the DIR of SONIC_DB_GLOBAL_JSON.
SONIC_DB_GLOBAL_JSON="/var/run/redis/sonic-db/database_global.json"
if [ -f "$SONIC_DB_GLOBAL_JSON" ]; then
# TODO Create a separate python script with the below logic and invoke it here.
redis_dir_list=`/usr/bin/python -c "import sys; import os; import json; f=open(sys.argv[1]); \
global_db_dir = os.path.dirname(sys.argv[1]); data=json.load(f); \
print(\" \".join([os.path.normpath(global_db_dir+'/'+elem['include']).partition('sonic-db')[0]\
for elem in data['INCLUDES'] if 'namespace' in elem])); f.close()" $SONIC_DB_GLOBAL_JSON`
fi
if [ -z "$DEV" ]; then
NET="host"
# For Multi-ASIC platform we have to mount the redis paths for database instances running in different
# namespaces, into the single instance dockers like snmp, pmon on linux host. These global dockers
# will need to get/set tables from databases in different namespaces.
# /var/run/redis0 ---> mounted as --> /var/run/redis0
# /var/run/redis1 ---> mounted as --> /var/run/redis1 .. etc
# The below logic extracts the base DIR's where database_config.json's for various namespaces exist.
# redis_dir_list is a string of form "/var/run/redis0/ /var/run/redis1/ /var/run/redis2/"
{%- if docker_container_name != "database" %}
if [ -n "$redis_dir_list" ]; then
for redis_dir in $redis_dir_list
do
REDIS_MNT=$REDIS_MNT" -v $redis_dir:$redis_dir:rw "
done
fi
{%- endif %}
else
# This part of code is applicable for Multi-ASIC platforms. Here we mount the namespace specific
# redis directory into the docker running in that namespace. Below eg: is for namespace "asic1"
# /var/run/redis1 ---> mounted as --> /var/run/redis1
# redis_dir_list is a string of form "/var/run/redis0/ /var/run/redis1/ /var/run/redis2/"
if [ -n "$redis_dir_list" ]; then
id=`expr $DEV + 1`
redis_dir=`echo $redis_dir_list | cut -d " " -f $id`
REDIS_MNT=" -v $redis_dir:$redis_dir:rw "
fi
{%- if docker_container_name == "database" %}
NET="bridge"
{%- else %}
NET="container:database$DEV"
{%- endif %}
fi
{%- if docker_container_name == "bgp" %}
if [ "$DEV" ]; then
if [ ! -d "/etc/sonic/frr/$DEV" ]; then
mkdir /etc/sonic/frr/$DEV
cp -r /etc/sonic/frr/*.conf /etc/sonic/frr/$DEV
fi
fi
{%- endif %}
{%- if sonic_asic_platform == "mellanox" %}
# TODO: Mellanox will remove the --tmpfs exception after SDK socket path changed in new SDK version
{%- endif %}
docker create {{docker_image_run_opt}} \
--net=$NET \
--uts=host \{# W/A: this should be set per-docker, for those dockers which really need host's UTS namespace #}
{%- if install_debug_image == "y" %}
-v /src:/src:ro -v /debug:/debug:rw \
{%- endif %}
{%- if '--log-driver=json-file' in docker_image_run_opt or '--log-driver' not in docker_image_run_opt %}
--log-opt max-size=2M --log-opt max-file=5 \
{%- endif %}
{%- if sonic_asic_platform == "mellanox" %}
{%- if docker_container_name == "syncd" %}
-v /var/log/mellanox/sniffer:/var/log/mellanox/sniffer:rw \
-v mlnx_sdk_socket:/var/run/sx_sdk \
-v mlnx_sdk_ready:/tmp \
-v /dev/shm:/dev/shm:rw \
-e SX_API_SOCKET_FILE=/var/run/sx_sdk/sx_api.sock \
{%- elif docker_container_name == "pmon" %}
-v /var/run/hw-management:/var/run/hw-management:rw \
-v mlnx_sdk_socket:/var/run/sx_sdk \
-v mlnx_sdk_ready:/tmp \
-e SX_API_SOCKET_FILE=/var/run/sx_sdk/sx_api.sock \
-v /dev/shm:/dev/shm:rw \
{%- else %}
--tmpfs /tmp \
{%- endif %}
{%- endif %}
{%- if docker_container_name == "bgp" %}
-v /etc/sonic/frr/$DEV:/etc/frr:rw \
{%- endif %}
-v /var/run/redis$DEV:/var/run/redis:rw \
$REDIS_MNT \
-v /usr/share/sonic/device/$PLATFORM:/usr/share/sonic/platform:ro \
{%- if docker_container_name != "database" %}
-v /usr/share/sonic/device/$PLATFORM/$HWSKU/$DEV:/usr/share/sonic/hwsku:ro \
{%- endif %}
{%- if sonic_asic_platform != "mellanox" %}
--tmpfs /tmp \
{%- endif %}
--tmpfs /var/tmp \
{%- if docker_container_name == "database" %}
--env "NAMESPACE_ID"="$DEV" \
--env "NAMESPACE_PREFIX"="$NAMESPACE_PREFIX" \
--env "NAMESPACE_COUNT"=$NUM_ASIC \
{%- endif %}
--name={{docker_container_name}}$DEV {{docker_image_name}}:latest || {
echo "Failed to docker run" >&1
exit 4
}
preStartAction
docker start {{docker_container_name}}$DEV
postStartAction
}
wait() {
docker wait {{docker_container_name}}$DEV
}
stop() {
docker stop {{docker_container_name}}$DEV
{%- if docker_container_name == "database" %}
if [ "$DEV" ]; then
ip netns delete "$NET_NS"
fi
{%- endif %}
}
OP=$1
DEV=$2 # namespace/device number to operate on
NAMESPACE_PREFIX="asic"
if [ "$DEV" ]; then
NET_NS="$NAMESPACE_PREFIX$DEV" #name of the network namespace
# While using -n (namespace) argument, sonic-cfggen/sonic-db-cli uses redis UNIX socket
# for accessing redis DB in a namespace. This unix socket has permission restrictions since
# it is created by systemd database.servce started with [User] as [root].
# sudo is needed here for services which are started by systemd with [User] as [admin]
# and needs to override this unix socket permission restrictions.
SONIC_CFGGEN="sudo sonic-cfggen -n $NET_NS"
SONIC_DB_CLI="sudo sonic-db-cli -n $NET_NS"
else
NET_NS=""
SONIC_CFGGEN="sonic-cfggen"
SONIC_DB_CLI="sonic-db-cli"
fi
case "$1" in
start|wait|stop)
$1
;;
*)
echo "Usage: $0 {start namespace(optional)|wait namespace(optional)|stop namespace(optional)}"
exit 1
;;
esac