Whitepaper técnico — PodHeitor Nutanix AHV para Bacula

Backup VM-level de Nutanix AHV vía Prism v3+v4 con Changed Regions Tracking nativo, replicación vendor-neutral PHCBT01, cross-hypervisor inbound desde Proxmox/vSphere/Hyper-V y disk-only / alternate-cluster restore — todos ausentes del plugin AHV de Bacula Enterprise 18.2.3.

Documento técnico complementario a la página del plugin PodHeitor Nutanix.

1. Lagunas del plugin Nutanix de Bacula Enterprise

Bacula Enterprise 18.2.3 incluye un plugin Nutanix AHV — basado en JVM, Prism v2/v3, sin CRT v4, sin cross-restore, sin replicación vendor-neutral, sin disk-only restore, sin alternate-cluster restore. Para clientes que corren pc.2024.3+ y quieren DR multi-vendor, eso deja cuatro lagunas operacionales grandes:

  1. Sin CRT v4. La API compute-changed-regions en pc.2024.3+ es más rápida y granular que el legado v3 changed_regions. BEE no la consume.
  2. Sin cross-restore. Backup AHV restaura solo en AHV. Salir de Nutanix exige V2V manual.
  3. Replicación acoplada a Nutanix Protection Policies. No funciona cross-vendor.
  4. Latencia JVM. Pausas GC durante streaming de discos grandes son mensurables.

El PodHeitor Nutanix Plugin es un sibling Rust de los plugins podheitor-proxmox, podheitor-vsphere e podheitor-hyperv — reusando sus formatos on-wire byte-a-byte para que los restores sean totalmente cross-compatibles.

2. Modelo arquitectónico — two-process Rust

El plugin sigue el patrón PodHeitor: cdylib (en este caso un shim C++ de ~120 LOC, constants-only, linkeado contra el metaplugin framework) + backend Rust standalone, comunicándose vía PTCOMM length-tagged framing en 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 Dos modos de despliegue, un binario

Mode Dónde corre el backend Cuándo elegir
proxy_mode=external FD host fuera del cluster AHV Default — exige ruta de red a Prism:9440 + DSIP:3260 y open-iscsi en el FD host
proxy_mode=in_cluster Linux VM dentro del cluster AHV Throughput máximo: data plane es virtual-NIC local a DSIP

3. Backup full — flujo paso a paso

  1. PTCOMM handshake; recibe JobInfo + Plugin params.
  2. Cluster discovery: PC v4 con fallback v3, retorna PE IP + JWT 15-min (cookie NTNX_IGW_SESSION).
  3. POST /api/nutanix/v3/vms/{uuid}/snapshot (o v4 equivalente). RAII SnapshotGuard garantiza delete on drop.
  4. Clone snapshot disks a un Volume Group temporario (Prism API).
  5. Attach VG al proxy/FD vía iSCSI: iscsiadm -m discovery + iscsiadm -m node --login.
  6. Enumera /dev/disk/by-path/... vía sysfs scan; mapea 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 en reads O_DIRECT 1 MiB / 4 KiB-aligned → D-packets.
  9. Logout iSCSI, delete VG, delete snapshot (a menos que sea la CBT reference).
  10. Persiste estado CBT: reference_recovery_point_ext_id (v4) o snapshot_uuid (v3) en /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 con fallback v3

El paso 5 del flujo full se convierte en:

  1. Crea nuevo snapshot (current). Mantiene el reference snapshot anterior.
  2. Por disco: llama compute-changed-regions (v4) o /data/changed_regions (v3) con (reference, current).
  3. Pagina: hasta 10.000 regiones por response, sigue nextOffset hasta agotar.
  4. Emite extents modificados en 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 densidad.
  6. En éxito: rotate. Delete old reference, promote current a new reference.

5. RAII guards — limpieza determinística

Snapshot Nutanix huérfano = capacity leak en el cluster. VG huérfano = LUN target zombi. Sesión iSCSI abierta = device file zombi en el FD. El backend lo resuelve vía Rust Drop chain inverso:

Guard Recurso Drop action
SnapshotGuard Recovery point Prism Delete vía API
VolumeGroupGuard VG temporario Delete vía API
IscsiSessionGuard iSCSI session iscsiadm -m node --logout
CleanupGuard Catch-all Atrapa Drop panics; loga errors pero nunca propaga

Drop ordering es load-bearing: iSCSI logout → VG delete → snapshot delete. El reverse-declaration drop order de Rust da eso gratis, pero insertar guards entre los existentes silenciosamente cambia el orden — la convención está encoded en comment en back_up_vm.

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

Detección en el scan de FNAME (primer file del job):

Prefijo 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) es traducida a 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 vía network_map

7. Replicación vendor-neutral (PHCBT01 over TLS)

A diferencia de las Nutanix Protection Policies, que exigen Nutanix en ambas puntas, la replicación PodHeitor opera sobre PHCBT01-over-TLS en el puerto 9848 — mismo formato de los plugins Proxmox/vSphere/Hyper-V. El receiver puede ser Nutanix, Proxmox, o cualquier host con peer FD PodHeitor.

  • Seed: full inicial vía el mismo camino de backup, marcado como reference.
  • Bitmap-push: ciclos periódicos leen CRT delta, envían vía TLS al receiver.
  • Failover modes: planned, unplanned, test, undo, permanent.

8. Disk-only y alternate-cluster restore

Dos modos ausentes del BEE AHV v18:

  • Disk-only restore: restaura solo disk-N.raw a un device path arbitrario en el FD host (sin upload al Image Service, sin creación de VM). Caso de uso: forense, recuperación de archivo específico vía mount manual.
  • Alternate-cluster restore: el parámetro target_cluster redirecciona el restore a PE distinto del origen. Combina con restore_vm_name= y network_map= para evitar IP collision.

9. Anti-patterns documentados

  • No use prism_insecure=true en producción. Existe para Nutanix CE (cert self-signed default). Importe la CA del PC en el trust store del FD en vez de bypass.
  • No invierta el orden de Drop de los guards. Delete de snapshot antes de logout iSCSI deja device files zombis en el FD.
  • No corra proxy_mode=external sin dsip= explícito. El fallback a cluster_name funciona en lab pero es frágil en producción (DNS, multi-DSIP).
  • No corra replicación contra cluster con Protection Policies activas en la misma VM. Conflicto de snapshots; el flujo actual no detecta automáticamente.

10. License posture

El plugin se distribuye bajo LicenseRef-PodHeitor-Proprietary. El backend es binario Rust standalone — no vincula estáticamente ningún source AGPLv3 de Bacula. El shim C++ es minimalista (~120 LOC, constants-only) y linka contra la Bacula metaplugin framework dynamically.

¿Listo para evaluar?

Trial gratuito de 30 días para clusters Nutanix AHV (Prism Central pc.2024.3+ recomendado, pc.2023.x soportado vía fallback v3). Garantizamos al menos 50% de descuento vs Bacula Enterprise, Veeam o Commvault, con cross-restore y replicación vendor-neutral incluidos.

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

Disponível em: pt-brPortuguês (Portugués, Brasil)enEnglish (Inglés)esEspañol

Deja una respuesta