#!/bin/bash if [ "$1" = "complete" ]; then compgen -W "help status start stop restart reload check list_options" "$2" exit 0 fi . `dirname $0`/functions option pidpath standard_option /var/run option sockpath standard_option /var/state option statedir standard_option /var/state/vrrp_state option config standard_option /var/state/vrrp.conf option group multiple_option option notify standard_option option only_vrrp boolean_option option only_check boolean_option option check_vrid boolean_option option track_svc multiple_option option track_mgt multiple_option option track_inter_svc standard_option 2 option track_inter_mgt standard_option 2 option track_weight_if standard_option -6 option track_weight_mgt standard_option 2 option track_weight_svc standard_option 8 option track_rise_svc standard_option 2 option track_rise_mgt standard_option 2 option track_fall_svc standard_option 2 option track_fall_mgt standard_option 2 option procname reserved_option keepalived option bin reserved_option /usr/sbin/keepalived option pidfile reserved_option option cmdline reserved_option # Takes some parameters from the network interface sections : # vrrp [inst ] id # vrrp [inst ] prio # vrrp [inst ] address # vrrp [inst ] auth_type PASS # vrrp [inst ] auth_password | vrrp password # vrrp [inst ] garp # vrrp [inst ] no-address # vrrp [inst ] use-vmac # vrrp [inst ] v2 | v3 # set the correct pidfile # keepalived uses several pidfile with different names. The best to do is to # cure the default name to check that the user doesn't play with it. function fct_begin_section { pidfile= } function fct_end_section { local svcname=$1 local instname=$2 local pidfilecheck local pidfilevrrp if [ -n "$pidfile" ]; then echo "[$1.$2] WARNING! ignoring pidfile from config.rc. Use 'pidpath' instead." fi pidfile=${opt_pidpath}/keepalived_$svcname${instname:+_${instname}}.pid if [ -n "${opt_only_vrrp}" -o -z "${opt_only_check}" ] ; then pidfilevrrp=${pidfile%.pid}_v.pid fi if [ -z "${opt_only_vrrp}" -o -n "${opt_only_check}" ] ; then pidfilecheck=${pidfile%.pid}_c.pid fi cmdline="$bin ${opt_only_vrrp:+--vrrp} ${opt_only_check:+--check} -f $opt_config -p $pidfile ${pidfilevrrp:+-r $pidfilevrrp} ${pidfilecheck:+-c $pidfilecheck}" } # reinitialize the cache before starting the daemon function fct_pre_start { local svcname=$1 local instname=$2 local -a physint local tmp nbchk rm -f $opt_config >/dev/null 2>&1 echo "# automatically generated file from $0" > $opt_config if [ ${#opt_group[*]} = 0 ]; then echo " --> VRRP: no interface in group. Aborting." return 1 fi # consitute a list of physical interfaces in the group delimited by pipes # then remove the pipes once data are de-duplicated. physint=( ) opt_group=( ${opt_group[*]} ) for tmp in ${opt_group[*]%%.[0-9]*}; do if [ -z "${physint[*]}" -o "${physint[*]//|$tmp|}" = "${physint[*]}" ]; then physint=( "${physint[@]}" "|$tmp|" ) fi done physint=( "${physint[@]//|}" ) { nbchk=0 for tmp in ${opt_track_mgt[@]}; do echo "vrrp_script chk_$nbchk {" echo " script \"killall -0 $tmp\"" echo " interval ${opt_track_inter_mgt:-2}" echo " weight ${opt_track_weight_mgt:-2}" echo " fall ${opt_track_fall_mgt:-2}" echo " rise ${opt_track_rise_mgt:-2}" echo "}" (( nbchk++ )) done for tmp in ${opt_track_svc[@]}; do echo "vrrp_script chk_$nbchk {" echo " script \"killall -0 $tmp\"" echo " interval ${opt_track_inter_svc:-2}" echo " weight ${opt_track_weight_svc:-8}" echo " fall ${opt_track_fall_svc:-2}" echo " rise ${opt_track_rise_svc:-2}" echo "}" (( nbchk++ )) done /sbin/init.d/network --filter_option vrrp list_options | awk \ -v physint_str="${physint[*]}" -v group_str="${opt_group[*]}" \ -v track_w_if="$opt_track_weight_if" -v nbchk="$nbchk" \ -v notify="$opt_notify" \ ' # my_strtonum --- convert string to number (from gawk manual) function my_strtonum(str, ret, n, i, k, c) { if (str ~ /^0[0-7]*$/) { # octal n = length(str) ret = 0 for (i = 1; i <= n; i++) { c = substr(str, i, 1) # index() returns 0 if c not in string, # includes c == "0" k = index("1234567", c) ret = ret * 8 + k } } else if (str ~ /^0[xX][[:xdigit:]]+$/) { # hexadecimal str = substr(str, 3) # lop off leading 0x n = length(str) ret = 0 for (i = 1; i <= n; i++) { c = substr(str, i, 1) c = tolower(c) # index() returns 0 if c not in string, # includes c == "0" k = index("123456789abcdef", c) ret = ret * 16 + k } } else if (str ~ \ /^[-+]?([0-9]+([.][0-9]*([Ee][0-9]+)?)?|([.][0-9]+([Ee][-+]?[0-9]+)?))$/) { # decimal number, possibly floating point ret = str + 0 } else ret = "NOT-A-NUMBER" return ret } BEGIN { split(physint_str, physint); split(group_str, group); for (i in group) known_dev[group[i]]=group[i]; if (track_w_if == "") track_w_if = -6; } { if ($1 !~ "^network\\.") next; if ($2 != "vrrp") next; eth = substr($1, 9); if (!(eth in known_dev)) next; inst = eth; arg = 3; if ($arg == "") next; if ($arg ~ "^inst") { inst = inst "|" $(arg+1); arg += 2; } vrrp_index[inst] = inst; vrrp_eth[inst] = eth; if ($arg == "id") { if (vrrp_id[inst]) print " --> Warning: VRRP id", $(arg+1), "already specified as", vrrp_id[inst], "for network interface", eth > "/dev/stderr" vrrp_id[inst] = $(arg+1); } else if ($arg ~ "^prio") { if (vrrp_prio[inst]) print " --> Warning: VRRP prio", $(arg+1), "already specified as", vrrp_prio[inst], "for network interface", eth > "/dev/stderr" vrrp_prio[inst] = $(arg+1); } else if ($arg ~ "^addr") { tmp_addr=""; for ( i = arg+1 ; i <= NF ; i++ ) { tmp_addr = (tmp_addr != "" ? tmp_addr " " : "") $i; } vrrp_addr[inst] = (vrrp_addr[inst] != "" ? vrrp_addr[inst] "\n" : "") tmp_addr; } else if ($arg ~ "^auth_t") vrrp_auth[inst] = $(arg+1); else if ($arg ~ "^auth_p" || $arg ~ "^pass") vrrp_pass[inst] = $(arg+1); else if ($arg ~ "^garp") vrrp_garp[inst] = $(arg+1); else if ($arg == "no-address") vrrp_no_address[inst] = 1; else if ($arg == "use-vmac") vrrp_use_vmac[inst] = 1; else if ($arg == "v3") vrrp_use_v3[inst] = 1; else if ($arg == "v2") vrrp_use_v3[inst] = 0; else print " --> ignoring unknown VRRP option:", $arg, "in network interface", eth > "/dev/stderr" } END { for (vrrp in vrrp_index) { v = vrrp_index[vrrp]; if (vrrp_id[v] == "") { print " --> ignoring VRRP instance", v, " : missing VRRP id option." > "/dev/stderr"; continue; } if (my_strtonum(vrrp_id[v]) < 1 || my_strtonum(vrrp_id[v]) > 255) { print " --> ignoring VRRP instance", v, " : VRRP id", vrrp_id[v], "out of range (1-255)." > "/dev/stderr"; continue; } if (vrrp_prio[v] != "" && (my_strtonum(vrrp_prio[v]) < 0 || my_strtonum(vrrp_prio[v]) > 255)) { print " --> ignoring VRRP instance", v, " : VRRP prio", vrrp_prio[v], "out of range (0-255)." > "/dev/stderr"; continue; } print "vrrp_instance", v, "{"; if (vrrp_use_v3[v] == 1) print "\tversion 3"; else print "\tversion 2"; print "\tinterface", vrrp_eth[v]; print "\tvirtual_router_id", vrrp_id[v]; print "\tdont_check_address"; print "\tdont_use_iptables"; if (vrrp_use_vmac[v] != "") print "\tuse_vmac"; if (vrrp_prio[v] != "") print "\tpriority", vrrp_prio[v]; if (vrrp_garp[v] != "") print "\tgarp_master_delay", vrrp_garp[v]; else if (vrrp_use_vmac[v] != "") { print "\tgarp_master_repeat 1"; } else print "\tgarp_master_delay 60"; if (notify != "") printf ("\tnotify \"%s\"\n", notify); n = split(vrrp_addr[v], addr, "\n"); if (!vrrp_no_address[v] && n > 255) { print "# ==> Error in VRRP instance ",v,": more than 255 IP addresses. You need to set \"vrrp no-address\". Aborting." } if (n) { if (vrrp_no_address[v] != "") print "\tvirtual_ipaddress_excluded {"; else print "\tvirtual_ipaddress {"; for (i = 1; i <= n; i++) print "\t\t", addr[i]; print "\t}"; } split(vrrp_eth[v], physdev, "."); print "\ttrack_interface {"; for (i in physint) { dev = physint[i]; if (dev == physdev[1]) print "\t\t", dev; else print "\t\t", dev, "weight", track_w_if; } print "\t}"; if (nbchk > 0) { print "\ttrack_script {"; for (i = 0; i < nbchk; i++) printf("\t\tchk_%d\n", i); print "\t}"; } if (vrrp_pass[v] != "") { print "\tauthentication {"; print "\t\tauth_type", vrrp_auth[v]?vrrp_auth[v]:"PASS"; print "\t\tauth_pass", vrrp_pass[v]; print "\t}"; } print "}"; print ""; } } ' } >> $opt_config if [ $? != 0 ]; then echo "# ==> Fatal error while generating VRRP configuration. Aborting." >&2 return 1 fi grep "Error in VRRP instance" $opt_config >&2 && return 1 if [ "$opt_check_vrid" = 1 -a "$(grep virtual_router_id $opt_config|wc -l)" != \ "$(grep virtual_router_id $opt_config|sort -u|wc -l)" ]; then echo " --> VRRP: at least two interfaces share a same 'vrrp id'. Aborting." return 1 fi # now we will reset the interfaces states so that vrrp_notify knows # what it must track if [ -n "$opt_statedir" ]; then [ -e "$opt_statedir" ] && rm -rf "$opt_statedir" mkdir -p "$opt_statedir" for inst in ${opt_group[*]}; do echo "INIT" > "$opt_statedir/$inst" done fi return 0 } function report_individual_status { local state int inst [ -n "$opt_statedir" ] || return 0 if ! [ -e "$opt_statedir" ]; then echo "State dir $opt_statedir does not exist." return 1 fi echo "Interfaces states :" for int in ${opt_group[*]}; do inst="" for inst in $(echo $opt_statedir/$int\|*); do state="$(cat $inst 2>/dev/null) " [ -n "$state" ] && echo " ${inst##*/} : $state" done if [ -z "$inst" ]; then state="$(cat $opt_statedir/$int 2>/dev/null) " [ -n "$state" ] && echo " $int : $state" fi done state="$(cat $opt_statedir/.global 2>/dev/null) " [ -n "$state" ] && echo " global : $state" echo return 0 } function do_status { local svcname=$1 local instname=$2 local pids local real_pids local pidfile=${opt_pidpath}/keepalived_$svcname${instname:+_${instname}}.pid local pidfilevrrp=${pidfile%.pid}_v.pid local pidfilecheck=${pidfile%.pid}_c.pid if [ -s "$pidfile" -o -s "$pidfilevrrp" -o -s "$pidfilecheck" ]; then pids=$(cat "$pidfile" "$pidfilevrrp" "$pidfilecheck" 2>/dev/null) real_pids=$(ps ho pid $pids) if [ -n "$real_pids" ]; then echo "VRRP running using these pids : $(echo $real_pids)" report_individual_status return 0 fi echo "Stale PID files are present." fi svc_pidof -o $$ -p "" $procname > /dev/null 2>&1 ; pids=$REPLY if [ -n "$pids" ]; then echo "The PID files indicate that the VRRP daemon is not running." echo "However, you should check those pids : $(echo $pids)" report_individual_status return 1 else echo "VRRP daemon not running." return 1 fi } function do_stop { local svcname=$1 local instname=$2 local pidfile=${opt_pidpath}/keepalived_$svcname${instname:+_${instname}}.pid local pidfilevrrp=${pidfile%.pid}_v.pid local pidfilecheck=${pidfile%.pid}_c.pid local ret local refreshed_pids refreshed_pids=$( for i in "$pidfilevrrp" "$pidfilecheck" "$pidfile"; do if [ -s "$i" ] ; then ps ho comm,pid $(cat $i) 2>/dev/null | while read c p r; do [ "$c" != "keepalived" ] || echo "$p" done fi done ) if [ -n "${refreshed_pids}" ]; then echo -n "# Stopping VRRP daemon for $svcname${instname:+[$instname]} ..." set -- 6 5 4 3 2 1 while [ $# -gt 0 -a -n "${refreshed_pids}" ]; do if [ $# -le 2 ]; then kill -9 ${refreshed_pids} >/dev/null 2>&1 else kill -CONT ${refreshed_pids} >/dev/null 2>&1 kill -TERM ${refreshed_pids} >/dev/null 2>&1 fi refreshed_pids=$( for i in ${refreshed_pids}; do ps ho comm,pid $i 2>/dev/null | while read c p r; do [ "$c" != "keepalived" ] || echo "$p" done done ) if [ -n "${refreshed_pids}" ]; then echo -n "." sleep 1 fi shift done echo echo " ==> Done." else echo "# VRRP daemon already stopped." fi ret=0 return $ret } function do_reload { local svcname=$1 local instname=$2 local pidfile=${opt_pidpath}/keepalived_$svcname${instname:+_${instname}}.pid local pids local real_pids if [ -s "$pidfile" ]; then pids=$(cat "$pidfile" 2>/dev/null) real_pids=$(ps ho pid $pids 2>/dev/null) fi if [ -z "$real_pids" -o ! -e "$opt_config" ]; then echo "VRRP not running, restarting" do_restart "$1" "$2" return $? fi echo "Updating VRRP configuration." rm -f "$opt_config.bak" >/dev/null 2>&1 cp "$opt_config" "$opt_config.bak" >/dev/null 2>&1 if ! fct_pre_start "$1" "$2"; then echo "Failed to build a new config, reloading backup" mv "$opt_config.bak" "$opt_config" >/dev/null 2>&1 kill -HUP $real_pids return 1 fi rm -f "$opt_config.bak" >/dev/null 2>&1 kill -HUP $real_pids return $? } load_config