#!/bin/bash
# migra-proxmox-wci.sh <VMID> <NODE_IP> <NOME_DESTINO> [--desligar-origem]
#
# Migra uma VM do EVEO-Proxmox para a WCI (Wevy/CloudStack).
# Adaptado do run-pipeline.sh (que era origem VMware/ovftool): a etapa 1 passa a
# ser qemu-img direto do LV do Storage0, o resto do fluxo e o mesmo ja validado.
#
# Etapas:
#   1. converte cada disco do LVM para qcow2 direto no NFS (roda NO NO, via ssh)
#   2. virt-v2v no disco de boot (injeta virtio) — so para Windows
#   3. fix-boot-windows.sh: GPT->MBR + bootcode, porque a Wevy usa SeaBIOS
#   4. uploadVolume na Wevy a partir de https://transfer.cloudax.cloud
#
# NAO cria a VM nem liga nada: as VMs ja existem desligadas na WCI e os IPs
# sao aplicados por fora. Este script so produz e envia os volumes.
set -o pipefail
VMID="${1:?uso: $0 <vmid> <node_ip> <nome_destino> [--desligar-origem]}"
NODE="${2:?node_ip obrigatorio}"
DEST="${3:?nome_destino obrigatorio}"
DESLIGAR="${4:-}"

SENHA='In0v@t3ch'
BASE=/NFS01/migrate
SERVE="$BASE/$DEST"
LOG="$BASE/logs/$DEST-$(date +%Y%m%d-%H%M%S).log"
mkdir -p "$SERVE" "$BASE/logs"
# o NFS faz root_squash: o no escreve como nobody, entao a pasta precisa ser 777
chmod 777 "$SERVE" 2>/dev/null || echo "In0v@t3ch" | sudo -S chmod 777 "$SERVE"
exec > >(tee -a "$LOG") 2>&1
echo "=== $(date) MIGRA vmid=$VMID node=$NODE destino=$DEST ==="

no() { ssh -i ~/.ssh/id_rsa -o StrictHostKeyChecking=no -o BatchMode=yes root@"$NODE" "$@"; }

echo "--- config da VM na origem ---"
no "qm config $VMID" | tee /tmp/$DEST.qmconfig
ESTADO=$(no "qm status $VMID" | awk '{print $2}')
echo "estado atual: $ESTADO"

if [ "$DESLIGAR" = "--desligar-origem" ] && [ "$ESTADO" = "running" ]; then
  echo "--- desligando a origem para copia consistente ---"
  no "qm shutdown $VMID --timeout 300" || no "qm stop $VMID"
  for i in $(seq 1 60); do
    [ "$(no "qm status $VMID" | awk '{print $2}')" = "stopped" ] && break
    sleep 5
  done
  echo "estado apos desligar: $(no "qm status $VMID" | awk '{print $2}')"
fi

# discos, na ordem: o de boot primeiro
BOOTDEV=$(grep -oP '^boot:\s*order=\K[^;,]+' /tmp/$DEST.qmconfig | head -1)
# exclui cdrom/cloudinit olhando a LINHA inteira: o VOLID e truncado no primeiro
# ',' e perde o "media=cdrom", entao testar o VOLID nunca funcionou.
DISCOS=$(grep -P '^(scsi|virtio|sata|ide)[0-9]+:' /tmp/$DEST.qmconfig \
         | grep -v 'media=cdrom' | grep -v 'cloudinit' \
         | grep -oP '^(scsi|virtio|sata|ide)[0-9]+(?=:)' | sort -u)
# se o boot apontar para um device que nao existe, usa o primeiro disco real
if ! echo "$DISCOS" | grep -qx "$BOOTDEV"; then
  BOOTDEV=$(echo "$DISCOS" | grep -E '^sata' | head -1)
  [ -z "$BOOTDEV" ] && BOOTDEV=$(echo "$DISCOS" | grep -E '^scsi' | head -1)
  [ -z "$BOOTDEV" ] && BOOTDEV=$(echo "$DISCOS" | grep -E '^virtio' | head -1)
  [ -z "$BOOTDEV" ] && BOOTDEV=$(echo "$DISCOS" | head -1)
  echo "sem linha boot: no config — assumindo $BOOTDEV como disco de boot"
fi
ORDEM="$BOOTDEV $(echo "$DISCOS" | grep -vx "$BOOTDEV")"
# DISCOS_SO="sata0 virtio0" limita quais devices convertem. Serve para VMs com disco
# gigante e quase vazio (ex.: FILESERVER01, H: com 1020G de disco e 18G de dado),
# onde nao cabe no NFS e o conteudo nao justifica a imagem.
if [ -n "${DISCOS_SO:-}" ]; then
  FILTRADA=""
  for D in $ORDEM; do
    for P in $DISCOS_SO; do [ "$D" = "$P" ] && FILTRADA="$FILTRADA $D"; done
  done
  ORDEM="$FILTRADA"
  echo "filtro DISCOS_SO=$DISCOS_SO -> convertendo apenas:$ORDEM"
fi
echo "boot=$BOOTDEV | ordem=$ORDEM"

# Confere ANTES se cabe: somar o tamanho dos discos a converter e comparar com o livre.
# Um piso fixo nao serve — conversao de 300G passa num piso de 80G e morre no meio,
# enchendo o NFS e derrubando os outros workers junto.
PRECISA=0
for DEV in $ORDEM; do
  T=$(grep -oP "^$DEV:.*size=\K[0-9]+" /tmp/$DEST.qmconfig | head -1)
  U=$(grep -oP "^$DEV:.*size=[0-9]+\K[A-Z]" /tmp/$DEST.qmconfig | head -1)
  case "$U" in T) T=$((T*1024));; M) T=$((T/1024));; esac
  PRECISA=$((PRECISA + T))
done
LIVRE=$(df --output=avail -BG /NFS01 | tail -1 | tr -dc '0-9')
echo "espaco: precisa ate ${PRECISA}G (pior caso, sem esparso) e ha ${LIVRE}G livres"
if [ "$PRECISA" -gt "$LIVRE" ] && [ "${PRECHECK_ESTRITO:-1}" = "1" ]; then
  echo "ABORT: nao cabe. Use DISCOS_SO ou PRECHECK_ESTRITO=0 (vigia em tempo real)."
  exit 9
fi

N=0
for DEV in $ORDEM; do
  VOLID=$(grep -oP "^$DEV:\s*\K[^,]+" /tmp/$DEST.qmconfig)
  [ -z "$VOLID" ] && continue
  echo "$VOLID" | grep -q "media=cdrom" && { echo "  $DEV e cdrom, pulando"; continue; }
  CAMINHO=$(no "pvesm path $VOLID")
  SAIDA="$SERVE/$DEST-disk-$N.qcow2"
  echo "--- [$DEV] $VOLID -> $CAMINHO -> $SAIDA ---"
  if [ -s "$SAIDA" ] && qemu-img check -q "$SAIDA" >/dev/null 2>&1; then
    echo "  ja existe e esta integro, pulando conversao"
  else
    if [ -s "$SAIDA" ]; then
      echo "  ATENCAO: $SAIDA existe mas esta truncado/corrompido -- refazendo do zero"
      rm -f "$SAIDA"
    fi
    # em LVM compartilhado o LV so fica ativo no no que roda a VM; ao desligar,
    # ele e desativado e o qemu-img nao acha o device. Ativa antes de converter.
    if echo "$CAMINHO" | grep -q '^/dev/'; then
      no "lvchange -ay '$CAMINHO' 2>/dev/null; ls -l '$CAMINHO'" || {
        echo "ABORT: nao consegui ativar o LV $CAMINHO"; exit 3; }
    fi
    # converte no proprio no, escrevendo direto no NFS que ele ja tem montado
    no "qemu-img convert -p -O qcow2 '$CAMINHO' '/mnt/pve/NFS01/migrate/$DEST/$DEST-disk-$N.qcow2'" &
    CONV=$!
    ( PISO=${PISO_GB:-40}
      while kill -0 $CONV 2>/dev/null; do
        L=$(df --output=avail -BG /NFS01 | tail -1 | tr -dc '0-9')
        if [ "${L:-999}" -lt "$PISO" ]; then
          echo "VIGIA: livre ${L}G < piso ${PISO}G -- abortando para preservar o NFS"
          no "pkill -9 -f 'qemu-img convert.*$DEST-disk-$N.qcow2'" >/dev/null 2>&1
          kill -9 $CONV 2>/dev/null; break
        fi
        sleep 20
      done ) &
    VIG=$!
    wait $CONV; RCC=$?
    kill $VIG >/dev/null 2>&1
    if [ "$RCC" != "0" ]; then
      echo "ABORT: qemu-img falhou em $DEV (rc=$RCC) -- removendo o qcow2 parcial"
      rm -f "$SAIDA"
      exit 3
    fi
  fi
  ls -lh "$SAIDA"
  echo "$CAMINHO" | grep -q '^/dev/' && no "lvchange -an '$CAMINHO' 2>/dev/null" || true
  N=$((N+1))
done

BOOTQ="$SERVE/$DEST-disk-0.qcow2"
echo "--- o disco de boot e Windows? ---"
EH_WIN=$(echo "$SENHA" | sudo -S virt-filesystems --long --all -a "$BOOTQ" 2>/dev/null | grep -c ntfs || true)
echo "ntfs encontrados: $EH_WIN"
if [ "$EH_WIN" -gt 0 ] && [ "${V2V:-0}" = "1" ]; then
  echo "--- virt-v2v no disco de boot (injeta virtio) ---"
  V2V="$BASE/v2v-$DEST"
  mkdir -p "$V2V"
  if [ ! -s "$V2V/$DEST-boot-sda" ]; then
    echo "$SENHA" | sudo -S env VIRTIO_WIN=/tmp/virtio-win.iso \
      virt-v2v -v -x -i disk "$BOOTQ" -o local -of qcow2 -os "$V2V" -on "$DEST-boot" || {
        echo "ABORT: virt-v2v falhou"; exit 4; }
  fi
  echo "--- fix-boot: GPT->MBR + bootcode (SeaBIOS da Wevy) ---"
  echo "$SENHA" | sudo -S bash "$BASE/fix-boot-windows.sh" "$V2V/$DEST-boot-sda" || {
    echo "ABORT: fix-boot falhou"; exit 5; }
  echo "$SENHA" | sudo -S mv "$V2V/$DEST-boot-sda" "$BOOTQ"
  echo "$SENHA" | sudo -S chmod 644 "$BOOTQ"
else
  # Conversao pura ja funcionou em migracoes anteriores deste parque.
  # virt-v2v + fix-boot (que faz sgdisk --zap-all e reescreve a tabela como MBR)
  # destruiu a particao de boot do AD-01 e gerou 0xc000000e no winload.
  # So roda se V2V=1 for passado explicitamente.
  echo "sem virt-v2v e sem fix-boot (V2V=${V2V:-0}) — disco vai como convertido"
fi

echo "--- publicando e enviando para a Wevy ---"
echo "$SENHA" | sudo -S chmod 644 "$SERVE"/*.qcow2
source <(echo "$SENHA" | sudo -S cat /etc/wevy/sante-creds.env)
for F in "$SERVE"/*.qcow2; do
  VOL=$(basename "$F" .qcow2)
  URL="$TRANSFER_BASE/$DEST/$VOL.qcow2"
  # O smoke test tem de ser LOCAL. As policies 132 e 274 do FortiGate tem srcintf=wan1,
  # entao a URL publica so responde para quem vem da internet — e nao ha hairpin NAT.
  # Testar daqui pelo IP publico da sempre 000 e abortava a migracao por engano.
  RC=$(curl -sI -o /dev/null -w "%{http_code}" "http://localhost:9000/$DEST/$VOL.qcow2")
  echo "  smoke local http://localhost:9000/$DEST/$VOL.qcow2 -> $RC  (publica: $URL)"
  [ "$RC" != "200" ] && { echo "  ABORT: o apache-migrate nao esta servindo o arquivo"; exit 6; }
  curl -s -X POST "$WEVY_API_URL/restapi/volume/uploadVolume" \
    -H "apikey: $WEVY_API_KEY" -H "secretkey: $WEVY_SECRET_KEY" \
    -H "Content-Type: application/json" \
    -d "{\"name\":\"$VOL\",\"format\":\"QCOW2\",\"url\":\"$URL\",\"zoneUuid\":\"$WEVY_ZONE_UUID\"}"
  echo
done
echo "=== $(date) FIM $DEST — volumes enviados. Falta anexar na VM (que esta desligada) ==="
