initd: initd-mi.patch

Support Multi-Instance protocol daemons initd

OSPFd is the first of the multi-instance daemons. This patch allows the
starting, stopping, restarting and monitoring of multiple instances of
the same protocol daemon.

Multiple instances are specified in the daemons file using a new variable:
ospfd_instances="1,2"

Absence of this variable means ospfd will start in legacy, single instance
mode. The original "ospfd=yes" line is still required.

Daemons are started with the "-n <instance>" option. Each daemon is named
"<daemon>-<instance>", for example "ospfd-1", "ospfd-2" etc. Similarly,
pid files are ospfd-1.pid and vty files are named ospfd-1.vty.

We're also introducing a new file, /etc/default/quagga to store the
default value for the maximum instances associated with a daemon.

watchquagga and others are unmodified and everything else just works once
this code is in place.

The code has been enhanced to support restarting watchquagga with only the
updated daemons when an individual daemon is stopped or started. For example,
without this patch, stopping just bgpd would terminate watchquagga even if
ospfd and zebra are still running. Similarly, starting just bgpd when ospfd
and zebra are running wouldn't update watchquagga to include bgpd. Furthermore,
when the daemons file is modified and a daemon is no longer deemed necessary
and quagga restarted, the daemon is not killed. For example, switching
ospfd=yes to ospfd=no and restarting the quagga will leave ospfd daemon
running. This case is also fixed with this patch.

However, adding a new instance to the ospfd_instances file and starting
just that instance will start just that instance and add it to watchquagga.
Similarly, a single instance maybe stopped or restarted.

Caveat emptor: With multi-instance daemons, stopping a single instance and then
starting a different instance will cause all instances to be monitored by
watchquagga i.e. all instances will be restarted, if necessary.

Signed-off-by: Dinesh G Dutt <ddutt at cumulusnetworks.com>
This commit is contained in:
Donald Sharp 2015-05-19 18:03:41 -07:00
parent a231c1d588
commit 8c5fbd4858
2 changed files with 215 additions and 39 deletions

1
debian/quagga.default vendored Normal file
View File

@ -0,0 +1 @@
MAX_INSTANCES=5

253
debian/quagga.init.d vendored
View File

@ -16,16 +16,24 @@
PATH=/bin:/usr/bin:/sbin:/usr/sbin PATH=/bin:/usr/bin:/sbin:/usr/sbin
D_PATH=/usr/lib/quagga D_PATH=/usr/lib/quagga
C_PATH=/etc/quagga C_PATH=/etc/quagga
V_PATH=/var/run/quagga
# Local Daemon selection may be done by using /etc/quagga/daemons. # Local Daemon selection may be done by using /etc/quagga/daemons.
# See /usr/share/doc/quagga/README.Debian.gz for further information. # See /usr/share/doc/quagga/README.Debian.gz for further information.
# Keep zebra first and do not list watchquagga! # Keep zebra first and do not list watchquagga!
DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd babeld" DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd babeld"
MAX_INSTANCES=5
# Print the name of the pidfile. # Print the name of the pidfile.
pidfile() pidfile()
{ {
echo "/var/run/quagga/$1.pid" echo "$V_PATH/$1.pid"
}
# Print the name of the vtysh.
vtyfile()
{
echo "$V_PATH/$1.vty"
} }
# Check if daemon is started by using the pidfile. # Check if daemon is started by using the pidfile.
@ -61,9 +69,12 @@ check_daemon()
# check for daemon binary # check for daemon binary
if [ ! -x "$D_PATH/$1" ]; then return 1; fi if [ ! -x "$D_PATH/$1" ]; then return 1; fi
# check for config file # check for config file
if [ ! -r "$C_PATH/$1.conf" ]; then if [ -n "$2" ]; then
echo -n " (not started without config file)" if [ ! -r "$C_PATH/$1-$2.conf" ]; then
return 1
fi
elif [ ! -r "$C_PATH/$1.conf" ]; then
return 1 return 1
fi fi
fi fi
@ -74,36 +85,70 @@ check_daemon()
# The Quagga daemons creates the pidfile when starting. # The Quagga daemons creates the pidfile when starting.
start() start()
{ {
echo -n " $1"
if ! check_daemon $1; then return; fi
if [ "$1" = "watchquagga" ]; then if [ "$1" = "watchquagga" ]; then
# We may need to restart watchquagga if new daemons are added and/or
# removed
if started "$1" ; then
stop watchquagga
else
# Echo only once. watchquagga is printed in the stop above
echo -n " $1"
fi
start-stop-daemon \ start-stop-daemon \
--start \ --start \
--pidfile=`pidfile $1` \ --pidfile=`pidfile $1` \
--exec "$D_PATH/$1" \ --exec "$D_PATH/$1" \
-- \ -- \
"${watchquagga_options[@]}" "${watchquagga_options[@]}"
elif [ -n "$2" ]; then
echo -n " $1-$2"
if ! check_daemon $1 $2 ; then
echo -n " (not started without config file)"
return;
fi
start-stop-daemon \
--start \
--pidfile=`pidfile $1-$2` \
--exec "$D_PATH/$1" \
-- \
`eval echo "$""$1""_options"` -n "$2"
else else
start-stop-daemon \ echo -n " $1"
--start \ if ! check_daemon $1; then
--pidfile=`pidfile $1` \ echo -n " (not started without config file)"
--exec "$D_PATH/$1" \ return;
-- \ fi
`eval echo "$""$1""_options"`
start-stop-daemon \
--start \
--pidfile=`pidfile $1` \
--exec "$D_PATH/$1" \
-- \
`eval echo "$""$1""_options"`
fi fi
} }
# Stop the daemon given in the parameter, printing its name to the terminal. # Stop the daemon given in the parameter, printing its name to the terminal.
stop() stop()
{ {
if ! started "$1" ; then local inst
echo -n " ($1)"
if [ -n "$2" ]; then
inst="$1-$2"
else
inst="$1"
fi
if ! started "$inst" ; then
echo -n " ($inst)"
return 0 return 0
else else
PIDFILE=`pidfile $1` PIDFILE=`pidfile $inst`
PID=`cat $PIDFILE 2>/dev/null` PID=`cat $PIDFILE 2>/dev/null`
start-stop-daemon --stop --quiet --oknodo --exec "$D_PATH/$1" start-stop-daemon --stop --quiet --oknodo --pidfile "$PIDFILE" --exec "$D_PATH/$1"
# #
# Now we have to wait until $DAEMON has _really_ stopped. # Now we have to wait until $DAEMON has _really_ stopped.
# #
@ -121,8 +166,9 @@ stop()
echo -n "." echo -n "."
done done
fi fi
echo -n " $1" echo -n " $inst"
rm -f `pidfile $1` rm -f `pidfile $inst`
rm -f `vtyfile $inst`
fi fi
} }
@ -131,13 +177,15 @@ convert_daemon_prios()
{ {
for name in $DAEMONS zebra vtysh_enable watchquagga_enable; do for name in $DAEMONS zebra vtysh_enable watchquagga_enable; do
# First, assign the value set by the user to $value # First, assign the value set by the user to $value
eval value=\$$name eval value=\${${name}:0:3}
# Daemon not activated or entry missing? # Daemon not activated or entry missing?
if [ "$value" = "no" -o "$value" = "" ]; then value=0; fi if [ "$value" = "no" -o "$value" = "" ]; then value=0; fi
# These strings parsed for backwards compatibility. # These strings parsed for backwards compatibility.
if [ "$value" = "yes" -o "$value" = "true" ]; then value=1; fi if [ "$value" = "yes" -o "$value" = "true" ]; then
value=1;
fi
# Zebra is threatened special. It must be between 0=off and the first # Zebra is threatened special. It must be between 0=off and the first
# user assigned value "1" so we increase all other enabled daemons' values. # user assigned value "1" so we increase all other enabled daemons' values.
@ -154,6 +202,7 @@ start_watchquagga()
local daemon_name local daemon_name
local daemon_prio local daemon_prio
local found_one local found_one
local daemon_inst
# Start the monitor daemon only if desired. # Start the monitor daemon only if desired.
if [ 0 -eq "$watchquagga_enable" ]; then if [ 0 -eq "$watchquagga_enable" ]; then
@ -173,8 +222,22 @@ start_watchquagga()
for daemon_name in $DAEMONS; do for daemon_name in $DAEMONS; do
eval daemon_prio=\$$daemon_name eval daemon_prio=\$$daemon_name
if [ "$daemon_prio" -gt 0 ]; then if [ "$daemon_prio" -gt 0 ]; then
watchquagga_options+=($daemon_name) eval "daemon_inst=\${${daemon_name}_instances//,/ }"
found_one=1 if [ -n "$daemon_inst" ]; then
for inst in ${daemon_inst}; do
eval "inst_disable=\${${daemon_name}_${inst}}"
if [ -z ${inst_disable} ] || [ ${inst_disable} != 0 ]; then
if check_daemon $daemon_name $inst; then
watchquagga_options+=("${daemon_name}-${inst}")
fi
fi
done
else
if check_daemon $daemon_name; then
watchquagga_options+=($daemon_name)
fi
fi
found_one=1
fi fi
done done
@ -201,21 +264,65 @@ stop_prio()
local wanted_prio local wanted_prio
local daemon_prio local daemon_prio
local daemon_list local daemon_list
local daemon_inst
local inst
if [ -n "$2" ] && [[ "$2" =~ (.*)-(.*) ]]; then
daemon=${BASH_REMATCH[1]}
inst=${BASH_REMATCH[2]}
else
daemon="$2"
fi
wanted_prio=$1 wanted_prio=$1
daemon_list=${2:-$DAEMONS} daemon_list=${daemon:-$DAEMONS}
echo -n "Stopping Quagga daemons (prio:$wanted_prio):" echo -n "Stopping Quagga daemons (prio:$wanted_prio):"
for prio_i in `seq 10 -1 $wanted_prio`; do for prio_i in `seq 10 -1 $wanted_prio`; do
for daemon_name in $daemon_list; do for daemon_name in $daemon_list; do
eval daemon_prio=\$$daemon_name eval daemon_prio=\${${daemon_name}:0:3}
daemon_inst=""
if [ $daemon_prio -eq $prio_i ]; then if [ $daemon_prio -eq $prio_i ]; then
stop "$daemon_name" eval "daemon_inst=\${${daemon_name}_instances//,/ }"
if [ -n "$daemon_inst" ]; then
for i in ${daemon_inst}; do
if [ -n "$inst" ] && [ "$i" == "$inst" ]; then
stop "$daemon_name" "$inst"
elif [ x"$inst" == x ]; then
stop "$daemon_name" "$i"
fi
done
else
stop "$daemon_name"
fi
fi fi
done done
done done
echo "." echo "."
if [ -z "$inst" ]; then
# Now stop other daemons that're prowling, coz the daemons file changed
echo -n "Stopping other quagga daemons"
if [ -n "$daemon" ]; then
eval "file_list_suffix="$V_PATH"/"$daemon*""
else
eval "file_list_suffix="$V_PATH/*""
fi
for pidfile in $file_list_suffix.pid; do
PID=`cat $pidfile 2>/dev/null`
start-stop-daemon --stop --quiet --oknodo --pidfile "$pidfile"
echo -n "."
rm -rf "$pidfile"
done
echo "."
echo -n "Removing remaining .vty files"
for vtyfile in $file_list_suffix.vty; do
rm -rf "$vtyfile"
done
echo "."
fi
} }
# Starts all daemons that have a higher level of priority than the given. # Starts all daemons that have a higher level of priority than the given.
@ -225,23 +332,75 @@ start_prio()
local wanted_prio local wanted_prio
local daemon_prio local daemon_prio
local daemon_list local daemon_list
local daemon_name
local daemon_inst
local inst
if [ -n "$2" ] && [[ "$2" =~ (.*)-(.*) ]]; then
daemon=${BASH_REMATCH[1]}
inst=${BASH_REMATCH[2]}
else
daemon="$2"
fi
wanted_prio=$1 wanted_prio=$1
daemon_list=${2:-$DAEMONS} daemon_list=${daemon:-$DAEMONS}
echo -n "Starting Quagga daemons (prio:$wanted_prio):" echo -n "Starting Quagga daemons (prio:$wanted_prio):"
for prio_i in `seq 1 $wanted_prio`; do for prio_i in `seq 1 $wanted_prio`; do
for daemon_name in $daemon_list; do for daemon_name in $daemon_list; do
eval daemon_prio=\$$daemon_name eval daemon_prio=\$${daemon_name}
daemon_inst=""
if [ $daemon_prio -eq $prio_i ]; then if [ $daemon_prio -eq $prio_i ]; then
start "$daemon_name" eval "daemon_inst=\${${daemon_name}_instances//,/ }"
if [ -n "$daemon_inst" ]; then
if [ `echo "$daemon_inst" | wc -w` -gt ${MAX_INSTANCES} ]; then
echo "Max instances supported is ${MAX_INSTANCES}. Aborting"
exit 1
fi
# Check if we're starting again by switching from single instance
# to MI version
if started "$daemon_name"; then
PIDFILE=`pidfile $daemon_name`
start-stop-daemon \
--stop --quiet --oknodo \
--pidfile "$PIDFILE" \
--exec "$D_PATH/$daemon_name"
rm -f `pidfile $1`
rm -f `vtyfile $1`
fi
for i in ${daemon_inst}; do
if [ -n "$inst" ] && [ "$i" == "$inst" ]; then
start "$daemon_name" "$inst"
elif [ x"$inst" == x ]; then
start "$daemon_name" "$i"
fi
done
else
# Check if we're starting again by switching from
# single instance to MI version
eval "file_list_suffix="$V_PATH"/"$daemon_name-*""
for pidfile in $file_list_suffix.pid; do
start-stop-daemon --stop --quiet --oknodo --pidfile "$pidfile"
echo -n "."
rm -rf "$pidfile"
done
for vtyfile in $file_list_suffix.vty; do
rm -rf "$vtyfile"
done
start "$daemon_name"
fi
fi fi
done done
done done
echo "." echo "."
} }
######################################################### #########################################################
# Main program # # Main program #
######################################################### #########################################################
@ -253,13 +412,25 @@ start_prio()
. "$C_PATH/daemons" . "$C_PATH/daemons"
. "$C_PATH/debian.conf" . "$C_PATH/debian.conf"
# Read configuration variable file if it is present
[ -r /etc/default/quagga ] && . /etc/default/quagga
MAX_INSTANCES=${MAX_INSTANCES:=5}
# Set priority of un-startable daemons to 'no' and substitute 'yes' to '0' # Set priority of un-startable daemons to 'no' and substitute 'yes' to '0'
convert_daemon_prios convert_daemon_prios
if [ ! -d /var/run/quagga ]; then if [ ! -d $V_PATH ]; then
mkdir -p /var/run/quagga echo "Creating $V_PATH"
chown quagga:quagga /var/run/quagga mkdir -p $V_PATH
chmod 755 /var/run/quagga chown quagga:quagga $V_PATH
chmod 755 /$V_PATH
fi
if [ -n "$3" ]; then
dmn="$2"-"$3"
else
dmn="$2"
fi fi
case "$1" in case "$1" in
@ -273,7 +444,7 @@ case "$1" in
# Start all daemons # Start all daemons
cd $C_PATH/ cd $C_PATH/
if [ "$2" != "watchquagga" ]; then if [ "$2" != "watchquagga" ]; then
start_prio 10 $2 start_prio 10 $dmn
fi fi
vtysh_b vtysh_b
start_watchquagga start_watchquagga
@ -289,20 +460,24 @@ case "$1" in
stop|0) stop|0)
# Stop all daemons at level '0' or 'stop' # Stop all daemons at level '0' or 'stop'
stop_watchquagga stop_watchquagga
if [ "$2" != "watchquagga" ]; then if [ "$dmn" != "watchquagga" ]; then
stop_prio 0 $2 [ -n "${dmn}" ] && eval "${dmn/-/_}=0"
stop_prio 0 $dmn
fi fi
if [ -z "$2" -o "$2" = "zebra" ]; then if [ -z "$dmn" -o "$dmn" = "zebra" ]; then
echo "Removing all routes made by zebra." echo "Removing all routes made by zebra."
ip route flush proto zebra ip route flush proto zebra
else
[ -n "$dmn" ] && eval "${dmn/-/_}=0"
start_watchquagga
fi fi
;; ;;
restart|force-reload) restart|force-reload)
$0 stop $2 $0 stop $dmn
sleep 1 sleep 1
$0 start $2 $0 start $dmn
;; ;;
*) *)