HEX
Server: nginx/1.29.3
System: Linux 11979.bigscoots-wpo.com 6.8.0-88-generic #89-Ubuntu SMP PREEMPT_DYNAMIC Sat Oct 11 01:02:46 UTC 2025 x86_64
User: nginx (1068)
PHP: 7.4.33
Disabled: exec,system,passthru,shell_exec,proc_open,proc_close,popen,show_source,cmd# Do not modify this line # 1684243876
Upload Files
File: //bigscoots/lxd/migrate.sh
#!/bin/bash
# LXD Container/VM Migration Script
# Usage: bash migrate.sh <container> <source_node> <source_ip> <target_node> <target_ip>
# Example: bash migrate.sh wpo-39566 vps216 154.18.240.50 wpo694 154.18.240.26

set -e

# ============================================================
# VARIABLES
# ============================================================
CONTAINER=$1
SOURCE_NODE=$2
SOURCE_IP=$3
TARGET_NODE=$4
TARGET_IP=$5
MIGRATION_ID=$(uuidgen)
MIGRATION_START=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
ABANDONED_NAME="${CONTAINER}-migrated-${MIGRATION_ID:0:8}"
CLEANUP_AFTER=$(date -u -d "+30 days" +"%Y-%m-%dT%H:%M:%SZ")

echo "============================================================"
echo " LXD Container Migration"
echo "============================================================"
echo " Migration ID : $MIGRATION_ID"
echo " Container    : $CONTAINER"
echo " Source       : $SOURCE_NODE ($SOURCE_IP)"
echo " Destination  : $TARGET_NODE ($TARGET_IP)"
echo "============================================================"

# ============================================================
# PHASE 1: SETUP - Add destination as remote
# ============================================================
echo ""
echo "[PHASE 1] Setting up remote connection to $TARGET_NODE..."

# Remove stale remote if exists from previous failed migration
if lxc remote list | grep -q "^| $TARGET_NODE "; then
    echo "[WARN] Stale remote $TARGET_NODE found, removing..."
    lxc remote remove $TARGET_NODE
fi

TOKEN=$(ssh -p 2222 root@$TARGET_IP "lxc config trust add --name migration-${MIGRATION_ID:0:8} --quiet")
lxc remote add $TARGET_NODE "$TOKEN"
echo "[OK] Remote $TARGET_NODE added successfully"

# ============================================================
# PHASE 2: INITIAL SNAPSHOT + BULK COPY
# ============================================================
echo ""
echo "[PHASE 2] Taking initial snapshot and starting bulk copy..."
echo "[INFO] Instance will remain online during this phase"

SYNC1_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
lxc snapshot $CONTAINER sync1
echo "[OK] Snapshot sync1 taken at $SYNC1_TIME"

echo "[INFO] Starting bulk copy to $TARGET_NODE (this may take a while)..."
lxc copy "$CONTAINER" "$TARGET_NODE:${CONTAINER}-mig" --stateless --target="$TARGET_NODE"
echo "[OK] Bulk copy complete"

# ============================================================
# PHASE 3: INCREMENTAL SYNC 1 (instance still online)
# ============================================================
echo ""
echo "[PHASE 3] Running incremental sync 1 of 2..."

SYNC2_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
lxc snapshot $CONTAINER sync2
echo "[OK] Snapshot sync2 taken at $SYNC2_TIME"

lxc copy "$CONTAINER" "$TARGET_NODE:${CONTAINER}-mig" --stateless --refresh --target="$TARGET_NODE"
echo "[OK] Incremental sync 1 complete"

lxc delete "$CONTAINER/sync1"
lxc delete "${CONTAINER}-mig/sync1"
SYNC1_DELETED=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
echo "[OK] Snapshot sync1 deleted from both sides"

# ============================================================
# PHASE 4: INCREMENTAL SYNC 2 (instance still online)
# ============================================================
echo ""
echo "[PHASE 4] Running incremental sync 2 of 2..."

SYNC3_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
lxc snapshot $CONTAINER sync3
echo "[OK] Snapshot sync3 taken at $SYNC3_TIME"

lxc copy "$CONTAINER" "$TARGET_NODE:${CONTAINER}-mig" --stateless --refresh --target="$TARGET_NODE"
echo "[OK] Incremental sync 2 complete"

lxc delete "$CONTAINER/sync2"
lxc delete "${CONTAINER}-mig/sync2"
SYNC2_DELETED=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
echo "[OK] Snapshot sync2 deleted from both sides"

# ============================================================
# PHASE 5: CUTOVER
# ============================================================
echo ""
echo "[PHASE 5] Starting cutover..."
DOWNTIME_START=$(date -u +"%Y-%m-%dT%H:%M:%SZ")

lxc stop $CONTAINER
echo "[OK] Instance stopped - DOWNTIME STARTED"

SYNCFINAL_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
lxc snapshot $CONTAINER syncfinal
lxc copy "$CONTAINER" "$TARGET_NODE:${CONTAINER}-mig" --stateless --refresh --target="$TARGET_NODE"
echo "[OK] Final incremental stream sent"

# ============================================================
# PHASE 6: RENAME
# ============================================================
echo ""
echo "[PHASE 6] Renaming instances..."

lxc rename $CONTAINER $ABANDONED_NAME
echo "[OK] Source renamed to $ABANDONED_NAME"

lxc rename "${CONTAINER}-mig" $CONTAINER
echo "[OK] Destination renamed to $CONTAINER"

# ============================================================
# PHASE 7: START + HEALTH CHECK
# ============================================================
echo ""
echo "[PHASE 7] Starting instance and running health check..."

lxc start $CONTAINER
echo "[OK] Instance started on $TARGET_NODE"

sleep 5
HEALTH=$(lxc exec $CONTAINER -- systemctl is-system-running 2>/dev/null || echo "agent-unavailable")
echo "[OK] Health check: $HEALTH"

DOWNTIME_END=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
DOWNTIME_SECONDS=$(( $(date -d "$DOWNTIME_END" +%s) - $(date -d "$DOWNTIME_START" +%s) ))
echo "[OK] DOWNTIME ENDED - Total downtime: ${DOWNTIME_SECONDS} seconds"

# ============================================================
# PHASE 8: CLEANUP SNAPSHOTS
# ============================================================
echo ""
echo "[PHASE 8] Cleaning up snapshots..."

lxc delete "$ABANDONED_NAME/sync3"
lxc delete "$ABANDONED_NAME/syncfinal"
lxc delete "$CONTAINER/sync3"
lxc delete "$CONTAINER/syncfinal"
SNAPSHOTS_DELETED=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
echo "[OK] All snapshots deleted"

# ============================================================
# PHASE 9: REMOVE REMOTE
# ============================================================
echo ""
echo "[PHASE 9] Removing LXD remote..."

lxc remote remove $TARGET_NODE
echo "[OK] Remote $TARGET_NODE removed"

MIGRATION_END=$(date -u +"%Y-%m-%dT%H:%M:%SZ")

# ============================================================
# MIGRATION SUMMARY JSON
# ============================================================
echo ""
echo "============================================================"
echo " MIGRATION COMPLETE"
echo "============================================================"
cat <<EOF
{
  "migration_id": "$MIGRATION_ID",
  "status": "completed_pending_cleanup",
  "created_at": "$MIGRATION_START",
  "completed_at": "$MIGRATION_END",
  "cleanup_after": "$CLEANUP_AFTER",
  "source": {
    "node": "$SOURCE_NODE",
    "ip": "$SOURCE_IP",
    "container_original_name": "$CONTAINER",
    "container_abandoned_name": "$ABANDONED_NAME",
    "status": "abandoned_pending_cleanup"
  },
  "destination": {
    "node": "$TARGET_NODE",
    "ip": "$TARGET_IP",
    "container_name": "$CONTAINER",
    "status": "live"
  },
  "snapshots": [
    {
      "name": "sync1",
      "taken_at": "$SYNC1_TIME",
      "type": "initial",
      "deleted_at": "$SYNC1_DELETED"
    },
    {
      "name": "sync2",
      "taken_at": "$SYNC2_TIME",
      "type": "incremental",
      "deleted_at": "$SYNC2_DELETED"
    },
    {
      "name": "sync3",
      "taken_at": "$SYNC3_TIME",
      "type": "incremental",
      "deleted_at": "$SNAPSHOTS_DELETED"
    },
    {
      "name": "syncfinal",
      "taken_at": "$SYNCFINAL_TIME",
      "type": "final",
      "deleted_at": "$SNAPSHOTS_DELETED"
    }
  ],
  "downtime": {
    "started_at": "$DOWNTIME_START",
    "ended_at": "$DOWNTIME_END",
    "duration_seconds": $DOWNTIME_SECONDS
  },
  "cleanup": {
    "status": "pending",
    "triggered_at": null,
    "completed_at": null,
    "steps": [
      {
        "action": "delete_abandoned_container",
        "target": "$ABANDONED_NAME",
        "node": "$SOURCE_NODE",
        "status": "pending"
      }
    ]
  }
}
EOF