Whitepaper técnico — PodHeitor Nutanix AHV para Bacula

Backup VM-level de Nutanix AHV via Prism v3+v4 com Changed Regions Tracking nativo, replicação vendor-neutral PHCBT01, cross-hypervisor inbound a partir de Proxmox/vSphere/Hyper-V e disk-only / alternate-cluster restore — todos ausentes do plugin AHV do Bacula Enterprise 18.2.3.

Documento técnico complementar à página do plugin PodHeitor Nutanix.

1. Lacunas do plugin Nutanix do Bacula Enterprise

O Bacula Enterprise 18.2.3 ships um plugin Nutanix AHV — em JVM, baseado em Prism v2/v3, sem CRT v4, sem cross-restore, sem replicação vendor-neutral, sem disk-only restore, sem alternate-cluster restore. Para clientes que rodam pc.2024.3+ e querem DR multi-vendor, isso deixa quatro lacunas operacionais grandes:

  1. Sem CRT v4. A API compute-changed-regions em pc.2024.3+ é mais rápida e granular que o legado v3 changed_regions. O BEE não a consome.
  2. Sem cross-restore. Backup AHV restaura só em AHV. Sair do Nutanix exige V2V manual.
  3. Replicação acoplada ao Nutanix Protection Policies. Não funciona cross-vendor.
  4. Latência JVM. Pausas GC durante streaming de disco grande são mensuráveis.

O PodHeitor Nutanix Plugin é um sibling Rust dos plugins podheitor-proxmox, podheitor-vsphere e podheitor-hyperv — reusando seus formatos on-wire byte-a-byte para que restores sejam totalmente cross-compatíveis.

2. Modelo arquitetural — two-process Rust

O plugin segue o padrão PodHeitor: cdylib (na verdade um shim C++ de ~120 LOC, constants-only, linkado contra a metaplugin framework) + backend Rust standalone, comunicando-se via PTCOMM length-tagged framing em stdin/stdout.

┌──────────────────────────────────────────────────────────────────────────┐
│                      Bacula File Daemon (bacula-fd)                       │
│  ┌──────────────────────────────────────────────────────────────────┐    │
│  │  podheitor-nutanix-fd.so  (metaplugin C++ shim, ~120 LOC)        │    │
│  │  - PLUGINNAMESPACE="@nutanix"                                    │    │
│  └──────────────────────────────────────────────────────────────────┘    │
│                              │ PTCOMM over pipe (stdin/stdout)            │
└──────────────────────────────┼────────────────────────────────────────────┘
                               ▼
┌──────────────────────────────────────────────────────────────────────────┐
│              podheitor-nutanix-backend  (Rust binary)                     │
│  ┌──────────────┬──────────────┬──────────────┬──────────────────────┐   │
│  │ prism v3/v4  │ snapshot     │ iscsi        │ disk_reader          │   │
│  │ REST client  │ RAII guard   │ attach/detach│ (O_DIRECT /dev/sdX)  │   │
│  ├──────────────┼──────────────┼──────────────┼──────────────────────┤   │
│  │ crt (CBT)    │ backup.rs    │ restore.rs   │ replication.rs       │   │
│  └──────────────┴──────────────┴──────────────┴──────────────────────┘   │
└────┬─────────────────────────┬────────────────────────┬───────────────────┘
     │ HTTPS (9440)            │ iSCSI (DSIP:3260)      │ TLS (9848)
     ▼                         ▼                        ▼
┌──────────────┐       ┌──────────────────┐     ┌──────────────────┐
│ Prism Central│       │ Nutanix CVMs     │     │  DR Receiver     │
└──────────────┘       └──────────────────┘     └──────────────────┘

2.1 Dois deployment modes, um binário

Mode Onde roda o backend Quando escolher
proxy_mode=external FD host fora do cluster AHV Default — exige rota de rede para Prism:9440 + DSIP:3260 e open-iscsi no FD host
proxy_mode=in_cluster Linux VM dentro do cluster AHV Throughput máximo: data plane é virtual-NIC local para DSIP

3. Backup full — fluxo passo a passo

  1. PTCOMM handshake; recebe JobInfo + Plugin params.
  2. Cluster discovery: PC v4 com fallback v3, retorna PE IP + JWT 15-min (cookie NTNX_IGW_SESSION).
  3. POST /api/nutanix/v3/vms/{uuid}/snapshot (ou v4 equivalente). RAII SnapshotGuard garante delete on drop.
  4. Clone snapshot disks para um Volume Group temporário (Prism API).
  5. Attach VG ao proxy/FD via iSCSI: iscsiadm -m discovery + iscsiadm -m node --login.
  6. Enumera /dev/disk/by-path/... via sysfs scan; mapeia disk ↔ block device por LUN.
  7. Emite FNAME packets: @nutanix/<cluster>/<vm-uuid>/vm-metadata.json, @nutanix/.../disks/disk-<idx>-<id>.raw.
  8. Stream bytes em reads O_DIRECT 1 MiB / 4 KiB-aligned → D-packets.
  9. Logout iSCSI, delete VG, delete snapshot (a menos que seja a CBT reference).
  10. Persiste estado CBT: reference_recovery_point_ext_id (v4) ou snapshot_uuid (v3) em /var/lib/podheitor-nutanix/bitmap/<cluster>/<vm-uuid>.json.
  11. PTCOMM F (end-of-data), wait FD ack, T (terminate).

4. Backup incremental — CRT v4 com fallback v3

O passo 5 do fluxo full vira:

  1. Cria novo snapshot (current). Mantém o reference snapshot anterior.
  2. Para cada disk: chama compute-changed-regions (v4) ou /data/changed_regions (v3) com (reference, current).
  3. Pagina: até 10.000 regiões por response, segue nextOffset até exaurir.
  4. Emite extents modificados em formato PHCBT01 (magic + original_size + region_count + {offset, length, data} × N) sobre D-packets.
  5. Hybrid path: dense extents → iSCSI attach + read; sparse extents → REST range-GET. Heurística por densidade.
  6. Em sucesso: rotate. Delete old reference, promote current a new reference.

5. RAII guards — limpeza determinística

Snapshot Nutanix orfã = capacity leak no cluster. VG órfã = LUN target zumbi. iSCSI session aberta = device file zumbi no FD. O backend resolve via Rust Drop chain reverso:

Guard Recurso Drop action
SnapshotGuard Recovery point Prism Delete via API
VolumeGroupGuard VG temporário Delete via API
IscsiSessionGuard iSCSI session iscsiadm -m node --logout
CleanupGuard Catch-all Catches Drop panics; loga errors mas nunca propaga

Drop ordering é load-bearing: iSCSI logout → VG delete → snapshot delete. Reverse-declaration drop order do Rust dá isso de graça, mas inserir guards entre os existentes silenciosamente troca a ordem — convenção é encoded em comment no back_up_vm.

6. Cross-restore inbound — Proxmox/vSphere/Hyper-V → AHV

Detecção no scan de FNAME (primeiro file do job):

Prefixo FNAME Pipeline
@proxmox/<vmid>/disks/*.raw raw → qcow2 (qemu-img convert) → Image Service upload
@vsphere/<vm>/disks/*.vmdk vmdk → qcow2 → Image Service upload
@hyperv/<vm>/disks/*.vhdx vhdx → qcow2 → Image Service upload

VM config (.conf / .vmx / .vmcx XML) é traduzida para AHV vm_spec_v3:

Source field AHV target
Memory (MB) resources.memory_size_mib
vCPUs num_sockets × num_vcpus_per_socket
Firmware BIOS/UEFI resources.boot_config.boot_type
SCSI/IDE disks disk_list[].device_properties.device_type=DISK, adapter_type=SCSI
NIC MAC+VLAN nic_list[].mac_address + subnet_reference via network_map

7. Replicação vendor-neutral (PHCBT01 over TLS)

Diferentemente das Nutanix Protection Policies, que exigem Nutanix nas duas pontas, a replicação PodHeitor opera sobre PHCBT01-over-TLS na porta 9848 — mesmo formato dos plugins Proxmox/vSphere/Hyper-V. Receiver pode ser Nutanix, Proxmox, ou qualquer host com peer FD PodHeitor.

  • Seed: full inicial via mesmo caminho de backup, marcado como reference.
  • Bitmap-push: ciclos periódicos lêem CRT delta, enviam via TLS para receiver.
  • Failover modes: planned, unplanned, test, undo, permanent.

8. Disk-only e alternate-cluster restore

Dois modos absent do BEE AHV v18:

  • Disk-only restore: restaura apenas disk-N.raw para um device path arbitrário no FD host (sem upload ao Image Service, sem criação de VM). Caso de uso: forense, recuperação de arquivo específico via mount manual.
  • Alternate-cluster restore: parâmetro target_cluster redireciona o restore para PE diferente do origem. Combina com restore_vm_name= e network_map= para evitar IP collision.

9. Anti-patterns documentados

  • Não use prism_insecure=true em produção. Existe para Nutanix CE (cert self-signed default). Importe a CA do PC no trust store do FD em vez de bypass.
  • Não inverta a ordem de Drop dos guards. Delete de snapshot antes de logout iSCSI deixa device files zumbis no FD.
  • Não rode proxy_mode=external sem dsip= explícito. O fallback para cluster_name funciona em lab mas é frágil em produção (DNS, multi-DSIP).
  • Não rode replicação contra cluster com Protection Policies ativas no mesmo VM. Conflito de snapshots; fluxo atual não detecta automaticamente.

10. License posture

O plugin distribui sob LicenseRef-PodHeitor-Proprietary. O backend é binário Rust standalone — não vincula estaticamente nenhum source AGPLv3 do Bacula. O shim C++ é minimalista (~120 LOC, constants-only) e linka contra a Bacula metaplugin framework dynamically.

Pronto para avaliar?

Trial gratuito de 30 dias para clusters Nutanix AHV (Prism Central pc.2024.3+ recomendado, pc.2023.x suportado via fallback v3). Garantimos no mínimo 50% de desconto vs Bacula Enterprise, Veeam ou Commvault, com cross-restore e replicação vendor-neutral inclusos.

Heitor Faria — Fundador, PodHeitor International
[email protected]
☎ +1 (789) 726-1749 · +55 (61) 98268-4220 (WhatsApp)
🔗 Página do plugin PodHeitor Nutanix

Disponível em: pt-brPortuguêsenEnglish (Inglês)esEspañol (Espanhol)

Deixe um comentário