#!/usr/bin/env bash set -euo pipefail ############################################################################### # Configuration ############################################################################### # Space separated host IPs. Include the master IP if the master node also needs # rmdc-watchdog-node installed. INSTALL_HOSTS="192.168.3.31 192.168.3.32 192.168.3.33" # Absolute binary path on the master node. Remote nodes will receive the binary # at the same absolute path. RMDC_WATCHDOG_NODE_BIN="/root/wdd/rmdc-watchdog-node" # Replace WDD_BOOTSTRAP_PSK in the systemd service. WDD_BOOTSTRAP_PSK="hO8dKlh9hSXZ25Bv9xsySxDe2rh7XikV" # Main NIC name. The script reads each node's IPv4 address on this NIC and uses # it as WDD_LISTEN_IP. MAIN_NIC="eno3" # Master node private IP. This replaces WDD_BOOTSTRAP_ALLOW_IPS. MASTER_IP="192.168.3.31" # Systemd service install path on every node. SERVICE_PATH="/usr/lib/systemd/system/rmdc-watchdog-node.service" # SSH settings for remote nodes. SSH_PORT="22" SSH_CONNECT_TIMEOUT_SEC="10" SSH_CONTROL_PERSIST="10m" ############################################################################### # Implementation ############################################################################### SERVICE_NAME="$(basename "$SERVICE_PATH")" REMOTE_TMP_DIR="/tmp/rmdc-watchdog-node-deploy" REMOTE_SERVICE_TMP="${REMOTE_TMP_DIR}/${SERVICE_NAME}" SSH_CONTROL_DIR="" SSH_CONTROL_PATH="" log() { printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*" } die() { printf 'error: %s\n' "$*" >&2 exit 1 } shell_quote() { printf "'" printf '%s' "${1-}" | sed "s/'/'\\\\''/g" printf "'" } require_command() { command -v "$1" >/dev/null 2>&1 || die "required command not found: $1" } validate_config() { [[ "$(id -u)" == "0" ]] || die "this script must run as root" [[ "$#" == "0" ]] || die "this script does not accept command line arguments; edit the configuration section instead" [[ -n "$INSTALL_HOSTS" ]] || die "INSTALL_HOSTS is empty" [[ -f "$RMDC_WATCHDOG_NODE_BIN" ]] || die "binary not found: $RMDC_WATCHDOG_NODE_BIN" [[ -n "$WDD_BOOTSTRAP_PSK" && "$WDD_BOOTSTRAP_PSK" != "change-me" ]] || die "WDD_BOOTSTRAP_PSK must be configured" [[ -n "$MAIN_NIC" ]] || die "MAIN_NIC must be configured" [[ -n "$MASTER_IP" ]] || die "MASTER_IP must be configured" [[ "$SERVICE_PATH" = /* ]] || die "SERVICE_PATH must be an absolute path" [[ "$RMDC_WATCHDOG_NODE_BIN" = /* ]] || die "RMDC_WATCHDOG_NODE_BIN must be an absolute path" require_command awk require_command basename require_command dirname require_command install require_command ip require_command mktemp require_command sed require_command systemctl } get_local_ipv4s() { { hostname -I 2>/dev/null | tr ' ' '\n' || true ip -o -4 addr show 2>/dev/null | awk '{split($4, a, "/"); print a[1]}' || true } | awk 'NF' | sort -u } is_local_host() { local host="$1" case "$host" in localhost|127.0.0.1|::1) return 0 ;; esac get_local_ipv4s | awk -v host="$host" '$0 == host {found = 1} END {exit found ? 0 : 1}' } local_nic_ip() { ip -o -4 addr show dev "$MAIN_NIC" scope global | awk '{split($4, a, "/"); print a[1]; exit}' } remote_sh() { local host="$1" local command="$2" ssh \ -p "$SSH_PORT" \ -o ConnectTimeout="$SSH_CONNECT_TIMEOUT_SEC" \ -o StrictHostKeyChecking=no \ -o UserKnownHostsFile=/dev/null \ -o LogLevel=ERROR \ -o ControlMaster=auto \ -o ControlPersist="$SSH_CONTROL_PERSIST" \ -o ControlPath="$SSH_CONTROL_PATH" \ "root@${host}" \ "bash -lc $(shell_quote "$command")" } remote_scp() { local source_path="$1" local host="$2" local target_path="$3" scp \ -P "$SSH_PORT" \ -o ConnectTimeout="$SSH_CONNECT_TIMEOUT_SEC" \ -o StrictHostKeyChecking=no \ -o UserKnownHostsFile=/dev/null \ -o LogLevel=ERROR \ -o ControlMaster=auto \ -o ControlPersist="$SSH_CONTROL_PERSIST" \ -o ControlPath="$SSH_CONTROL_PATH" \ "$source_path" \ "root@${host}:${target_path}" } cleanup_ssh_control() { local host if [[ -n "${SSH_CONTROL_DIR:-}" && -d "$SSH_CONTROL_DIR" ]]; then for host in $INSTALL_HOSTS; do if ! is_local_host "$host"; then ssh \ -p "$SSH_PORT" \ -o StrictHostKeyChecking=no \ -o UserKnownHostsFile=/dev/null \ -o LogLevel=ERROR \ -o ControlPath="$SSH_CONTROL_PATH" \ -O exit \ "root@${host}" >/dev/null 2>&1 || true fi done rm -rf "$SSH_CONTROL_DIR" fi } remote_nic_ip() { local host="$1" remote_sh "$host" "ip -o -4 addr show dev $(shell_quote "$MAIN_NIC") scope global | awk '{split(\$4, a, \"/\"); print a[1]; exit}'" } render_service() { local listen_ip="$1" local output_path="$2" local working_dir working_dir="$(dirname "$RMDC_WATCHDOG_NODE_BIN")" cat > "$output_path" <