Skip to content

Ensayos PBS: motor compartido y wrappers

Actualizado el 20 de octubre de 2026. La guía anterior describía un script de VMID 999 con destrucción incondicional. Ese procedimiento está retirado: no reservar ni borrar un invitado siguiendo aquellas instrucciones.

Fuentes y alcance

El motor mantenido es pbs_restore_drill.py, instalado como /usr/local/bin/pbs_restore_drill.py. El role observability_collector lo copia en los dos nodos Proxmox. Este role participa en el playbook normal; una publicación en Git puede propagarse mediante Ansible-pull sin que se lance un ensayo. Una revisión local no acredita por sí sola la versión instalada: comprobarla separadamente.

La observación pasiva de las 07:36:50 CEST del 20 de octubre encontró la versión con guarda de volúmenes f7b4f654… en ambos hosts, root:root y 0755, con Ansible-pull completado correctamente. Es anterior a la corrección de cancelación descrita a continuación; no implica un ensayo nuevo ni acredita que esa corrección posterior esté instalada.

A las 07:55:49 CEST, pmx50 ya tenía el helper corregido 7e2c3fda…, tras un Ansible-pull natural terminado07:55:48, success0. Pmx51 aún tenía f7b4f654…; su último pull terminó07:41:57, antes de publicarse la corrección. En esa lectura la convergencia del segundo nodo quedaba pendiente. La muestra posterior de 07:58:26,971655 CEST ya encontró 7e2c3fda… en pmx51, tras su pull natural07:55:59–07:57:15, success0. Ambos nodos convergieron al artefacto corregido, regular root:root0755. No se forzó un pull ni se inició un ensayo; la instalación no acredita una restauración con esa versión.

Los entrypoints conservados son:

  • /usr/local/bin/pbs-drill.sh: wrapper de pmx50 para perfiles lxc o vm, con log en /var/log/pbs-drill.log y notificación programada existente.
  • /usr/local/bin/pbs-restore-test.sh: wrapper del perfil config en el nodo secundario; conserva su log y ping de Healthchecks cuando termina bien.

Ejecutar un wrapper produce acciones y avisos. No es una comprobación pasiva. No se reproducen aquí secretos ni URLs privadas de los canales.

Qué comprueba el motor

  1. Reserva el ensayo mediante un directorio compartido en pmxcfs: /etc/pve/priv/homelab-pbs-drill-lock. Una reserva existente impide otro ensayo; no caduca ni debe borrarse a ciegas.
  2. Rechaza un VMID temporal ocupado. Usa 9901 para lxc/vm y 9902 para config; no destruye un invitado preexistente para hacer sitio.
  3. Selecciona una copia de un objetivo permitido: LXC 100/123/155/200/251, VM 171/208 o configuración de CT123. CT186 y CT217 quedan excluidos.
  4. Restaura en zfs-ha y valida el propietario de cada volumen: debe pertenecer exactamente al VMID temporal. Elimina red, mounts ajenos, hooks y dispositivos mediante una lista de configuración permitida. No se reutilizan discos de producción para el ensayo.
  5. Aplica una etiqueta de propiedad única y límites del invitado. Los perfiles LXC/VM arrancan para comprobar su estado; config no arranca el invitado.
  6. La limpieza exige propiedad acreditada. Ante propiedad o estado desconocido, no tratar el invitado como desechable ni sustituir la comprobación por un destroy manual.

Corrección en fuente del preflight LXC, 21 de octubre

Antes de pct restore, la fuente ahora rechaza todas las entradas raw lxc.* (incluidas las formas : y =), mpN, devN, hookscript y template, además de rootfs ambiguo, rutas de host, aliases de volumen y mapas idmap por mount. La guarda anterior sólo rechazaba lxc.idmap/lxc.include: eliminar los otros recursos después de restaurar llegaba tarde para proteger la fase de montaje. Los errores existentes de namespace se conservan.

El rootfs requiere un volumen PVE posicional único y tamaño positivo. Una llamada Perl fija al parser instalado PVE::LXC::Config->parse_volume('rootfs', ...) valida propiedades y unidades mediante el mismo schema que usa PVE; no asigna ni monta volúmenes. El motor exige tipo volume, tamaño finito positivo, volumen sin sustitución y ausencia de idmap. Un fallo del parser impide restaurar. No se mantiene una gramática alternativa de unidades/tipos en Python.

El comando LXC declara --onboot 0 --start 0 --ha-managed 0 desde el comienzo. La configuración suministrada prevalece sobre onboot: 1 del backup, evitando un arranque automático entre la restauración y el aislamiento posterior. netN puede estar en la fuente; no se usa unique ni se arranca hasta haber retirado la red y acreditado el aislamiento. Los perfiles, objetivos y calendarios se conservan; CT217 sigue excluido y no se incorpora ningún perfil de restore suyo.

La autoridad es el backend pve-container 6.1.14 leído de forma acotada el 21oct en pmx50, boot 558fbd3c…: LXC.pm importa raw LXC antes de crear/montar los discos; Create.pm::sanitize_and_merge_config conserva valores suministrados; Config.pm aporta el schema y parser rootfs. Capturas privadas: ~/.local/state/pbs-restore-backend-native-20261021-rs7rm_st/.

Fuente nativa SHA256
LXC.pm <REDACTED>
Create.pm <REDACTED>
Config.pm <REDACTED>

Validación de esta corrección: 67 tests offline PASS de toda la suite scripts/simplification/tests, con backend falso y procesos sintéticos. Los negativos atraviesan execute: recursos peligrosos no alcanzan restore/start, ni tocan el sentinel de producción; respuestas inválidas del parser no se aceptan. Las respuestas Perl de esa suite están simuladas: esos 67 tests prueban el control de flujo completo mediante backend falso, no el parser instalado. Revisión independiente de capacity sobre el commit 512cc879: PASS, con los 67 tests repetidos y contraste de las tres fuentes nativas.

Por separado, Root ejecutó 11/11 fixtures del parser nativo PASS a las 13:21:05 CEST del 21oct, en una única sesión de sólo lectura. Usó el mismo snippet Perl fijo, extraído del motor 512cc879, con valores sintéticos: unidades G/M/bytes, tamaño fraccionario y opciones válidas; cero, negativo, tamaño ausente, unidad inválida, bind, idmap y propiedad desconocida. Se comprobó la aceptación o rechazo de la proyección parseada; no se ejecutó execute, ningún restore, comando de almacenamiento, wrapper o job. Boot 558fbd3c… y los hashes/metadatos de las tres fuentes nativas permanecieron idénticos antes y después.

Recibo privado: ~/.local/state/pbs-native-parser-fixtures-20261021-kybmo8js/native-result.json, SHA256 <REDACTED>. Motor probado SHA256 <REDACTED>; snippet SHA256 <REDACTED>.

La PR101 se fusionó a las 13:23:25, tras CI Python3.10/3.12 y contratos/promtool correctos. El ciclo ordinario de pmx50 iniciado13:23:45 terminó con salida0; a las13:25:16 la copia instalada coincide con el SHA256 del motor probado arriba. En pmx51, el ciclo13:32:44 también termina con salida0 y el hash coincide a las13:34:15. Recibo adicional privado: ~/.local/state/pmx51-pbs-viewer-convergence-20261021-mqvq2rrf/completed-cycle.json, SHA256 <REDACTED>. Ambos boots permanecen estables; no se forzó pull ni se inició ningún ensayo. Recibo: ~/.local/state/pulse-pbs-ordinary-convergence-20261021-ol5zt5yn/receipt.json, SHA256 <REDACTED>. Esto no acredita recuperación CT217 ni consistencia de datos.

Cancelación y resultado incierto

La fuente incorpora cancelación cooperativa TERM/INT/HUP, límites por comando y de operación, y recibos locales privados bajo /var/lib/homelab-pbs-drill/. El recibo se escribe mediante sustitución atómica y fsync; antes de liberar la reserva sólo puede indicar release_pending, nunca éxito terminal. Una señal observada durante la liberación o la escritura final produce fallo, sin repetir la destrucción o la liberación.

Un restore fallido, cancelado o agotado puede haber dejado un worker PVE activo: terminar el proceso CLI no acredita la terminación de ese worker. En tal caso se conserva la reserva y no se etiqueta, arranca ni destruye el invitado incierto. La limpieza de un invitado aislado exige volver a comprobar nodo, VMID, etiqueta, configuración exacta, volúmenes y estado antes de detener y antes de destruir. Después se comprueba ausencia del invitado en API y configuración; esto no es una auditoría exhaustiva de volúmenes huérfanos en el almacenamiento.

Si una reserva queda retenida:

  1. No borrar el lock, reintentar el ensayo ni deducir un UPID por aproximación.
  2. Leer el recibo privado del intento en el nodo que lo ejecutó y contrastar nodo, VMID temporal, nonce, snapshot y fase con las tareas reales de PVE.
  3. Acreditar que cualquier worker asociado ha terminado y revisar configuración, volúmenes y propiedad actuales. Una etiqueta ausente o cambiada no autoriza destruir el invitado.
  4. Reconciliar manualmente recursos y reserva sólo con esa evidencia; conservar el recibo y registrar lo resuelto. Un recibo pendiente puede coexistir con una reserva ya liberada si falló la escritura final: no recrearla a ciegas.

Validación local y revisión independiente: 50 pruebas (34 de ciclo de vida y 16 de aislamiento/entorno), sin acceso a PVE. No se ha ejecutado un restore nuevo. SIGKILL, caída del host, procesos en D-state o daemon inaccesible quedan fuera de la garantía de limpieza; la incertidumbre exige revisión, no un éxito supuesto. Consultar el informe de recuperación.

Calendario declarado

La fuente ansible/roles/observability_collector/files/cron.d/pbs-drill declara:

Perfil Calendario en pmx50
LXC Día 1 de cada mes a las 05:05
VM Día 15 de enero, abril, julio y octubre a las 05:05

Es el calendario fuente, no una prueba de ejecución reciente ni del estado actual de cron. No cambiarlo ni iniciar un restore como parte de una auditoría.

Validación y operación

Las pruebas locales del motor usan fixtures y procesos de prueba; no contactan con Proxmox ni PBS:

python3 -m unittest discover -s scripts/simplification/tests -q

El ensayo real del 19 de octubre restauró CT123, comprobó el arranque aislado y verificó la retirada del invitado, volúmenes y reserva. Ese resultado corresponde a aquella versión y copia. No certifica CT217, el arranque de todas las aplicaciones, la consistencia de sus bases ni los originales que viven en NFS.

Un ensayo futuro requiere una ventana revisada y recursos disponibles. Mientras se investigan los resets de pmx50 y el throttling de pmx51, no iniciarlo desde esta guía. El estado coordinado y las condiciones específicas de recuperación de CT217 conservan las limitaciones vigentes.