SAI, HA y apagado nativo — 19 octubre 2026¶
Estado aplicado y frontera de activación¶
Revisados ambos hosts. El SHUTDOWNCMD no falta en pmx51: ambos tienen el
helper ejecutable y el mismo SHA256 <REDACTED>.
Es el código antiguo que para CT/VM secuencialmente antes del host. Su despliegue
Ansible solo estaba declarado en el primario; la futura declaración opt-in cubre
ambos nodos. No se ha cambiado el helper activo ni NUT, ni disparado un apagado,
FSD, corte, migración, reinicio o acción Meross.
Se han instalado bibliotecas inactivas y un validador de solo lectura en ambos nodos, sin cron ni daemon nuevo:
/usr/local/sbin/homelab-ups-check
/usr/local/lib/homelab-ups/coordinator.py
/usr/local/lib/homelab-ups/ha-policy.pl
El validador termina rc1 en ambos, con barreras explícitas:
- Guarda Meross sin acreditar: no están expuestos
write_config_safe,call_servicenihab_runen el perfil de herramientas actual. - Apagado nativo completo en <=150 s y reserva de batería suficiente sin acreditar. No se ha reemplazado este requisito por un kill de LRM.
La fuente queda protegida por ups_coordinator_enabled: false. Se preparó el
bloque de activación y recuperación Ansible, pero no se ha habilitado. Ejecutar
la etiqueta/role sin cambiar ese opt-in solo mantiene bibliotecas/validador:
no sustituye SHUTDOWNCMD ni cambia políticas HA. Cuando se active, el role de
observabilidad dejará de sobrescribir el nuevo helper con la copia antigua.
La política viva continúa shutdown_policy=conditional, sin journal de
transacción UPS. El validador confirmó OL, quorum, ambos nodos online y probes
SSH de boot-id en ambos sentidos. No se añadieron flags de mantenimiento.
Por qué el plazo y Meross bloquean el cierre¶
Código instalado contrastado: PVE/HA/LRM.pm::shutdown_request transforma un
poweroff con política freeze en parada y congelación de sus recursos. No cambia
el desired state persistente. conditional congela al reiniciar, pero permite
recuperación en otro nodo al apagar. El valor freeze afecta apagados ordenados;
no equivale a desarmar HA globalmente ni suprime recuperación ante un crash.
PVE/HA/Env/PVE2.pm::get_max_workers usa max_workers del datacenter o 4.
La gracia HA por recurso es 60 s por defecto. Con 14 recursos puede requerir
cuatro tandas, hasta 240 s de gracia, además de coordinación. pve-guests usa
stopall con gracia predeterminada 180 s por invitado, órdenes de parada y
concurrencia nativas. Ambas units tienen TimeoutStopSec=infinity. Esto impide
prometer un máximo total de 150 s con la configuración actual. El valor leído
battery.runtime=6203 en OL no acredita autonomía restante al llegar LOWBATT.
No se han cambiado los timeouts ni matado recursos para aparentar ese plazo.
El paquete Meross de HA aún puede ciclar el enchufe por pérdida de conectividad.
La regla leída en ha-addon:/config/AGENTS.md, líneas 160–164, exige:
Automation edits are complete only after the approved safe write, approved
automation.reloadviacall_service, and read-only load verification.
Y añade: «never bypass the profile via shell». La autorización del usuario ya existe; la limitación es el canal disponible. No se han extraído tokens, usado shell como sustituto ni modificado el paquete HA por otro camino. La propuesta de guarda Meross sigue siendo conceptual: no hay un parche de paquete HA entregado ni instalado en esta actuación. Debe diseñarse y validarse por el canal permitido antes de acreditar el paso 1. Una consulta de energía que dependa del propio pmx50 puede fallar cuando ese host está caído; no se propone un gate unknown que anule indiscriminadamente su recuperación. Hace falta una señal independiente o distinguir corte eléctrico acreditado de pérdida del primario, conservando la recuperación deliberada existente.
Secuencia concreta preparada, antes de activar¶
- Aplicar la guarda Meross mediante las herramientas HA permitidas, recargar y verificar su carga/estado. Debe bloquear el power-cycle por batería/flag de parada/mantenimiento y fallar de forma segura ante energía desconocida.
- Determinar una reserva real y un presupuesto de parada nativa: medir durante mantenimiento autorizado, ajustar disparo temprano NUT si procede y comprobar órdenes/gracias/concurrencia. No modificar desired=started a stopped. No imponer un kill de LRM para satisfacer un número.
- Acreditar la integración con
/etc/homelab-ups-ready.jsonen ambos nodos:meross_guard_verified=true,ha_package_sha256,native_shutdown_budget_seconds(positivo, <=150),battery_reserve_seconds(al menos presupuesto+60),valid_untilISO8601. No se ha creado un recibo ficticio para satisfacer el validador. - Con OL/quorum/ambos nodos/probes correctos, habilitar el opt-in y aplicar power_safety a ambos. El preflight rc1 bloquea reemplazar SHUTDOWNCMD si falta cualquier evidencia. El destino NUT existente se sustituye únicamente en ese momento; su backup se conserva. No requiere reiniciar NUT para cambiar un ejecutable referenciado por ruta.
- En un evento real: lock local; guardar propiedad del flag y estado previo del
timer; marcar
/run/ups-shutdown-in-progressy flag persistente propio; deteneransible-pull.timeren runtime sin disable. Escribir journal previo a cambiar la política bajo el mismo lock clusterdatacenter.cfgque usa la API de Proxmox. Poner freeze conservando el resto de opciones. Disparar/sbin/shutdown -h now; LRM/pve-guests conservan la parada nativa. - Si no hay quorum o no se puede escribir freeze, no bloquear la parada local por esperar al cluster: dejar diagnóstico/flags y solicitar igualmente la parada nativa. En ese caso no se promete freeze cluster. Si falla solo el probe peer, con quorum todavía se configura freeze, pero el boot-id desconocido bloquea restauración automática posterior.
- Tras volver la alimentación, la tarea de recuperación del mismo Ansible existente exige OL sin OB/LB/FSD/OFF/ALARM, quorum, ambos nodos online, ningún shutdown activo y boot-id de ambos distinto al anterior. Restaurar solamente la propiedad HA previa si aún coincide con la escrita por el coordinador; conservar cambios ajenos. No forzar conditional si antes era otra política. Retirar solo el flag creado por este coordinador, conservando flags manuales. Reiniciar el timer solo si antes estaba activo. Una recuperación interrumpida se puede repetir; el journal permanece para auditoría. Un peer sin ciclo de arranque acreditado deja recuperación pendiente, no se adivina.
Que los flags impidan autoencendido mientras falta un nodo es deliberado; recuperar el primer arranque físico/BIOS/Meross exige la guarda completa del paso 1. No se ha alterado el template autowake ya corregido por otro agente.
Pruebas y límites¶
22 fixtures correctas sin acceder al host/UPS para simular cortes: barreras de
activación, ausencia de quorum, OB, boot idéntico, flag manual, duplicado NUT,
fallback local, ausencia de comandos pct/qm/ha-stop, doble freeze, restauración
con cambios ajenos, conflicto de política, propiedad HA originalmente ausente.
Incluyen errores de escritura STATE/MARKER, fallo del timer y estado local
corrupto: en todos se intenta la parada nativa desde finally. Si no pudo
guardarse la propiedad de un flag, no se crea el flag persistente. La prueba
intercalada de dos nodos conserva un comprobante común phase=restored con los
boot-id anteriores; el primer nodo no elimina ese journal, de modo que el
segundo verifica las mismas guardas y limpia su propio OFF/STATE. Otra prueba
ejercita el motor Perl real simulado y confirma que el comprobante terminado
permanece. Repetir recovery sin trabajo no informa changed en Ansible.
Motor Perl probado con módulos PVE simulados; sintaxis Perl comprobada además
con módulos reales en ambos hosts. Python/YAML válidos. ha-policy.pl status
real es solo lectura y devolvió conditional/journal null en ambos. No se ha
ensayado apagado ni recuperación con corte real, ni certificado el presupuesto
150 s o la alimentación de los NAS.
Fuentes: Coordinador, motor transaccional, fixtures. Documentación primaria NUT SHUTDOWNCMD, FINALDELAY y HOSTSYNC. Semántica HA contrastada con el código instalado de Proxmox, no extrapolada de una versión distinta. El runbook de mantenimiento previo conserva el detalle de las guardas ya activadas, que este trabajo no sobrescribe.