Add backup/restore scripts and update web/admin UI
This commit is contained in:
Executable
+257
@@ -0,0 +1,257 @@
|
||||
#!/bin/bash
|
||||
# =============================================================================
|
||||
# Server Monitorizare v2 - Migration Restore Script
|
||||
# =============================================================================
|
||||
# Restores a backup archive produced by backup_system.sh onto a new server.
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/restore_backup.sh <backup.tar.gz> [--db-only] [--ansible-only]
|
||||
#
|
||||
# What it does:
|
||||
# 1. Extracts the archive to a temp staging area
|
||||
# 2. Restores the SQLite database (with a safety backup of any existing DB)
|
||||
# 3. Restores Ansible inventory, playbooks, ssh_keys, ansible.cfg
|
||||
# 4. Restores WMT releases and ansible_settings.json
|
||||
# 5. Fixes SSH key permissions (must be 600 for Ansible)
|
||||
# 6. Prints post-restore checklist
|
||||
#
|
||||
# For a fresh server install, run this AFTER:
|
||||
# pip install -r requirements.txt
|
||||
# =============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
APP_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
log() { echo "[$(date '+%H:%M:%S')] $*"; }
|
||||
ok() { echo " [OK] $*"; }
|
||||
warn() { echo " [WARN] $*"; }
|
||||
err() { echo " [ERROR] $*" >&2; }
|
||||
|
||||
usage() {
|
||||
echo "Usage: $0 <path/to/backup.tar.gz> [--db-only | --ansible-only]"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --db-only Restore only the SQLite database"
|
||||
echo " --ansible-only Restore only Ansible files (inventory, playbooks, ssh_keys)"
|
||||
echo ""
|
||||
echo "Example:"
|
||||
echo " $0 data/backups/smv2_backup_20260708_020000.tar.gz"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Parse args
|
||||
# ---------------------------------------------------------------------------
|
||||
ARCHIVE="${1:-}"
|
||||
MODE="all" # all | db-only | ansible-only
|
||||
YES=0 # set to 1 by --yes to skip interactive confirmation
|
||||
|
||||
[[ -n "$ARCHIVE" ]] || usage
|
||||
[[ -f "$ARCHIVE" ]] || { err "Archive not found: $ARCHIVE"; exit 1; }
|
||||
|
||||
for arg in "${@:2}"; do
|
||||
case "$arg" in
|
||||
--db-only) MODE="db-only" ;;
|
||||
--ansible-only) MODE="ansible-only" ;;
|
||||
--yes|-y) YES=1 ;;
|
||||
*) err "Unknown option: $arg"; usage ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Extract archive
|
||||
# ---------------------------------------------------------------------------
|
||||
log "Extracting archive: $ARCHIVE"
|
||||
STAGE_DIR=$(mktemp -d)
|
||||
trap 'rm -rf "$STAGE_DIR"' EXIT
|
||||
|
||||
tar -xzf "$ARCHIVE" -C "$STAGE_DIR"
|
||||
log "Extracted to staging: $STAGE_DIR"
|
||||
|
||||
# Show manifest if present
|
||||
if [[ -f "$STAGE_DIR/BACKUP_MANIFEST.txt" ]]; then
|
||||
echo ""
|
||||
echo "=== Backup Manifest ==="
|
||||
cat "$STAGE_DIR/BACKUP_MANIFEST.txt"
|
||||
echo "========================"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Confirm before proceeding
|
||||
# ---------------------------------------------------------------------------
|
||||
if [[ "$YES" == "1" ]]; then
|
||||
log "Non-interactive mode (--yes) — proceeding automatically."
|
||||
else
|
||||
read -r -p "Proceed with restore to $APP_DIR ? [y/N] " confirm
|
||||
[[ "${confirm,,}" == "y" ]] || { log "Aborted."; exit 0; }
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Restore: SQLite database
|
||||
# ---------------------------------------------------------------------------
|
||||
restore_db() {
|
||||
local src="$STAGE_DIR/enhanced_monitoring.db"
|
||||
local dst="$APP_DIR/data/enhanced_monitoring.db"
|
||||
|
||||
if [[ ! -f "$src" ]]; then
|
||||
warn "No database file in archive — skipping DB restore"
|
||||
return
|
||||
fi
|
||||
|
||||
mkdir -p "$APP_DIR/data"
|
||||
|
||||
# Safety backup of any existing database
|
||||
if [[ -f "$dst" ]]; then
|
||||
local safety_bak="${dst}.pre_restore_${TIMESTAMP}.bak"
|
||||
cp "$dst" "$safety_bak"
|
||||
ok "Existing DB saved as safety backup: $(basename "$safety_bak")"
|
||||
fi
|
||||
|
||||
cp "$src" "$dst"
|
||||
ok "Database restored: $dst ($(du -sh "$dst" | cut -f1))"
|
||||
|
||||
# Quick sanity check
|
||||
local dev_count; dev_count=$(sqlite3 "$dst" "SELECT COUNT(*) FROM devices;" 2>/dev/null || echo "?")
|
||||
ok "Devices in restored DB: $dev_count"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Restore: Ansible files
|
||||
# ---------------------------------------------------------------------------
|
||||
restore_ansible() {
|
||||
local src="$STAGE_DIR/ansible"
|
||||
|
||||
if [[ ! -d "$src" ]]; then
|
||||
warn "No ansible/ directory in archive — skipping"
|
||||
return
|
||||
fi
|
||||
|
||||
# Backup existing ansible dir if present
|
||||
if [[ -d "$APP_DIR/ansible" ]]; then
|
||||
local bak="$APP_DIR/ansible.pre_restore_${TIMESTAMP}.bak"
|
||||
mv "$APP_DIR/ansible" "$bak"
|
||||
ok "Existing ansible/ moved to: $(basename "$bak")"
|
||||
fi
|
||||
|
||||
cp -a "$src" "$APP_DIR/ansible"
|
||||
|
||||
# Critical: SSH private key must be 600 or Ansible rejects it
|
||||
if [[ -f "$APP_DIR/ansible/ssh_keys/app_key" ]]; then
|
||||
chmod 600 "$APP_DIR/ansible/ssh_keys/app_key"
|
||||
ok "SSH private key permissions set to 600"
|
||||
fi
|
||||
|
||||
ok "Ansible directory restored"
|
||||
ok "Playbooks: $(ls "$APP_DIR/ansible/playbooks/"*.yml 2>/dev/null | wc -l) files"
|
||||
ok "Inventory hosts: $(grep -c 'ansible_host:' "$APP_DIR/ansible/inventory/dynamic_inventory.yaml" 2>/dev/null || echo 0)"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Restore: WMT releases
|
||||
# ---------------------------------------------------------------------------
|
||||
restore_wmt_releases() {
|
||||
local src="$STAGE_DIR/wmt_releases"
|
||||
if [[ ! -d "$src" ]]; then return; fi
|
||||
|
||||
mkdir -p "$APP_DIR/data/wmt_releases"
|
||||
# Only copy files not already present (don't overwrite newer releases)
|
||||
for f in "$src"/*; do
|
||||
local fname; fname=$(basename "$f")
|
||||
if [[ ! -f "$APP_DIR/data/wmt_releases/$fname" ]]; then
|
||||
cp "$f" "$APP_DIR/data/wmt_releases/"
|
||||
ok "WMT release restored: $fname"
|
||||
else
|
||||
ok "WMT release already present (skipped): $fname"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Restore: ansible_settings.json
|
||||
# ---------------------------------------------------------------------------
|
||||
restore_settings() {
|
||||
local src="$STAGE_DIR/ansible_settings.json"
|
||||
if [[ ! -f "$src" ]]; then return; fi
|
||||
|
||||
local dst="$APP_DIR/data/ansible_settings.json"
|
||||
if [[ -f "$dst" ]]; then
|
||||
cp "$dst" "${dst}.pre_restore_${TIMESTAMP}.bak"
|
||||
fi
|
||||
cp "$src" "$dst"
|
||||
ok "ansible_settings.json restored"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Restore: config/
|
||||
# ---------------------------------------------------------------------------
|
||||
restore_config() {
|
||||
local src="$STAGE_DIR/config"
|
||||
if [[ ! -d "$src" ]]; then return; fi
|
||||
|
||||
if [[ -d "$APP_DIR/config" ]]; then
|
||||
ok "config/ already present — skipping (manual merge may be needed)"
|
||||
warn "Backup config is in: $STAGE_DIR/config/ (not auto-applied)"
|
||||
return
|
||||
fi
|
||||
cp -a "$src" "$APP_DIR/config"
|
||||
ok "config/ restored"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Dispatch
|
||||
# ---------------------------------------------------------------------------
|
||||
log "Restore mode: $MODE"
|
||||
|
||||
case "$MODE" in
|
||||
all)
|
||||
restore_db
|
||||
restore_ansible
|
||||
restore_wmt_releases
|
||||
restore_settings
|
||||
restore_config
|
||||
;;
|
||||
db-only)
|
||||
restore_db
|
||||
;;
|
||||
ansible-only)
|
||||
restore_ansible
|
||||
restore_settings
|
||||
;;
|
||||
esac
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Post-restore checklist
|
||||
# ---------------------------------------------------------------------------
|
||||
echo ""
|
||||
echo "================================================================"
|
||||
echo " Post-Restore Checklist"
|
||||
echo "================================================================"
|
||||
echo " 1. Install Python dependencies (if fresh server):"
|
||||
echo " cd $APP_DIR && pip install -r requirements.txt"
|
||||
echo ""
|
||||
echo " 2. Start the application:"
|
||||
echo " cd $APP_DIR && python main.py"
|
||||
echo ""
|
||||
echo " 3. Verify device list loads at: http://<new-server-ip>:5000"
|
||||
echo ""
|
||||
echo " 4. Test Ansible connectivity:"
|
||||
echo " cd $APP_DIR/ansible && ansible -i inventory/dynamic_inventory.yaml all -m ping --limit 1"
|
||||
echo ""
|
||||
echo " 5. If the new server IP has changed, update any WMT devices"
|
||||
echo " pointing to the old server URL (wmt_global_config.server_log_url)"
|
||||
echo " via the admin UI or:"
|
||||
echo " sqlite3 data/enhanced_monitoring.db \\"
|
||||
echo " \"UPDATE wmt_global_config SET server_log_url='http://<NEW_IP>:5000/api/log';\""
|
||||
echo ""
|
||||
echo " 6. Re-install cron backup on the new server:"
|
||||
echo " bash $SCRIPT_DIR/backup_system.sh install-cron"
|
||||
echo ""
|
||||
echo "================================================================"
|
||||
log "Restore completed."
|
||||
Reference in New Issue
Block a user