keepalived: high-availability files and data sync

add new package keepalived-sync to synchronize files and data
between master and backup node. The master node uses SSH over rsync
to send and the backup node will use inotifywatch to watch received files.

The master node can track rsync.sh script to send configuration file on
a backup node based on the vrrp_script configuration of the same script.

The backup node will have a keepalived-inotify service, which would watch
for newly received files and it would call hotplug event. Each service
can keep its respective script under the keepalived hotplug directory and
executes commands to stop, start service or update any config in real-time.

Whenever a switchover will happen, the backup node would have the latest
config and data files from the master node.

Hotplug events can be used to apply config when files are received.

Signed-off-by: Jaymin Patel <jem.patel@gmail.com>
This commit is contained in:
Jaymin Patel 2022-09-09 19:10:49 +05:30
parent 83ff83e320
commit 33398a38aa
18 changed files with 912 additions and 15 deletions

View file

@ -274,4 +274,103 @@ endif
endef
define Package/keepalived-sync
SECTION:=net
CATEGORY:=Network
TITLE:=Keepalived Master and Backup Synchronization
DEPENDS:= +keepalived +rsync +inotifywait +sudo +@BUSYBOX_CUSTOM +@BUSYBOX_CONFIG_TIMEOUT
endef
define Package/keepalived-sync/description
Keepalived HA with Master to Backup files and data Synchronization
endef
define Package/keepalived-sync/conffiles
/etc/keepalived/scripts
/etc/keepalived/keys
endef
define Package/keepalived-sync/install
$(INSTALL_DIR) $(1)/etc/init.d
$(INSTALL_BIN) ./files/etc/init.d/keepalived-inotify \
$(1)/etc/init.d/keepalived-inotify
$(INSTALL_DIR) $(1)/usr/share/keepalived/scripts
$(INSTALL_BIN) ./files/usr/share/keepalived/scripts/rsync.sh \
$(1)/usr/share/keepalived/scripts/rsync.sh
$(INSTALL_DIR) $(1)/etc/keepalived/scripts
$(LN) /usr/share/keepalived/scripts/rsync.sh \
$(1)/etc/keepalived/scripts/rsync.sh
$(INSTALL_DIR) $(1)/usr/bin
$(INSTALL_BIN) ./files/usr/bin/keepalived-rsync-inotify \
$(1)/usr/bin/keepalived-rsync-inotify
$(INSTALL_DIR) $(1)/lib/functions/keepalived
$(INSTALL_DATA) ./files/lib/functions/keepalived/hotplug.sh \
$(1)/lib/functions/keepalived/hotplug.sh
$(INSTALL_DATA) ./files/lib/functions/keepalived/common.sh \
$(1)/lib/functions/keepalived/common.sh
$(INSTALL_DIR) $(1)/usr/libexec/keepalived/rpc
$(INSTALL_DATA) ./files/usr/libexec/keepalived/rpc/sync.sh \
$(1)/usr/libexec/keepalived/rpc/sync.sh
$(INSTALL_DIR) $(1)/etc/hotplug.d/keepalived
$(CP) ./files/etc/hotplug.d/keepalived/* \
$(1)/etc/hotplug.d/keepalived
endef
USER=keepalived
USER_ID=60001
USER_HOME=/usr/share/keepalived/rsync
SUDO_DIR=/etc/sudoers.d
SUDO_FILE=$(SUDO_DIR)/$(USER)
KEYS_DIR=/etc/keepalived/keys
define Package/keepalived-sync/postinst
#!/bin/sh
mkdir -p "$${IPKG_INSTROOT}/etc/uci-defaults"
DEFAULT_SCRIPT="$${IPKG_INSTROOT}/etc/uci-defaults/99-keepalived-sync"
cat << EOF > $${DEFAULT_SCRIPT}
#!/bin/sh
. /lib/functions.sh
mkdir -p $(KEYS_DIR)
group_add "$(USER)" "$(USER_ID)"
user_add "$(USER)" "$(USER_ID)" "$(USER_ID)" "$(USER)" "$(USER_HOME)" "/bin/ash"
mkdir -m 700 -p "$(USER_HOME)"
mkdir -m 700 -p "$(USER_HOME)/.ssh"
chown "$(USER)":"$(USER)" "$(USER_HOME)" -R
[ ! -d "$(SUDO_DIR)" ] && mkdir "$(SUDO_DIR)"
echo "$(USER) ALL= NOPASSWD:/usr/bin/rsync" > "$(SUDO_FILE)"
EOF
[ -z "$${IPKG_INSTROOT}" ] && [ -f "$${DEFAULT_SCRIPT}" ] && sh "$${DEFAULT_SCRIPT}"
exit 0
endef
define Package/keepalived-sync/postrm
#!/bin/sh
[ -n "$${IPKG_INSTROOT}" ] && exit 0
[ -d "$(KEYS_DIR)" ] && rm -rf "$(KEYS_DIR)"
[ -d "$(USER_HOME)" ] && rm -rf "$(USER_HOME)"
[ -f "$(SUDO_FILE)" ] && rm -f "$(SUDO_FILE)"
sed -i -e "/^$(USER):/d" /etc/passwd /etc/shadow /etc/group
exit 0
endef
$(eval $(call BuildPackage,keepalived))
$(eval $(call BuildPackage,keepalived-sync))

View file

@ -0,0 +1,12 @@
#!/bin/sh
# shellcheck source=/dev/null
. /lib/functions/keepalived/hotplug.sh
set_service_name rpcd
set_reload_if_sync
add_sync_file /etc/config/rpcd
keepalived_hotplug

View file

@ -0,0 +1,12 @@
#!/bin/sh
# shellcheck source=/dev/null
. /lib/functions/keepalived/hotplug.sh
set_service_name system
set_reload_if_sync
add_sync_file /etc/config/system
keepalived_hotplug

View file

@ -0,0 +1,12 @@
#!/bin/sh
# shellcheck source=/dev/null
. /lib/functions/keepalived/hotplug.sh
set_service_name ucitrack
set_reload_if_sync
add_sync_file /etc/config/ucitrack
keepalived_hotplug

View file

@ -0,0 +1,12 @@
#!/bin/sh
# shellcheck source=/dev/null
. /lib/functions/keepalived/hotplug.sh
set_service_name firewall
set_reload_if_sync
add_sync_file /etc/config/firewall
keepalived_hotplug

View file

@ -0,0 +1,15 @@
#!/bin/sh
# shellcheck source=/dev/null
. /lib/functions/keepalived/hotplug.sh
set_service_name dnsmasq
set_restart_if_master
set_stop_if_backup
set_reload_if_sync
add_sync_file /etc/config/dhcp
add_sync_file /tmp/dhcp.leases
keepalived_hotplug

View file

@ -0,0 +1,15 @@
#!/bin/sh
# shellcheck source=/dev/null
. /lib/functions/keepalived/hotplug.sh
set_service_name dropbear
set_reload_if_backup
set_reload_if_sync
add_sync_file /etc/config/dropbear
add_sync_file /etc/dropbear/dropbear_ed25519_host_key
add_sync_file /etc/dropbear/dropbear_rsa_host_key
keepalived_hotplug

View file

@ -0,0 +1,14 @@
#!/bin/sh
# shellcheck source=/dev/null
. /lib/functions/keepalived/hotplug.sh
set_service_name uhttpd
set_restart_if_sync
add_sync_file /etc/config/uhttpd
add_sync_file /etc/uhttpd.crt
add_sync_file /etc/uhttpd.key
keepalived_hotplug

View file

@ -0,0 +1,8 @@
#!/bin/sh
# shellcheck source=/dev/null
. /lib/functions/keepalived/hotplug.sh
add_sync_file /etc/config/luci
keepalived_hotplug

View file

@ -0,0 +1,18 @@
#!/bin/sh
# shellcheck source=/dev/null
. /lib/functions/keepalived/hotplug.sh
add_sync_file /etc/group
add_sync_file /etc/hosts
add_sync_file /etc/inittab
add_sync_file /etc/passwd
add_sync_file /etc/rc.local
add_sync_file /etc/profile
add_sync_file /etc/shadow
add_sync_file /etc/shell
add_sync_file /etc/shinit
add_sync_file /etc/sysctl.conf
add_sync_file /tmp/dhcp.leases
keepalived_hotplug

View file

@ -0,0 +1,65 @@
#!/bin/sh /etc/rc.common
START=99
USE_PROCD=1
PROG="/usr/bin/keepalived-rsync-inotify"
KEEPALIVED_USER=keepalived
KEEPALIVED_HOME=$(awk -F: "/^$KEEPALIVED_USER/{print \$6}" /etc/passwd)
start_instance() {
local cfg=$1
local vrrp_instance=$2
local peer=$3
config_get name $cfg name
[ -z "$name" ] && return
[ "$name" != "$peer" ] && return
config_get sync $cfg sync 0
[ "$sync" = "0" ] && return
config_get sync_mode $cfg sync_mode
[ "$sync_mode" != "receive" ] && return
config_get sync_dir $cfg sync_dir $KEEPALIVED_HOME
[ -z "$sync_dir" ] && return
[ ! -d "$sync_dir" ] && mkdir -m 755 -p "$sync_dir"
procd_open_instance "$name"
procd_set_param command /bin/sh "$PROG" "$vrrp_instance" "$name" "$sync_dir"
procd_set_param pidfile /var/run/keepalived-inotify-$name.pid
procd_close_instance
}
process_unicast_peer() {
local peer=$1
local vrrp_instance=$2
config_foreach start_instance peer "$vrrp_instance" "$peer"
}
process_vrrp_instance() {
local cfg=$1
local peer_instance=$2
local name unicast_peer
config_get name $cfg name
config_get unicast_peer $cfg unicast_peer
if [ -n "$peer_instance" ]; then
list_contains unicast_peer "$peer_instance" || return
process_unicast_peer "$peer_instance" "$name"
else
config_list_foreach $cfg unicast_peer process_unicast_peer "$name"
fi
}
start_service() {
local peer_instance=$1
config_load keepalived
config_foreach process_vrrp_instance vrrp_instance "$peer_instance"
}

View file

@ -256,6 +256,21 @@ print_track_bfd_indent() {
printf '\n' >> "$KEEPALIVED_CONF"
}
print_unicast_peer_indent() {
local section="$1"
local curr_track_elem="$2"
local indent="$3"
local name address
config_get name "$section" name
[ "$name" != "$curr_track_elem" ] && return 0
config_get address "$section" address
[ -z "$address" ] && return 0
printf '%b%s\n' "${indent}" "$address">> "$KEEPALIVED_CONF"
}
static_routes() {
local route
config_get route "$1" route
@ -403,7 +418,13 @@ vrrp_instance() {
# Handle simple lists of strings (with no spaces in between)
for opt in unicast_peer; do
config_get "$opt" "$1" "$opt"
print_list_indent "$opt"
eval optval=\$$opt
[ -z "$optval" ] && continue
printf '%b%s {\n' "${INDENT_1}" "$opt" >> "$KEEPALIVED_CONF"
for t in $optval; do
config_foreach print_unicast_peer_indent peer "$t" "$INDENT_2"
done
printf '%b}\n' "${INDENT_1}" >> "$KEEPALIVED_CONF"
done
unset optval

View file

@ -0,0 +1,47 @@
#!/bin/sh
# shellcheck disable=SC2039
__FILE__="$(basename "$0")"
KEEPALIVED_USER=keepalived
KEEPALIVED_DEBUG=0
__function__() {
type "$1" > /dev/null 2>&1
}
log() {
local facility=$1
shift
logger -t "${__FILE__}[$$]" -p "$facility" "$*"
}
log_info() {
log info "$*"
}
log_debug() {
[ "$KEEPALIVED_DEBUG" = "0" ] && return
log debug "$*"
}
log_notice() {
log notice "$*"
}
log_warn() {
log warn "$*"
}
log_err() {
log err "$*"
}
get_rsync_user() {
echo "$KEEPALIVED_USER"
}
get_rsync_user_home() {
awk -F: "/^$KEEPALIVED_USER/{print \$6}" /etc/passwd
}

View file

@ -0,0 +1,257 @@
#!/bin/sh
# shellcheck disable=SC2039
# shellcheck source=/dev/null
. /lib/functions/keepalived/common.sh
set_var() {
export "$1=$2"
}
get_var() {
eval echo "\"\${${1}}\""
}
get_var_flag() {
local value
value=$(get_var "$1")
value=${value:-0}
[ "$value" = "0" ] && return 1
return 0
}
_service() {
[ -z "$SERVICE_NAME" ] && return
local rc="/etc/init.d/$SERVICE_NAME"
[ ! -x "$rc" ] && return
case $1 in
start) $rc running || $rc start ;;
stop) $rc running && $rc stop ;;
reload)
if $rc running; then
$rc reload
else
$rc start
fi
;;
restart)
if $rc running; then
$rc restart
else
$rc start
fi
;;
esac
}
_start_service() {
_service start
}
_stop_service() {
_service stop
}
_restart_service() {
_service restart
}
_reload_service() {
_service reload
}
set_service_name() {
set_var SERVICE_NAME "$1"
}
add_sync_file() {
append SYNC_FILES_LIST "$1"
}
is_sync_file() {
list_contains SYNC_FILES_LIST "$1"
}
set_update_target() {
set_var UPDATE_TARGET "${1:-1}"
}
get_update_target() {
get_var UPDATE_TARGET
}
unset_update_target() {
set_var UPDATE_TARGET
}
is_update_target() {
get_var_flag UPDATE_TARGET
}
set_master_cb() {
set_var MASTER_CB "$1"
}
get_master_cb() {
get_var MASTER_CB
}
set_backup_cb() {
set_var BACKUP_CB "$1"
}
get_backup_cb() {
get_var BACKUP_CB
}
set_fault_cb() {
set_var FAULT_CB "$1"
}
get_fault_cb() {
get_var FAULT_CB
}
set_sync_cb() {
set_var SYNC_CB "$1"
}
get_sync_cb() {
get_var SYNC_CB
}
set_reload_if_master() {
set_var NOTIFY_MASTER_RELOAD 1
}
master_and_reload() {
get_var_flag NOTIFY_MASTER_RELOAD
}
set_restart_if_master() {
set_var NOTIFY_MASTER_RESTART 1
}
master_and_restart() {
get_var_flag NOTIFY_MASTER_RESTART
}
set_reload_if_backup() {
set_var NOTIFY_BACKUP_RELOAD 1
}
backup_and_reload() {
get_var_flag NOTIFY_BACKUP_RELOAD
}
set_stop_if_backup() {
set_var NOTIFY_BACKUP_STOP 1
}
backup_and_stop() {
get_var_flag NOTIFY_BACKUP_STOP 1
}
set_reload_if_sync() {
set_var NOTIFY_SYNC_RELOAD "${1:-1}"
}
get_reload_if_sync() {
get_var NOTIFY_SYNC_RELOAD
}
sync_and_reload() {
get_var_flag NOTIFY_SYNC_RELOAD
}
set_restart_if_sync() {
set_var NOTIFY_SYNC_RESTART 1
}
sync_and_restart() {
get_var_flag NOTIFY_SYNC_RESTART
}
_notify_master() {
if master_and_reload; then
log_debug "reload service $SERVICE_NAME"
_reload_service
elif master_and_restart; then
log_debug "restart service $SERVICE_NAME"
_restart_service
fi
}
_notify_backup() {
if backup_and_stop; then
log_debug "stop service $SERVICE_NAME"
_stop_service
elif backup_and_reload; then
log_debug "restart service $SERVICE_NAME"
_restart_service
fi
}
_notify_fault() {
return 0
}
_notify_sync() {
[ -z "$RSYNC_SOURCE" ] && return
[ -z "$RSYNC_TARGET" ] && return
if ! is_update_target; then
log_notice "skip $RSYNC_TARGET. Update target not set. To set use \"set_update_target 1\""
return
fi
is_sync_file "$RSYNC_TARGET" || return
if ! cp -a "$RSYNC_SOURCE" "$RSYNC_TARGET"; then
log_err "can not copy $RSYNC_SOURCE => $RSYNC_TARGET"
return
fi
log_debug "updated $RSYNC_SOURCE to $RSYNC_TARGET"
if sync_and_reload; then
log_debug "reload service $SERVICE_NAME"
_reload_service
elif sync_and_restart; then
log_debug "restart service $SERVICE_NAME"
_restart_service
fi
}
call_cb() {
[ $# -eq 0 ] && return
if __function__ "$1"; then
log_debug "calling function \"$1\""
"$1"
else
log_err "function \"$1\" not defined"
fi
}
keepalived_hotplug() {
[ -z "$(get_master_cb)" ] && set_master_cb _notify_master
[ -z "$(get_backup_cb)" ] && set_backup_cb _notify_backup
[ -z "$(get_fault_cb)" ] && set_fault_cb _notify_fault
[ -z "$(get_sync_cb)" ] && set_sync_cb _notify_sync
[ -z "$(get_update_target)" ] && set_update_target "$@"
[ -z "$(get_reload_if_sync)" ] && set_reload_if_sync "$@"
case $ACTION in
NOTIFY_MASTER) call_cb "$(get_master_cb)" ;;
NOTIFY_BACKUP) call_cb "$(get_backup_cb)" ;;
NOTIFY_FAULT) call_cb "$(get_fault_cb)" ;;
NOTIFY_SYNC) call_cb "$(get_sync_cb)" ;;
esac
}

View file

@ -0,0 +1,54 @@
#!/bin/sh
# shellcheck shell=ash
# shellcheck source=/dev/null
. /lib/functions/keepalived/common.sh
if [ $# -lt 3 ]; then
echo "$0 <vrrp_instance> <peer> <rsync_dir>"
exit 1
fi
VRRP_INSTANCE=$1
PEER=$2
RSYNC_DIR=$3
INOTIFY_ACTIONS="create,delete,modify,move,moved_to,moved_from"
INOTIFY_PID=""
TMP_DIR=/tmp/keepalived
FIFO_FILE="$TMP_DIR"/inotifywait-$PEER.fifo
daemonize_inotifywait() {
/usr/bin/inotifywait -q -r --exclude '/\..+' -o "$FIFO_FILE" -m "$RSYNC_DIR" -e ${INOTIFY_ACTIONS} 2> /dev/null &
INOTIFY_PID="$!"
}
main() {
local inotify_action inotify_dir inotify_file
local source_file target_file
[ ! -d "$TMP_DIR" ] && mkdir "$TMP_DIR"
mkfifo "${FIFO_FILE}" || exit 1
daemonize_inotifywait
while read -r inotify_dir inotify_action inotify_file; do
source_file="${inotify_dir}${inotify_file}"
target_file=$(echo "${inotify_dir}" | sed -e "s:${RSYNC_DIR}::g")"${inotify_file}"
log_debug "received $target_file ($inotify_action) in $source_file"
ACTION=NOTIFY_SYNC TYPE=peer NAME=$PEER INSTANCE=$VRRP_INSTANCE \
RSYNC_SOURCE="${source_file}" RSYNC_TARGET="${target_file}" \
/sbin/hotplug-call keepalived
done < "$FIFO_FILE"
}
TRAP() {
[ -n "$INOTIFY_PID" ] && kill "$INOTIFY_PID"
[ -e "$FIFO_FILE" ] && rm -f "$FIFO_FILE"
}
trap TRAP TERM INT
main "$@"

View file

@ -0,0 +1,59 @@
#!/bin/sh
# shellcheck disable=SC2039
# shellcheck source=/dev/null
. /usr/share/libubox/jshn.sh
# shellcheck source=/dev/null
. /lib/functions.sh
peer() {
local cfg=$1
local c_name=$2
local name last_sync_time last_sync_status
config_get name "$cfg" name
[ "$name" != "$c_name" ] && return
config_get last_sync_time "$cfg" last_sync_time 0
config_get last_sync_status "$cfg" last_sync_status NA
json_add_object unicast_peer
json_add_string name "$name"
json_add_int time "$last_sync_time"
json_add_string status "$last_sync_status"
json_close_array
}
unicast_peer() {
config_foreach peer peer "$1"
}
vrrp_instance() {
local cfg=$1
local name
config_get name "$cfg" name
json_add_object vrrp_instance
json_add_string name "$name"
json_add_array unicast_peer
config_list_foreach "$cfg" unicast_peer unicast_peer
json_close_array
json_close_object
}
rsync_status() {
config_load keepalived
json_init
json_add_array vrrp_instance
config_foreach vrrp_instance vrrp_instance
json_close_array
json_dump
}
sync_help() {
json_add_object rsync_status
json_close_object
}

View file

@ -1,6 +1,10 @@
#!/bin/sh
# shellcheck disable=SC2039
# shellcheck source=/dev/null
. /lib/functions.sh
# shellcheck source=/dev/null
. /usr/share/libubox/jshn.sh
RPC_SCRIPTS=/usr/libexec/keepalived/rpc
@ -16,21 +20,22 @@ foreach_extra() {
[ ! -d $RPC_SCRIPTS ] && return
for file in $RPC_SCRIPTS/*; do
for file in "$RPC_SCRIPTS"/*; do
obj="${file##*/}"
$1 "${obj%%.*}"
done
}
keepalived_dump() {
local stats_file="/tmp/keepalived.json"
local pids
local stats_file pids
stats_file="/tmp/keepalived.json"
[ -f "$stats_file" ] && rm -f "$stats_file"
pids=$(pidof /usr/sbin/keepalived)
if [ -n "$pids" ]; then
kill -37 $pids > /dev/null 2>&1
kill -37 "$pids" > /dev/null 2>&1
json_load "{ \"status\" : $(cat $stats_file) }"
else
json_init
@ -50,21 +55,28 @@ call_extra() {
}
call_method() {
case "$1" in
local cmd=$1
case "$cmd" in
dump)
keepalived_dump
;;
*)
call_extra $1
call_extra "$cmd"
;;
esac
}
list_extra() {
if __function__ "${1}_help"; then
${1}_help
local arg func
arg=$1
func="${arg}_help"
if __function__ "$func"; then
$func
else
json_add_object "$1"
json_add_object "$arg"
json_close_object
fi
}
@ -77,18 +89,21 @@ list_methods() {
json_add_object dump
json_close_object
foreach_extra list_extra ${1}
foreach_extra list_extra "${1}"
json_dump
}
main () {
case "$1" in
main() {
local cmd=$1
shift
case "$cmd" in
list)
list_methods
list_methods "$@"
;;
call)
call_method $2
call_method "$@"
;;
esac
}

View file

@ -0,0 +1,162 @@
#!/bin/sh
# shellcheck disable=SC2039
# shellcheck source=/dev/null
. /lib/functions.sh
# shellcheck source=/dev/null
. /lib/functions/keepalived/common.sh
RSYNC_USER=$(get_rsync_user)
RSYNC_HOME=$(get_rsync_user_home)
utc_timestamp() {
date -u +%s
}
update_last_sync_time() {
uci_revert_state keepalived "$1" last_sync_time
uci_set_state keepalived "$1" last_sync_time "$(utc_timestamp)"
}
update_last_sync_status() {
local cfg="$1"
shift
local status="$*"
uci_revert_state keepalived "$cfg" last_sync_status
uci_set_state keepalived "$cfg" last_sync_status "$status"
}
ha_sync_send() {
local cfg=$1
local address ssh_key ssh_port sync_list sync_dir sync_file count
local ssh_options ssh_remote dirs_list files_list
local changelog="/tmp/changelog"
config_get address "$cfg" address
[ -z "$address" ] && return 0
config_get ssh_port "$cfg" ssh_port 22
config_get sync_dir "$cfg" sync_dir "$RSYNC_HOME"
[ -z "$sync_dir" ] && return 0
config_get ssh_key "$cfg" ssh_key "$sync_dir"/.ssh/id_rsa
config_get sync_list "$cfg" sync_list
for sync_file in $sync_list $(sysupgrade -l); do
[ -f "$sync_file" ] && {
dir="${sync_file%/*}"
list_contains files_list "${sync_file}" || append files_list "${sync_file}"
}
[ -d "$sync_file" ] && dir="${sync_file}"
list_contains dirs_list "${sync_dir}${dir}" || append dirs_list "${sync_dir}${dir}"
done
ssh_options="-y -y -i $ssh_key -p $ssh_port"
ssh_remote="$RSYNC_USER@$address"
# shellcheck disable=SC2086
timeout 10 ssh $ssh_options $ssh_remote mkdir -m 755 -p "$dirs_list /tmp" || {
log_err "can not connect to $address. check key or connection"
update_last_sync_time "$cfg"
update_last_sync_status "$cfg" "SSH Connection Failed"
return 0
}
# shellcheck disable=SC2086
if rsync --out-format='%n' --dry-run -a --relative ${files_list} -e "ssh $ssh_options" --rsync-path="sudo rsync" "$ssh_remote":"$sync_dir" > "$changelog"; then
count=$(wc -l "$changelog")
if [ "${count%% *}" = "0" ]; then
log_debug "all files are up to date"
update_last_sync_time "$cfg"
update_last_sync_status "$cfg" "Up to Date"
return 0
fi
else
log_err "rsync dry run failed for $address"
update_last_sync_time "$cfg"
update_last_sync_status "$cfg" "Rsync Detection Failed"
return 0
fi
# shellcheck disable=SC2086
rsync -a --relative ${files_list} ${changelog} -e "ssh $ssh_options" --rsync-path="sudo rsync" "$ssh_remote":"$sync_dir" || {
log_err "rsync transfer failed for $address"
update_last_sync_time "$cfg"
update_last_sync_status "$cfg" "Rsync Transfer Failed"
}
log_info "keepalived sync is compeleted for $address"
update_last_sync_time "$cfg"
update_last_sync_status "$cfg" "Successful"
}
ha_sync_receive() {
local cfg=$1
local ssh_pubkey
local name auth_file home_dir
config_get name "$cfg" name
config_get sync_dir "$cfg" sync_dir "$RSYNC_HOME"
[ -z "$sync_dir" ] && return 0
config_get ssh_pubkey "$cfg" ssh_pubkey
[ -z "$ssh_pubkey" ] && return 0
home_dir=$sync_dir
auth_file="$home_dir/.ssh/authorized_keys"
if ! grep -q "^$ssh_pubkey$" "$auth_file" 2> /dev/null; then
log_notice "public key not found. Updating"
echo "$ssh_pubkey" > "$auth_file"
chown "$RSYNC_USER":"$RSYNC_USER" "$auth_file"
fi
/etc/init.d/keepalived-inotify enabled || /etc/init.d/keepalived-inotify enable
/etc/init.d/keepalived-inotify running "$name" || /etc/init.d/keepalived-inotify start "$name"
}
ha_sync_each_peer() {
local cfg="$1"
local c_name="$2"
local name sync sync_mode
config_get name "$cfg" name
[ "$name" != "$c_name" ] && return 0
config_get sync "$cfg" sync 0
[ "$sync" = "0" ] && return 0
config_get sync_mode "$cfg" sync_mode
[ -z "$sync_mode" ] && return 0
case "$sync_mode" in
send) ha_sync_send "$cfg" ;;
receive) ha_sync_receive "$cfg" ;;
esac
}
ha_sync_peers() {
config_foreach ha_sync_each_peer peer "$1"
}
ha_sync() {
config_list_foreach "$1" unicast_peer ha_sync_peers
}
main() {
local lockfile="/var/lock/keepalived-rsync.lock"
if ! lock -n "$lockfile" > /dev/null 2>&1; then
log_info "another process is already running"
return 1
fi
config_load keepalived
config_foreach ha_sync vrrp_instance
lock -u "$lockfile"
return 0
}
main "$@"