first commit
This commit is contained in:
Executable
+1019
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,290 @@
|
||||
#!/usr/bin/env bash
|
||||
# =====================================================================
|
||||
# common.sh - gemeinsame Funktionen fuer cmk-manage.sh
|
||||
# Wird per "source" eingebunden, nicht direkt ausgefuehrt.
|
||||
# =====================================================================
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# Ausgabe / Logging
|
||||
# ---------------------------------------------------------------------
|
||||
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[ %s ]%s %s\n' "$C_BLU" "INFO" "$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; }
|
||||
debug() { [ -n "${VERBOSE:-}" ] && printf ' > %s\n' "$*" >&2 || true; }
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# .env laden
|
||||
# ---------------------------------------------------------------------
|
||||
load_env() {
|
||||
local envfile="${1:-$PROJECT_DIR/.env}"
|
||||
if [ -f "$envfile" ]; then
|
||||
set -a
|
||||
# shellcheck disable=SC1090
|
||||
. "$envfile"
|
||||
set +a
|
||||
ENV_FILE="$envfile"
|
||||
else
|
||||
warn "Keine .env gefunden ($envfile) - es gelten die Standardwerte."
|
||||
ENV_FILE=""
|
||||
fi
|
||||
# Defaults
|
||||
CMK_CONTAINER_NAME="${CMK_CONTAINER_NAME:-checkmk}"
|
||||
NPM_CONTAINER_NAME="${NPM_CONTAINER_NAME:-nginx-proxy-manager}"
|
||||
CMK_SITE_ID="${CMK_SITE_ID:-cmk}"
|
||||
BACKUP_DIR="${BACKUP_DIR:-$PROJECT_DIR/backups}"
|
||||
case "$BACKUP_DIR" in
|
||||
/*) : ;;
|
||||
*) BACKUP_DIR="$PROJECT_DIR/${BACKUP_DIR#./}" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# .env-Wert dauerhaft setzen
|
||||
env_set() {
|
||||
local key="$1" val="$2" f="${ENV_FILE:-$PROJECT_DIR/.env}"
|
||||
[ -f "$f" ] || die "Keine .env vorhanden - kann '$key' nicht setzen."
|
||||
if grep -qE "^[#[:space:]]*${key}=" "$f"; then
|
||||
sed -i -E "s|^[#[:space:]]*${key}=.*|${key}=${val}|" "$f"
|
||||
else
|
||||
printf '%s=%s\n' "$key" "$val" >>"$f"
|
||||
fi
|
||||
export "$key=$val"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# Container-Runtime erkennen (docker / podman)
|
||||
# ---------------------------------------------------------------------
|
||||
detect_runtime() {
|
||||
if [ -n "${CMK_RUNTIME:-}" ]; then
|
||||
command -v "$CMK_RUNTIME" >/dev/null 2>&1 \
|
||||
|| die "CMK_RUNTIME='$CMK_RUNTIME' ist nicht installiert."
|
||||
RUNTIME="$CMK_RUNTIME"
|
||||
elif command -v docker >/dev/null 2>&1 && docker info >/dev/null 2>&1; then
|
||||
RUNTIME=docker
|
||||
elif command -v podman >/dev/null 2>&1; then
|
||||
RUNTIME=podman
|
||||
elif command -v docker >/dev/null 2>&1; then
|
||||
RUNTIME=docker # vorhanden, aber Daemon evtl. gestoppt
|
||||
else
|
||||
die "Weder docker noch podman gefunden. Bitte zuerst ./install.sh ausfuehren."
|
||||
fi
|
||||
debug "Runtime: $RUNTIME"
|
||||
}
|
||||
|
||||
# Compose-Kommando ermitteln (als Array COMPOSE_CMD)
|
||||
detect_compose() {
|
||||
COMPOSE_CMD=()
|
||||
if [ "$RUNTIME" = docker ]; then
|
||||
if docker compose version >/dev/null 2>&1; then
|
||||
COMPOSE_CMD=(docker compose)
|
||||
elif command -v docker-compose >/dev/null 2>&1; then
|
||||
COMPOSE_CMD=(docker-compose)
|
||||
fi
|
||||
else
|
||||
if command -v podman-compose >/dev/null 2>&1; then
|
||||
COMPOSE_CMD=(podman-compose)
|
||||
elif podman compose version >/dev/null 2>&1; then
|
||||
COMPOSE_CMD=(podman compose)
|
||||
fi
|
||||
fi
|
||||
[ ${#COMPOSE_CMD[@]} -gt 0 ] || die "Kein Compose-Kommando gefunden (docker compose / podman-compose)."
|
||||
debug "Compose: ${COMPOSE_CMD[*]}"
|
||||
}
|
||||
|
||||
compose() {
|
||||
detect_compose
|
||||
( cd "$PROJECT_DIR" && "${COMPOSE_CMD[@]}" "$@" )
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# Container-Helfer
|
||||
# ---------------------------------------------------------------------
|
||||
container_exists() { $RUNTIME container inspect "$1" >/dev/null 2>&1; }
|
||||
container_running() { [ "$($RUNTIME container inspect -f '{{.State.Running}}' "$1" 2>/dev/null)" = "true" ]; }
|
||||
|
||||
require_cmk_running() {
|
||||
container_exists "$CMK_CONTAINER_NAME" \
|
||||
|| die "Container '$CMK_CONTAINER_NAME' existiert nicht. Zuerst: $0 up"
|
||||
container_running "$CMK_CONTAINER_NAME" \
|
||||
|| die "Container '$CMK_CONTAINER_NAME' laeuft nicht. Zuerst: $0 up"
|
||||
}
|
||||
|
||||
# Kommando im Checkmk-Container als root ausfuehren
|
||||
ct() { $RUNTIME exec "$CMK_CONTAINER_NAME" "$@"; }
|
||||
# ... mit stdin
|
||||
cti() { $RUNTIME exec -i "$CMK_CONTAINER_NAME" "$@"; }
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# OMD-Helfer (im Container)
|
||||
# ---------------------------------------------------------------------
|
||||
cmk_sites() {
|
||||
ct sh -c 'ls -1 /omd/sites 2>/dev/null' | sed '/^$/d' || true
|
||||
}
|
||||
|
||||
cmk_site_exists() {
|
||||
cmk_sites | grep -qxF "$1"
|
||||
}
|
||||
|
||||
cmk_site_version() {
|
||||
ct omd version -b "$1" 2>/dev/null | tr -d '\r' || true
|
||||
}
|
||||
|
||||
# 0 = laeuft komplett, 1 = teilweise, 2 = gestoppt
|
||||
cmk_site_state() {
|
||||
local rc=0
|
||||
ct omd status --bare "$1" >/dev/null 2>&1 || rc=$?
|
||||
echo "$rc"
|
||||
}
|
||||
|
||||
cmk_site_state_text() {
|
||||
case "$(cmk_site_state "$1")" in
|
||||
0) echo "laeuft" ;;
|
||||
1) echo "teilweise" ;;
|
||||
*) echo "gestoppt" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
cmk_versions_installed() {
|
||||
ct omd versions -b 2>/dev/null | tr -d '\r' || ct sh -c 'ls -1 /omd/versions | grep -v "^default$"' 2>/dev/null || true
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# Diverses
|
||||
# ---------------------------------------------------------------------
|
||||
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 prompt="${1:-Fortfahren?} [j/N] " a
|
||||
read -r -p "$prompt" 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}'
|
||||
}
|
||||
|
||||
# =====================================================================
|
||||
# TUI-Helfer (dialog bevorzugt, sonst whiptail) - ncurses wie im mc
|
||||
# =====================================================================
|
||||
UI_BACKTITLE="Checkmk Stack Manager - Container-Runtime: ${RUNTIME:-?}"
|
||||
|
||||
ui_init() {
|
||||
[ -n "${UI_BIN:-}" ] && return 0
|
||||
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_H="${UI_H:-22}"; UI_W="${UI_W:-78}"; UI_MH="${UI_MH:-13}"
|
||||
export DIALOGRC="${DIALOGRC:-}"
|
||||
}
|
||||
|
||||
ui_available() { ui_init; [ -n "$UI_BIN" ]; }
|
||||
|
||||
ui_require() {
|
||||
ui_available || die "Fuer die TUI wird 'dialog' oder 'whiptail' benoetigt.
|
||||
Debian/Ubuntu : apt-get install -y dialog
|
||||
RHEL/Rocky : dnf install -y dialog"
|
||||
UI_BACKTITLE="Checkmk Stack Manager - Runtime: $RUNTIME"
|
||||
}
|
||||
|
||||
ui_menu() { # title text tag item [tag item ...] -> gewaehlter tag auf stdout
|
||||
local title="$1" text="$2"; shift 2
|
||||
"$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$title" \
|
||||
--menu "$text" "$UI_H" "$UI_W" "$UI_MH" "$@" 3>&1 1>&2 2>&3
|
||||
}
|
||||
|
||||
ui_checklist() { # title text tag item status ...
|
||||
local title="$1" text="$2"; shift 2
|
||||
"$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$title" \
|
||||
--checklist "$text" "$UI_H" "$UI_W" "$UI_MH" "$@" 3>&1 1>&2 2>&3
|
||||
}
|
||||
|
||||
ui_radiolist() {
|
||||
local title="$1" text="$2"; shift 2
|
||||
"$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$title" \
|
||||
--radiolist "$text" "$UI_H" "$UI_W" "$UI_MH" "$@" 3>&1 1>&2 2>&3
|
||||
}
|
||||
|
||||
ui_input() { # title text default
|
||||
"$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$1" \
|
||||
--inputbox "$2" 10 "$UI_W" "${3:-}" 3>&1 1>&2 2>&3
|
||||
}
|
||||
|
||||
ui_password() {
|
||||
"$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$1" \
|
||||
--passwordbox "$2" 10 "$UI_W" 3>&1 1>&2 2>&3
|
||||
}
|
||||
|
||||
ui_msg() { # title text
|
||||
"$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$1" --msgbox "$2" 16 "$UI_W"
|
||||
}
|
||||
|
||||
ui_yesno() { # title text -> exit 0 = ja
|
||||
"$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$1" --yesno "$2" 14 "$UI_W"
|
||||
}
|
||||
|
||||
ui_text() { # title file
|
||||
"$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$1" --textbox "$2" "$UI_H" "$UI_W"
|
||||
}
|
||||
|
||||
ui_info() { # title text (ohne OK-Button)
|
||||
"$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$1" --infobox "$2" 8 "$UI_W"
|
||||
}
|
||||
|
||||
# Kommando ausfuehren, Ausgabe live in Datei, danach in Textbox anzeigen
|
||||
ui_run() { # title cmd...
|
||||
local title="$1"; shift
|
||||
local logf; logf="$(mktemp)"
|
||||
ui_info "$title" "Bitte warten ...\n\n$*"
|
||||
if "$@" >"$logf" 2>&1; then
|
||||
ui_text "$title - erfolgreich" "$logf"
|
||||
rm -f "$logf"; return 0
|
||||
else
|
||||
ui_text "$title - FEHLGESCHLAGEN" "$logf"
|
||||
rm -f "$logf"; return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# Dateiauswahl im Midnight-Commander-Stil (funktioniert auch mit whiptail)
|
||||
# ---------------------------------------------------------------------
|
||||
ui_pick_file() { # startdir [glob] -> Dateipfad auf stdout
|
||||
local dir="${1:-$PWD}" glob="${2:-*}" sel
|
||||
dir="$(cd "$dir" 2>/dev/null && pwd || echo "$PWD")"
|
||||
while :; do
|
||||
local -a items=()
|
||||
items+=(".." "<Verzeichnis hoeher>")
|
||||
local d f
|
||||
while IFS= read -r d; do
|
||||
[ -n "$d" ] && items+=("$d/" "<DIR>")
|
||||
done < <(find "$dir" -mindepth 1 -maxdepth 1 -type d -printf '%f\n' 2>/dev/null | sort)
|
||||
while IFS= read -r f; do
|
||||
[ -n "$f" ] || continue
|
||||
items+=("$f" "$(human_size "$(file_size "$dir/$f")")")
|
||||
done < <(find "$dir" -mindepth 1 -maxdepth 1 -type f -name "$glob" -printf '%f\n' 2>/dev/null | sort)
|
||||
|
||||
sel="$(ui_menu "Datei waehlen" "Verzeichnis: $dir\nFilter: $glob" "${items[@]}")" || return 1
|
||||
case "$sel" in
|
||||
"..") dir="$(dirname "$dir")" ;;
|
||||
*/) dir="$dir/${sel%/}" ;;
|
||||
*) printf '%s\n' "$dir/$sel"; return 0 ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
Executable
+378
@@ -0,0 +1,378 @@
|
||||
#!/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
|
||||
Reference in New Issue
Block a user