379 lines
16 KiB
Bash
Executable File
379 lines
16 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# =====================================================================
|
|
# migrate_creator.sh
|
|
#
|
|
# Wird auf den QUELLSERVER kopiert (klassische Checkmk-/OMD-Direkt-
|
|
# installation auf Bare-Metal oder VM) und dort als root ausgefuehrt.
|
|
#
|
|
# Aufgabe: eine oder mehrere Sites auswaehlen und daraus ein Migrations-
|
|
# Bundle (.tar.gz) erzeugen, das auf dem Zielsystem mit
|
|
# ./scripts/cmk-manage.sh import --file <bundle>
|
|
# in den Checkmk-Container uebernommen wird.
|
|
#
|
|
# Die Agents muessen dabei NICHT neu eingerichtet werden, solange
|
|
# * der Site-Name unveraendert bleibt und
|
|
# * das Zielsystem dieselbe IP-Adresse / denselben DNS-Namen bekommt.
|
|
# Die Site-Zertifikate (Agent-Registrierung) sind im omd-Backup enthalten.
|
|
#
|
|
# Dieses Skript ist bewusst eigenstaendig - es hat ausser bash, tar und
|
|
# omd keine Abhaengigkeiten. Fuer die Oberflaeche wird dialog oder
|
|
# whiptail genutzt, falls vorhanden.
|
|
# =====================================================================
|
|
set -euo pipefail
|
|
|
|
VERSION="1.0"
|
|
BUNDLE_DIRNAME="cmkbundle"
|
|
OUT_DIR="${PWD}"
|
|
declare -a SITES=()
|
|
ALL=""; STOP_SITE=""; ASSUME_YES=""; USE_GUI=""; SCP_TARGET=""; LABEL=""
|
|
declare -a OMD_OPTS=()
|
|
|
|
if [ -t 1 ] && [ -z "${NO_COLOR:-}" ]; then
|
|
C_RED=$'\033[31m'; C_GRN=$'\033[32m'; C_YEL=$'\033[33m'
|
|
C_BLU=$'\033[34m'; C_BLD=$'\033[1m'; C_RST=$'\033[0m'
|
|
else
|
|
C_RED=; C_GRN=; C_YEL=; C_BLU=; C_BLD=; C_RST=
|
|
fi
|
|
log() { printf '%s[ INFO ]%s %s\n' "$C_BLU" "$C_RST" "$*"; }
|
|
ok() { printf '%s[ OK ]%s %s\n' "$C_GRN" "$C_RST" "$*"; }
|
|
warn() { printf '%s[ WARN ]%s %s\n' "$C_YEL" "$C_RST" "$*" >&2; }
|
|
err() { printf '%s[FEHLER]%s %s\n' "$C_RED" "$C_RST" "$*" >&2; }
|
|
die() { err "$*"; exit 1; }
|
|
|
|
usage() {
|
|
cat <<'EOF'
|
|
migrate_creator.sh - Migrations-Bundle einer Checkmk-Direktinstallation erzeugen
|
|
|
|
AUFRUF
|
|
migrate_creator.sh [Optionen]
|
|
|
|
OPTIONEN
|
|
-l, --list vorhandene Sites anzeigen und beenden
|
|
-s, --site SITE Site auswaehlen (mehrfach angebbar)
|
|
-a, --all alle Sites sichern
|
|
-o, --out VERZEICHNIS Zielverzeichnis fuer die .tar.gz-Bundles (Vorgabe: aktuelles)
|
|
--label TEXT Freitext-Kennzeichnung im Dateinamen
|
|
--no-rrds Performance-Daten (RRDs) auslassen -> kleineres Archiv
|
|
--no-logs Logdateien auslassen
|
|
--stop Site waehrend des Backups stoppen (konsistenteste Variante)
|
|
--scp ZIEL Bundle anschliessend per scp uebertragen,
|
|
z.B. --scp root@10.0.0.5:/opt/checkmk-stack/backups/
|
|
-g, --gui ncurses-Oberflaeche (dialog/whiptail)
|
|
-y, --yes keine Rueckfragen
|
|
-h, --help diese Hilfe
|
|
|
|
BEISPIELE
|
|
./migrate_creator.sh --list
|
|
./migrate_creator.sh --gui
|
|
./migrate_creator.sh --site prod --stop --out /var/tmp
|
|
./migrate_creator.sh --all --no-logs --scp root@10.0.0.5:/opt/checkmk-stack/backups/
|
|
EOF
|
|
}
|
|
|
|
# ---------------------------------------------------------------------
|
|
# Hilfsfunktionen
|
|
# ---------------------------------------------------------------------
|
|
human_size() {
|
|
local b="${1:-0}"
|
|
if [ "$b" -ge 1073741824 ] 2>/dev/null; then awk -v b="$b" 'BEGIN{printf "%.1f GiB", b/1073741824}'
|
|
elif [ "$b" -ge 1048576 ] 2>/dev/null; then awk -v b="$b" 'BEGIN{printf "%.1f MiB", b/1048576}'
|
|
elif [ "$b" -ge 1024 ] 2>/dev/null; then awk -v b="$b" 'BEGIN{printf "%.1f KiB", b/1024}'
|
|
else echo "${b} B"; fi
|
|
}
|
|
file_size() { stat -c %s "$1" 2>/dev/null || echo 0; }
|
|
confirm() {
|
|
[ -n "$ASSUME_YES" ] && return 0
|
|
local a; read -r -p "${1:-Fortfahren?} [j/N] " a </dev/tty || return 1
|
|
case "$a" in [jJyY]*) return 0 ;; *) return 1 ;; esac
|
|
}
|
|
primary_ip() {
|
|
ip -4 route get 1.1.1.1 2>/dev/null | awk '{for(i=1;i<=NF;i++) if($i=="src"){print $(i+1);exit}}' \
|
|
|| hostname -I 2>/dev/null | awk '{print $1}'
|
|
}
|
|
all_ips() { hostname -I 2>/dev/null | tr ' ' '\n' | sed '/^$/d' | paste -sd, - ; }
|
|
|
|
list_sites() { ls -1 /omd/sites 2>/dev/null | sed '/^$/d'; }
|
|
site_version(){ omd version -b "$1" 2>/dev/null | tr -d '\r'; }
|
|
site_state() { local rc=0; omd status --bare "$1" >/dev/null 2>&1 || rc=$?
|
|
case "$rc" in 0) echo laeuft ;; 1) echo teilweise ;; *) echo gestoppt ;; esac; }
|
|
site_size() { du -sb "/omd/sites/$1" 2>/dev/null | awk '{print $1}'; }
|
|
|
|
# ---------------------------------------------------------------------
|
|
# TUI-Helfer
|
|
# ---------------------------------------------------------------------
|
|
ui_init() {
|
|
if command -v dialog >/dev/null 2>&1; then UI_BIN=dialog
|
|
elif command -v whiptail >/dev/null 2>&1; then UI_BIN=whiptail
|
|
else UI_BIN=""; fi
|
|
UI_BACKTITLE="migrate_creator $VERSION - $(hostname)"
|
|
}
|
|
ui_menu() { local t="$1" x="$2"; shift 2; "$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$t" --menu "$x" 20 76 11 "$@" 3>&1 1>&2 2>&3; }
|
|
ui_checklist() { local t="$1" x="$2"; shift 2; "$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$t" --checklist "$x" 20 76 11 "$@" 3>&1 1>&2 2>&3; }
|
|
ui_input() { "$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$1" --inputbox "$2" 10 76 "${3:-}" 3>&1 1>&2 2>&3; }
|
|
ui_msg() { "$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$1" --msgbox "$2" 18 76; }
|
|
ui_yesno() { "$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$1" --yesno "$2" 14 76; }
|
|
ui_text() { "$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$1" --textbox "$2" 22 78; }
|
|
ui_info() { "$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$1" --infobox "$2" 8 76; }
|
|
|
|
# ---------------------------------------------------------------------
|
|
# Bundle erzeugen
|
|
# ---------------------------------------------------------------------
|
|
create_bundle() {
|
|
local site="$1"
|
|
local version state ts outfile tmp restarted="" sum size
|
|
|
|
version="$(site_version "$site")"
|
|
[ -n "$version" ] || die "Konnte die Version der Site '$site' nicht ermitteln."
|
|
state="$(site_state "$site")"
|
|
ts="$(date +%Y%m%d-%H%M%S)"
|
|
outfile="$OUT_DIR/cmkmigrate_${site}_${version}${LABEL:+_$LABEL}_${ts}.tar.gz"
|
|
|
|
log "Site '$site' Version $version Status $state Groesse $(human_size "$(site_size "$site")")"
|
|
|
|
if [ -n "$STOP_SITE" ] && [ "$state" != "gestoppt" ]; then
|
|
log "Stoppe Site '$site' ..."
|
|
omd stop "$site" >/dev/null
|
|
restarted=1
|
|
fi
|
|
|
|
tmp="$(mktemp -d)"
|
|
mkdir -p "$tmp/$BUNDLE_DIRNAME/siteinfo"
|
|
|
|
log "Erzeuge omd-Backup ${OMD_OPTS[*]+(${OMD_OPTS[*]})} - das kann dauern ..."
|
|
if ! omd backup ${OMD_OPTS[@]+"${OMD_OPTS[@]}"} "$site" "$tmp/$BUNDLE_DIRNAME/site.tar.gz"; then
|
|
[ -n "$restarted" ] && omd start "$site" >/dev/null || true
|
|
rm -rf "$tmp"
|
|
die "omd backup der Site '$site' ist fehlgeschlagen."
|
|
fi
|
|
|
|
if [ -n "$restarted" ]; then
|
|
log "Starte Site '$site' wieder ..."
|
|
omd start "$site" >/dev/null || warn "Site '$site' konnte nicht wieder gestartet werden."
|
|
fi
|
|
|
|
# --- Zusatzinformationen (rein informativ, fuer die Nachkontrolle) ---
|
|
omd config "$site" show >"$tmp/$BUNDLE_DIRNAME/siteinfo/omd-config.txt" 2>/dev/null || true
|
|
omd status --bare "$site" >"$tmp/$BUNDLE_DIRNAME/siteinfo/omd-status.txt" 2>/dev/null || true
|
|
ip -4 addr show >"$tmp/$BUNDLE_DIRNAME/siteinfo/ip-addr.txt" 2>/dev/null || true
|
|
{
|
|
echo "hostname : $(hostname -f 2>/dev/null || hostname)"
|
|
echo "ips : $(all_ips)"
|
|
echo "kernel : $(uname -r)"
|
|
[ -r /etc/os-release ] && (. /etc/os-release && echo "os : $PRETTY_NAME")
|
|
echo "omd sites:"; omd sites 2>/dev/null || true
|
|
} >"$tmp/$BUNDLE_DIRNAME/siteinfo/system.txt"
|
|
# Anzahl registrierter Agent-Verbindungen (nur zur Kontrolle nach dem Import)
|
|
if [ -d "/omd/sites/$site/var/agent-receiver" ]; then
|
|
find "/omd/sites/$site/var/agent-receiver" -maxdepth 2 -type d 2>/dev/null \
|
|
| wc -l >"$tmp/$BUNDLE_DIRNAME/siteinfo/agent-receiver-entries.txt" || true
|
|
fi
|
|
|
|
sum="$(sha256sum "$tmp/$BUNDLE_DIRNAME/site.tar.gz" | awk '{print $1}')"
|
|
size="$(file_size "$tmp/$BUNDLE_DIRNAME/site.tar.gz")"
|
|
printf '%s site.tar.gz\n' "$sum" >"$tmp/$BUNDLE_DIRNAME/site.tar.gz.sha256"
|
|
|
|
cat >"$tmp/$BUNDLE_DIRNAME/MANIFEST" <<EOF
|
|
BUNDLE_VERSION=1
|
|
BUNDLE_TYPE=migrate
|
|
SITE=$site
|
|
CMK_VERSION=$version
|
|
SOURCE_KIND=baremetal
|
|
SOURCE_HOST=$(hostname -f 2>/dev/null || hostname)
|
|
SOURCE_IP=$(primary_ip)
|
|
SOURCE_IPS=$(all_ips)
|
|
SOURCE_OS=$( [ -r /etc/os-release ] && (. /etc/os-release && echo "$PRETTY_NAME") || echo unbekannt )
|
|
CREATED=$(date -Is)
|
|
CREATED_BY=migrate_creator.sh/$VERSION
|
|
ARCHIVE=site.tar.gz
|
|
ARCHIVE_SHA256=$sum
|
|
ARCHIVE_SIZE=$size
|
|
OMD_BACKUP_OPTS=${OMD_OPTS[*]-}
|
|
LABEL=$LABEL
|
|
SITE_STOPPED_DURING_BACKUP=$([ -n "$STOP_SITE" ] && echo yes || echo no)
|
|
AGENT_RECEIVER_PORT=8000
|
|
EOF
|
|
|
|
cat >"$tmp/$BUNDLE_DIRNAME/INFO.txt" <<EOF
|
|
Checkmk Migrations-Bundle
|
|
=========================
|
|
Site : $site
|
|
Checkmk-Version : $version
|
|
Quellserver : $(hostname -f 2>/dev/null || hostname)
|
|
Quell-IP(s) : $(all_ips)
|
|
Erstellt : $(date -Is)
|
|
Nutzdaten : site.tar.gz ($(human_size "$size"))
|
|
SHA256 : $sum
|
|
|
|
Import auf dem Zielsystem (Container-Installation):
|
|
1) Bundle in das Verzeichnis ./backups des Projekts kopieren
|
|
2) In der .env die passende Checkmk-Version eintragen:
|
|
CMK_IMAGE_TAG=${version%.*}
|
|
und den Stack neu erstellen: ./scripts/cmk-manage.sh up
|
|
3) Import ausfuehren:
|
|
./scripts/cmk-manage.sh import --file backups/$(basename "$outfile") --set-default-site
|
|
|
|
Damit die vorhandenen Agents ohne Neuinstallation weiterarbeiten:
|
|
* Der Site-Name muss "$site" bleiben (kein --as-site verwenden).
|
|
* Das Zielsystem muss die IP/den DNS-Namen des Quellservers uebernehmen.
|
|
* Port 8000 (Agent-Receiver) und 443/80 (Web) muessen erreichbar sein.
|
|
* Der alte Server sollte danach abgeschaltet bzw. dessen Site gestoppt werden.
|
|
EOF
|
|
|
|
log "Verpacke Bundle ..."
|
|
mkdir -p "$OUT_DIR"
|
|
tar -czf "$outfile" -C "$tmp" "$BUNDLE_DIRNAME"
|
|
rm -rf "$tmp"
|
|
|
|
ok "Bundle erstellt: $outfile ($(human_size "$(file_size "$outfile")"))"
|
|
LAST_BUNDLE="$outfile"
|
|
|
|
if [ -n "$SCP_TARGET" ]; then
|
|
log "Uebertrage per scp nach $SCP_TARGET ..."
|
|
scp "$outfile" "$SCP_TARGET" && ok "Uebertragung abgeschlossen." \
|
|
|| warn "scp fehlgeschlagen - bitte manuell kopieren."
|
|
fi
|
|
}
|
|
|
|
print_summary() {
|
|
cat <<EOF
|
|
|
|
${C_BLD}Weiteres Vorgehen${C_RST}
|
|
1) Bundle auf das Zielsystem kopieren, z.B.:
|
|
scp ${LAST_BUNDLE:-<bundle>} root@<ziel>:/opt/checkmk-stack/backups/
|
|
2) Dort importieren:
|
|
cd /opt/checkmk-stack
|
|
./scripts/cmk-manage.sh import --file backups/$(basename "${LAST_BUNDLE:-<bundle>}") --set-default-site
|
|
3) Quellserver: Site stoppen bzw. Server abschalten, damit es keine
|
|
doppelte Ueberwachung und keinen IP-Konflikt gibt:
|
|
omd stop <site> && omd disable ... / systemctl poweroff
|
|
4) Zielsystem auf die IP/den DNS-Namen des Quellservers umstellen -
|
|
dann melden sich die vorhandenen Agents unveraendert weiter.
|
|
|
|
EOF
|
|
}
|
|
|
|
# ---------------------------------------------------------------------
|
|
# GUI-Ablauf
|
|
# ---------------------------------------------------------------------
|
|
gui_main() {
|
|
ui_init
|
|
[ -n "$UI_BIN" ] || die "Fuer --gui wird 'dialog' oder 'whiptail' benoetigt.
|
|
Debian/Ubuntu : apt-get install -y dialog
|
|
RHEL/Rocky : dnf install -y dialog"
|
|
|
|
local -a items=() s
|
|
while IFS= read -r s; do
|
|
[ -n "$s" ] || continue
|
|
items+=("$s" "$(site_version "$s") | $(site_state "$s") | $(human_size "$(site_size "$s")")" off)
|
|
done < <(list_sites)
|
|
[ ${#items[@]} -gt 0 ] || die "Auf diesem System wurde keine OMD-Site gefunden."
|
|
|
|
ui_msg "Checkmk Migration" \
|
|
"Dieses Skript erzeugt aus einer Direktinstallation ein Migrations-Bundle
|
|
(.tar.gz) fuer den Import in eine Checkmk-Container-Installation.
|
|
|
|
Server : $(hostname -f 2>/dev/null || hostname)
|
|
IP(s) : $(all_ips)
|
|
Sites : $(list_sites | tr '\n' ' ')
|
|
|
|
Die Agents muessen spaeter NICHT neu eingerichtet werden, solange der
|
|
Site-Name gleich bleibt und das Zielsystem dieselbe IP uebernimmt."
|
|
|
|
local chosen
|
|
chosen="$(ui_checklist "Sites auswaehlen" "Mit LEERTASTE markieren, mit ENTER bestaetigen:" "${items[@]}")" || exit 0
|
|
[ -n "$chosen" ] || { ui_msg "Abbruch" "Es wurde keine Site ausgewaehlt."; exit 0; }
|
|
SITES=()
|
|
local x
|
|
for x in $(printf '%s' "$chosen" | tr -d '"'); do SITES+=("$x"); done
|
|
|
|
local opt
|
|
opt="$(ui_checklist "Optionen" "Optionen fuer das Backup:" \
|
|
stop "Site waehrend des Backups stoppen (empfohlen)" on \
|
|
no-rrds "Performance-Daten (RRD) auslassen" off \
|
|
no-logs "Logdateien auslassen" off)" || exit 0
|
|
case "$opt" in *stop*) STOP_SITE=1 ;; esac
|
|
case "$opt" in *no-rrds*) OMD_OPTS+=(--no-rrds) ;; esac
|
|
case "$opt" in *no-logs*) OMD_OPTS+=(--no-logs) ;; esac
|
|
|
|
OUT_DIR="$(ui_input "Zielverzeichnis" "Wohin sollen die Bundles geschrieben werden?" "${OUT_DIR}")" || exit 0
|
|
LABEL="$(ui_input "Kennzeichnung" "Optionaler Text fuer den Dateinamen (leer = keiner):" "")" || LABEL=""
|
|
LABEL="$(printf '%s' "$LABEL" | tr -cd 'A-Za-z0-9._-')"
|
|
|
|
if ui_yesno "Direkt uebertragen?" "Sollen die Bundles anschliessend per scp auf das Zielsystem kopiert werden?"; then
|
|
SCP_TARGET="$(ui_input "scp-Ziel" "Ziel angeben (Benutzer@Host:/Pfad/):" "root@zielserver:/opt/checkmk-stack/backups/")" || SCP_TARGET=""
|
|
fi
|
|
|
|
ui_yesno "Starten" "Jetzt Bundle(s) erzeugen fuer:\n\n ${SITES[*]}\n\nZiel: $OUT_DIR\n$([ -n "$STOP_SITE" ] && echo 'Die Site(s) werden dabei kurz gestoppt!')" || exit 0
|
|
|
|
local logf; logf="$(mktemp)"
|
|
clear
|
|
ASSUME_YES=1
|
|
local site
|
|
for site in "${SITES[@]}"; do
|
|
create_bundle "$site" 2>&1 | tee -a "$logf"
|
|
done
|
|
print_summary | tee -a "$logf" >/dev/null
|
|
ui_text "Fertig" "$logf"
|
|
rm -f "$logf"
|
|
print_summary
|
|
}
|
|
|
|
# ---------------------------------------------------------------------
|
|
# Argumente
|
|
# ---------------------------------------------------------------------
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
-l|--list) LIST_ONLY=1; shift ;;
|
|
-s|--site) SITES+=("$2"); shift 2 ;;
|
|
-a|--all) ALL=1; shift ;;
|
|
-o|--out) OUT_DIR="$2"; shift 2 ;;
|
|
--label) LABEL="$2"; shift 2 ;;
|
|
--no-rrds) OMD_OPTS+=(--no-rrds); shift ;;
|
|
--no-logs) OMD_OPTS+=(--no-logs); shift ;;
|
|
--stop) STOP_SITE=1; shift ;;
|
|
--scp) SCP_TARGET="$2"; shift 2 ;;
|
|
-g|--gui) USE_GUI=1; shift ;;
|
|
-y|--yes) ASSUME_YES=1; shift ;;
|
|
-h|--help) usage; exit 0 ;;
|
|
*) die "Unbekannte Option: $1 (--help fuer Hilfe)" ;;
|
|
esac
|
|
done
|
|
|
|
# ---------------------------------------------------------------------
|
|
# Vorbedingungen
|
|
# ---------------------------------------------------------------------
|
|
command -v omd >/dev/null 2>&1 \
|
|
|| die "Auf diesem System ist keine OMD-/Checkmk-Direktinstallation gefunden worden ('omd' fehlt)."
|
|
[ "$(id -u)" -eq 0 ] || die "Bitte als root ausfuehren (sudo $0 ...)."
|
|
|
|
if [ -n "${LIST_ONLY:-}" ]; then
|
|
printf '%-20s %-18s %-12s %s\n' "SITE" "VERSION" "STATUS" "GROESSE"
|
|
while IFS= read -r s; do
|
|
[ -n "$s" ] || continue
|
|
printf '%-20s %-18s %-12s %s\n' "$s" "$(site_version "$s")" "$(site_state "$s")" "$(human_size "$(site_size "$s")")"
|
|
done < <(list_sites)
|
|
exit 0
|
|
fi
|
|
|
|
if [ -n "$USE_GUI" ] || { [ ${#SITES[@]} -eq 0 ] && [ -z "$ALL" ] && [ -t 0 ]; }; then
|
|
gui_main
|
|
exit 0
|
|
fi
|
|
|
|
[ -n "$ALL" ] && mapfile -t SITES < <(list_sites)
|
|
[ ${#SITES[@]} -gt 0 ] || die "Keine Site angegeben. --site SITE, --all oder --gui verwenden."
|
|
|
|
for s in "${SITES[@]}"; do
|
|
list_sites | grep -qxF "$s" || die "Site '$s' existiert auf diesem Server nicht."
|
|
done
|
|
|
|
printf '\n%sZu sichernde Sites:%s %s\n' "$C_BLD" "$C_RST" "${SITES[*]}"
|
|
printf '%sZielverzeichnis :%s %s\n\n' "$C_BLD" "$C_RST" "$OUT_DIR"
|
|
[ -n "$STOP_SITE" ] && warn "Die Site(s) werden waehrend des Backups gestoppt!"
|
|
confirm "Jetzt Migrations-Bundle(s) erzeugen?" || die "Abgebrochen."
|
|
|
|
for s in "${SITES[@]}"; do
|
|
create_bundle "$s"
|
|
done
|
|
print_summary
|