Route ansible_cmd through ppf inventory instead of /opt/ansible default. Eliminates dynamic inventory warnings and connects via WireGuard IPs.
171 lines
5.4 KiB
Bash
171 lines
5.4 KiB
Bash
#!/bin/bash
|
|
# ppf-common.sh -- shared library for PPF operations toolkit
|
|
# Source this file; do not execute directly.
|
|
|
|
set -eu
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Paths
|
|
# ---------------------------------------------------------------------------
|
|
PPF_DIR="${PPF_DIR:-$HOME/git/ppf}"
|
|
ANSIBLE_DIR="/opt/ansible"
|
|
ANSIBLE_VENV="${ANSIBLE_DIR}/venv/bin/activate"
|
|
PPF_INVENTORY="${PPF_DIR}/tools/playbooks/inventory.ini"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Host topology
|
|
# ---------------------------------------------------------------------------
|
|
MASTER="odin"
|
|
WORKERS="cassius edge sentinel"
|
|
ALL_HOSTS="odin cassius edge sentinel"
|
|
|
|
# Container names per role
|
|
MASTER_CONTAINER="ppf"
|
|
WORKER_CONTAINER="ppf-worker"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Colors (respects NO_COLOR -- https://no-color.org)
|
|
# ---------------------------------------------------------------------------
|
|
if [ -z "${NO_COLOR:-}" ] && [ -t 1 ]; then
|
|
C_RST='\033[0m'
|
|
C_DIM='\033[2m'
|
|
C_BOLD='\033[1m'
|
|
C_RED='\033[38;5;167m'
|
|
C_GREEN='\033[38;5;114m'
|
|
C_YELLOW='\033[38;5;180m'
|
|
C_BLUE='\033[38;5;110m'
|
|
C_CYAN='\033[38;5;116m'
|
|
else
|
|
C_RST='' C_DIM='' C_BOLD='' C_RED='' C_GREEN=''
|
|
C_YELLOW='' C_BLUE='' C_CYAN=''
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Output helpers
|
|
# ---------------------------------------------------------------------------
|
|
log_ok() { printf "${C_GREEN} ✓${C_RST} %s\n" "$*"; }
|
|
log_err() { printf "${C_RED} ✗${C_RST} %s\n" "$*" >&2; }
|
|
log_warn() { printf "${C_YELLOW} ⚠${C_RST} %s\n" "$*"; }
|
|
log_info() { printf "${C_BLUE} ●${C_RST} %s\n" "$*"; }
|
|
log_dim() { printf "${C_DIM} %s${C_RST}\n" "$*"; }
|
|
|
|
die() { log_err "$@"; exit 1; }
|
|
|
|
# Section header
|
|
section() {
|
|
printf "\n${C_BOLD}${C_CYAN} %s${C_RST}\n" "$*"
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Host resolution helpers
|
|
# ---------------------------------------------------------------------------
|
|
is_master() { [ "$1" = "$MASTER" ]; }
|
|
is_worker() {
|
|
local h
|
|
for h in $WORKERS; do [ "$h" = "$1" ] && return 0; done
|
|
return 1
|
|
}
|
|
|
|
container_name() {
|
|
if is_master "$1"; then echo "$MASTER_CONTAINER"; else echo "$WORKER_CONTAINER"; fi
|
|
}
|
|
|
|
# Expand target aliases into host list
|
|
# "all" -> all hosts
|
|
# "workers" -> worker hosts
|
|
# "odin" -> just odin
|
|
# Multiple args are concatenated with comma
|
|
resolve_targets() {
|
|
local targets=""
|
|
local arg
|
|
for arg in "$@"; do
|
|
case "$arg" in
|
|
all) targets="${targets:+$targets }$ALL_HOSTS" ;;
|
|
workers) targets="${targets:+$targets }$WORKERS" ;;
|
|
master) targets="${targets:+$targets }$MASTER" ;;
|
|
*) targets="${targets:+$targets }$arg" ;;
|
|
esac
|
|
done
|
|
# Deduplicate while preserving order
|
|
echo "$targets" | tr ' ' '\n' | awk '!seen[$0]++' | tr '\n' ' ' | sed 's/ $//'
|
|
}
|
|
|
|
# Convert space-separated host list to comma-separated for ansible
|
|
hosts_csv() {
|
|
echo "$*" | tr ' ' ','
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Ansible wrapper
|
|
# ---------------------------------------------------------------------------
|
|
# Runs ansible with toolkit inventory via venv.
|
|
# Usage: ansible_cmd <ansible args...>
|
|
ansible_cmd() {
|
|
(
|
|
# shellcheck disable=SC1090
|
|
. "$ANSIBLE_VENV"
|
|
cd "$ANSIBLE_DIR"
|
|
ansible -i "$PPF_INVENTORY" "$@"
|
|
)
|
|
}
|
|
|
|
# Runs ansible-playbook with toolkit inventory via venv.
|
|
# Usage: ansible_playbook_cmd <ansible-playbook args...>
|
|
ansible_playbook_cmd() {
|
|
(
|
|
# shellcheck disable=SC1090
|
|
. "$ANSIBLE_VENV"
|
|
cd "$ANSIBLE_DIR"
|
|
ansible-playbook "$@"
|
|
)
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Remote podman/compose wrappers
|
|
# ---------------------------------------------------------------------------
|
|
# Run a podman command on a remote host as the podman user.
|
|
# Uses dynamic UID discovery.
|
|
# Usage: podman_cmd HOST "podman subcommand..."
|
|
podman_cmd() {
|
|
local host="$1"; shift
|
|
local cmd="$*"
|
|
ansible_cmd "$host" -m raw -a \
|
|
"uid=\$(id -u podman) && sudo -u podman XDG_RUNTIME_DIR=/run/user/\$uid $cmd"
|
|
}
|
|
|
|
# Run a podman-compose subcommand on a remote host.
|
|
# Usage: compose_cmd HOST "subcommand [args]"
|
|
compose_cmd() {
|
|
local host="$1"; shift
|
|
local cmd="$*"
|
|
ansible_cmd "$host" -m raw -a \
|
|
"uid=\$(id -u podman) && cd /home/podman/ppf && sudo -u podman XDG_RUNTIME_DIR=/run/user/\$uid podman-compose $cmd"
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Validation
|
|
# ---------------------------------------------------------------------------
|
|
validate_syntax() {
|
|
local errors=0
|
|
local f
|
|
section "Validating Python syntax"
|
|
for f in "$PPF_DIR"/*.py; do
|
|
[ -f "$f" ] || continue
|
|
if python3 -m py_compile "$f" 2>/dev/null; then
|
|
log_dim "$(basename "$f")"
|
|
else
|
|
log_err "$(basename "$f")"
|
|
errors=$((errors + 1))
|
|
fi
|
|
done
|
|
if [ "$errors" -gt 0 ]; then
|
|
die "$errors file(s) failed syntax check"
|
|
fi
|
|
log_ok "All files valid"
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Version
|
|
# ---------------------------------------------------------------------------
|
|
PPF_TOOLS_VERSION="1.0.0"
|