# A01.P011 — Drones de Operaciones: Actualización Automatizada de OS Linux > **Estado**: 🟢 Fase 2 en progreso > **Pertenece a**: [A01 — Ecosistema ADN](./A01_dtic-ADN.md) > **Relacionado**: [A01.P009 — Evolución Dron ADN](./A01.P009_Evolucion-Dron-ADN.md) > **Fecha**: 2026-04-13 > **Responsable**: Lic. Ricardo MONLA > **Versión**: 3.0 --- ## 📈 Progreso ``` Fase 1: ██████████ 100% Parche de Emergencia (hotfix en bkps.rb) ✅ Fase 2: ████████░░ 80% Desacoplamiento — `dron ops` + candados ✅ Fase 2.5: ████████░░ 80% Estandarización de Fichas de Nodos 🔧 Fase 3: ░░░░░░░░░░ 0% Robustez — Pre-flight, Smart Lock, Reboot Check Fase 4: █████░░░░░ 50% Anillos de Despliegue (--ring habilitado) 🔧 Fase 5: ░░░░░░░░░░ 0% Observabilidad — Métricas por Nodo ``` --- ## 📋 Resumen Ejecutivo Este plan define la evolución de la capacidad de **Operaciones de Infraestructura** dentro del ecosistema ADN, comenzando por la actualización automatizada de sistemas operativos Linux (`apt update/upgrade`) mediante AtomicDrones. **Problema raíz:** La funcionalidad de actualizar nodos Linux fue injertada como un parche rápido dentro de la herramienta de backups (`bkps.rb` / `proc_linux.rb`), violando el principio de responsabilidad única y generando acoplamiento semántico incorrecto. Actualizar un OS **no es un backup**. **Visión:** Un nuevo subcomando `dron ops` (operaciones) que reutilice la infraestructura existente de ADN (`NodosInfo`, `dron/ejecutor.rb`, Bitácora Web) para orquestar tareas de mantenimiento de infraestructura de forma atómica, visible y desacoplada del sistema de backups. --- ## 🎯 Objetivo Crear un sistema de **Drones de Operaciones** dentro del ecosistema ADN que permita: 1. Actualizar sistemas operativos Linux de forma automatizada, desatendida y atómica (1 dron = 1 nodo). 2. Registrar cada operación individualmente en la Bitácora Web con trazabilidad completa. 3. Reutilizar las herramientas ADN existentes (`NodosInfo`, `dron/ejecutor.rb`, `Dron::Base`) sin duplicar código. 4. Desacoplar completamente esta funcionalidad del módulo de backups (`bkps.rb`). --- ## 🔍 Hallazgos y Diagnóstico ### Auditoría del Estado Actual (2026-04-12) | Hallazgo | Severidad | Detalle | |----------|:---------:|---------| | **Acoplamiento anómalo** | ✅ Resuelto | `proc_linux.rb` vivía en `bkps/lib/` — ahora existe `dron/ops.rb` independiente | | **Duplicación de lógica de nodos** | ✅ Resuelto | `ops.rb` reutiliza `NodosInfo.extraer_metadata()` | | **Orquestación vía bkps** | ✅ Resuelto | Comando nuevo: `dron ops update_os --target linux` | | **Bloqueo interactivo** | ✅ Resuelto | Corregido con `DEBIAN_FRONTEND=noninteractive` | | **Falta de candados** | 🟡 Media | Nodos con auth por password ahora usan `candados run` + `sshpass` | | **Fichas no estandarizadas** | 🔴 Alta | El parser de `NodosInfo` falla en ~40% de fichas por formato libre | ### Hallazgo Crítico: Fichas de Nodos No Estandarizadas (2026-04-13) > **Impacto:** El módulo `NodosInfo` detecta usuarios SSH incorrectos ("Requiere", "El", "Configurado", "ssh") porque las fichas `.md` de nodos no siguen un formato uniforme. Cada nodo fue documentado con estructura distinta. | Nodo | Campo SSH en ficha | Usuario detectado | Correcto | |------|-------------------|:-----------------:|:--------:| | srvv-dns | `**SSH**: ssh rmonla@10.0.10.2` | `ssh` → corregido a `rmonla` | ✅ (con fallback) | | srvv-docs | `**Acceso**: Requiere permisos docker` | `Requiere` → corregido a `root` | ⚠️ (fallback) | | srvv-sitio0 | `**Acceso**: El usuario rmonla...` | `El` → corregido a `root` | ⚠️ (fallback) | | srvv-nginx-rm | `\| **SSH** \| Puerto 7022, usuario root` | `ssh` → corregido a `root` | ⚠️ (fallback) | | srvv-nginx-rm | `\| **Bóveda** \| srvv-nginx-rm:root` | No detectado | ❌ falta formato | **Causa raíz:** Las fichas `.md` son prosa libre sin campos obligatorios estandarizados. El parser necesita ~15 regex heurísticas para cubrir variantes, y aun así falla. ### Código Duplicado Eliminado (Fase 2) | Función | Antes (proc_linux.rb) | Ahora (ops.rb) | |---------|:-------------------:|------------------------| | Detectar si es Linux | `es_nodo_linux?()` 40 líneas | `NodosInfo.detectar_so() == :linux` | | Extraer IP | `extraer_info_nodo()` regex | `NodosInfo.detectar_ip()` | | Extraer usuario SSH | regex custom | `NodosInfo.detectar_usuario()` + sanitización | | Auth por password | No soportado | `candados run` + `sshpass` via `clave_boveda` | ### Inventario de Flota Linux (13 nodos detectados) | Nodo | IP | Rol | Host | |------|-----|-----|------| | srv-dasu | 10.0.10.X | Servidor físico | — | | srv-pmox1 | 10.0.10.201 | Hipervisor Proxmox | — | | srv-pmox2 | 10.0.10.202 | Hipervisor Proxmox | — | | srv-pmox3 | 10.0.10.203 | Hipervisor Proxmox | — | | srv-xen1 | 10.0.10.X | Hipervisor XenServer (legacy) | — | | srvv-data | 10.0.10.X | VM — Datos | srv-pmoxN | | srvv-dns | 10.0.10.2 | VM — DNS CoreDNS | srv-pmox1 | | srvv-docs | 10.0.10.X | VM — Documentación | srv-pmoxN | | srvv-dtic | 10.0.10.X | VM — Servicios DTIC | srv-pmoxN | | srvv-koha | 10.0.10.X | VM — Biblioteca Koha | srv-pmoxN | | srvv-sitio0 | 10.0.10.X | VM — Sitio principal | srv-pmoxN | | srvv-sitio2 | 10.0.10.X | VM — Sitio secundario | srv-pmoxN | | srvv-uptime | 10.0.10.X | VM — Monitoreo | srv-pmoxN | --- ## 🗺️ Fases de Implementación ### Fase 1: Parche de Emergencia (Hotfix en bkps.rb) — ✅ COMPLETA **Objetivo:** Lograr que los drones de actualización Linux funcionen mínimamente, aunque acoplados al módulo de backups. | ID | Tarea | Estado | |:--:|-------|:------:| | F1.T1 | Sustituir `apt` por `apt-get` con `DEBIAN_FRONTEND=noninteractive` | ✅ | | F1.T2 | Agregar `--force-confdef --force-confold` a DPKG | ✅ | | F1.T3 | Registrar tipo `linux` en `TIPOS` de `bkps.rb` | ✅ | | F1.T4 | Corregir `ejecutar_uno()` para leer ficha `.md` del nodo individual | ✅ | | F1.T5 | Instalar gema `net-ssh` en entorno de usuario | ✅ | | F1.T6 | Corregir verbo en nota de Bitácora: `🐧 Actualizar` | ✅ | **Criterio de éxito:** ✅ `./adn/tools/run bkps orquestar update_linux_nodes --on-error continue` ejecuta 13 drones individuales visibles en Bitácora Web. ``` Fase 1: ██████████ 100% ✅ ``` --- ### Fase 2: Desacoplamiento — Nuevo CLI `dron ops` — 🔧 EN PROGRESO **Objetivo:** Extraer la lógica de operaciones de infraestructura fuera de `bkps.rb` y crear un subcomando propio dentro del sistema de Drones ADN. **Principio:** Reutilizar, no reimplementar. El 80% del código necesario ya existe en ADN Core. | ID | Tarea | Descripción | Estado | |:--:|-------|-------------|:------:| | F2.T1 | Crear `adn/tools/cli/dron/ops.rb` | Módulo de operaciones de infraestructura | ✅ | | F2.T2 | Integrar `NodosInfo` como scanner | Filtrado por OS, estado, validación de usuario SSH | ✅ | | F2.T3 | Conectar con `dron/ejecutor.rb` | Cada nodo = 1 dron ejecutor estándar (auto-bitácora) | ✅ | | F2.T4 | Registrar en `dispatcher.rb` | Case `'ops'` → `OpsComando.ejecutar(args)` + ayuda CLI | ✅ | | F2.T5 | Integrar `candados` para auth password | Nodos con `clave_boveda` usan `candados run` + `sshpass` | ✅ | | F2.T6 | Deprecar `proc_linux.rb` de bkps | Marcar como legacy, redirigir a nuevo módulo | ⏳ | | F2.T7 | Eliminar `update_linux_nodes` de `bkps.yml` | Limpieza de la configuración de backups | ⏳ | **Arquitectura implementada:** ``` ┌─────────────────────────────────────────────────────────────────┐ │ ./adn/tools/run dron ops update_os --target linux │ └───────────────────────────┬─────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ Dron::Ops (cli/dron/ops.rb) │ │ │ │ 1. Scanner: NodosInfo.extraer_metadata(nodo) para cada *.md │ │ → filtra por OS explícito (Debian/Ubuntu/Proxmox) │ │ → excluye Windows, firmware, cámaras │ │ → excluye nodos con estado 🔴 │ │ → sanitiza usuario SSH (lista negra + fallback user@IP) │ │ │ │ 2. Por cada nodo Linux: │ │ → Si tiene clave_boveda: │ │ candados run 'sshpass -p $PASS ssh ...' │ │ → Si no: │ │ ssh -i user@ip 'apt-get update && upgrade' │ │ → Dron::Ejecutor.lanzar(cmd, nota) → Bitácora Web │ └─────────────────────────────────────────────────────────────────┘ ``` **Uso actual (funcional):** ```bash # Listar nodos Linux con tipo de auth (dry-run) ./adn/tools/run dron ops update_os --target linux --dry-run # Ejecutar con continuación ante fallos ./adn/tools/run dron ops update_os --target linux --on-error continue # Ejecutar solo un nodo específico ./adn/tools/run dron ops update_os --nodo srvv-dns # Ayuda completa ./adn/tools/run dron ops --help ``` **Criterio de éxito:** ⚠️ Parcial. El comando funciona y los drones son visibles en Bitácora. Pendiente deprecar `proc_linux.rb` y limpiar `bkps.yml` una vez estandarizadas las fichas. ``` Fase 2: ████████░░ 80% ``` --- ### Fase 2.5: Estandarización de Fichas de Nodos — 🔧 EN PROGRESO **Objetivo:** Definir un formato canónico obligatorio para las fichas `nodos/*.md` que elimine la ambigüedad del parser `NodosInfo` y habilite operaciones confiables de toda herramienta ADN. | ID | Tarea | Descripción | Estado | |:--:|-------|-------------|:------:| | F2.5.T1 | Definir formato canónico | Tabla `` / `` con campos fijos | ✅ | | F2.5.T2 | Migrar fichas existentes | 32/32 fichas migradas (14 Linux + 2 otros + 16 Windows) | ✅ | | F2.5.T3 | Actualizar `NodosInfo` | Parser dual: tabla canónica primero, regex fallback | ✅ | | F2.5.T4 | Simplificar `ops.rb` | Eliminadas ~30 líneas de sanitización y regex de contenido | ✅ | | F2.5.T5 | Validador de fichas | `./adn/tools/run nodos validar` → reporta fichas no conformes | ⏳ | | F2.5.T6 | Actualizar `generar nodo` | Que la plantilla del generador use el formato canónico | ⏳ | **Formato canónico implementado:** ```markdown # Nodo: | Campo | Valor | |:------|:------| | **Hostname** | `` | | **IP** | `` | | **OS** | Debian 12 (Bookworm) | | **Estado** | 🟢 Online | | **Rol** | Descripción breve | | **SSH** | `user@ip:port` | | **Auth** | `key` / `password` / `rsa_legacy` | | **Bóveda** | `clave:candados` (solo si auth=password) | | **Host** | `host-anfitrión` (solo VMs) | | **VMID** | `123` (solo VMs) | | **Ring** | `test` / `standard` / `core` | ``` **Resultados:** - 32/32 fichas migradas con tabla canónica - `NodosInfo` lee tabla canónica en 1 regex (vs ~15 antes) - `ops.rb` simplificado: de ~65 líneas de scanner a ~20 - Nodos Linux detectados: 14 → 17 (se descubrieron `dtic-bitacoras`, `srv-ns8`, `srvv-sitio`) - `--ring test|standard|core` habilitado en `dron ops` (Fase 4 parcial) ``` Fase 2.5: ████████░░ 80% ``` --- ### Fase 3: Robustez — Pre-flight, Smart Lock, Reboot Check **Objetivo:** Hacer que cada dron de actualización sea inteligente: que verifique condiciones previas, detecte errores conocidos y reporte el estado post-actualización. | ID | Tarea | Descripción | Estado | |:--:|-------|-------------|:------:| | F3.T1 | Pre-flight: espacio en disco | Abortar si `/` o `/var` están al >95% | ⏳ | | F3.T2 | Pre-flight: estado del nodo | Saltar nodos con `**Estado**: 🔴` | ⏳ | | F3.T3 | Smart Lock detection | Detectar `Could not get lock /var/lib/dpkg/lock` y reportar `❌ dpkg bloqueado` | ⏳ | | F3.T4 | Reboot Required check | Post-upgrade: `[ -f /var/run/reboot-required ]` → `⚠️ Reboot needed` | ⏳ | | F3.T5 | Resumen de paquetes actualizados | Capturar cantidad de paquetes actualizados del output de `apt-get` | ⏳ | **Flujo enriquecido por nodo:** ``` 🐧 Dron update_os para srvv-dns ├── 🔍 Pre-flight checks │ ├── Disco /: 61% ✔ │ ├── Disco /var: OK ✔ │ └── Estado: 🟢 ✔ ├── [1/3] apt-get update ✔ ├── [2/3] apt-get upgrade -y ✔ (4 paquetes) ├── [3/3] apt-get autoremove -y ✔ ├── 🔍 Post-flight checks │ └── reboot-required: NO ✔ └── ✅ srvv-dns actualizado (47s) ``` **Criterio de éxito:** Cero drones con errores silenciosos. Cada falencia conocida tiene su propio código de diagnóstico en la nota de Bitácora. ``` Fase 3: ░░░░░░░░░░ 0% ``` --- ### Fase 4: Anillos de Despliegue (Rings) **Objetivo:** No actualizar toda la flota de golpe. Dividir los nodos en anillos de criticidad para evitar caídas simultáneas de servicios dependientes. | ID | Tarea | Descripción | Estado | |:--:|-------|-------------|:------:| | F4.T1 | Definir anillos en `nodos/*.md` | Nuevo campo `**Ring**: test|standard|core` | ⏳ | | F4.T2 | Ring 0 — Test | Nodos de bajo impacto: srvv-uptime, srvv-sitio2 | ⏳ | | F4.T3 | Ring 1 — Standard | VMs de servicios: srvv-dns, srvv-docs, srvv-koha | ⏳ | | F4.T4 | Ring 2 — Core | Hipervisores y data: srv-pmox1/2/3, srvv-data | ⏳ | | F4.T5 | Pausa entre anillos | Esperar confirmación o timeout entre rings | ⏳ | **Uso propuesto:** ```bash # Solo nodos de testing ./adn/tools/run dron ops update_os --ring test # Despliegue completo con pausa entre anillos ./adn/tools/run dron ops update_os --rings all --pause 300 ``` **Criterio de éxito:** Nunca se actualizan los 3 hipervisores Proxmox al mismo tiempo. Los nodos de servicio crítico se actualizan después de validar en el ring de test. ``` Fase 4: ░░░░░░░░░░ 0% ``` --- ### Fase 5: Observabilidad — Métricas por Nodo **Objetivo:** Historial de actualizaciones por nodo visible en la Bitácora Web. | ID | Tarea | Descripción | Estado | |:--:|-------|-------------|:------:| | F5.T1 | Registro de versión de OS | Capturar `lsb_release -d` pre y post-update | ⏳ | | F5.T2 | Historial de updates por nodo | Consulta: "¿Cuándo fue la última actualización de srvv-dns?" | ⏳ | | F5.T3 | Alerta de nodos atrasados | `dron ops status` → nodos sin update en >30 días | ⏳ | | F5.T4 | Dashboard de salud de flota OS | Vista consolidada en Bitácora Web | ⏳ | **Criterio de éxito:** Con un solo comando se puede ver el estado de actualización de toda la flota Linux, detectando nodos atrasados. ``` Fase 5: ░░░░░░░░░░ 0% ``` --- ## 📊 Conclusiones ### Lo que funciona hoy (Fases 1 + 2 + 2.5) - **17 nodos Linux** detectados correctamente (incluidos 3 Proxmox, 1 XenServer, srv-ns8). - **Desacoplamiento logrado:** `dron ops update_os` es subcomando propio del sistema de Drones ADN. - **Candados integrado:** `srvv-nginx-rm` y futuros nodos con password usan la bóveda. - **Fichas estandarizadas:** 32/32 con tabla `META:BEGIN` / `META:END`. - **Rings habilitados:** `--ring test|standard|core` filtra la flota. - **Dry-run informativo:** Muestra nodo, IP, usuario, auth type y ring. ### Desbloqueado por la estandarización - **Parser simplificado:** `NodosInfo` lee tabla canónica con 1 regex (antes ~15). - **Scanner limpio:** `ops.rb` pasó de ~65 líneas de workarounds a ~20 líneas claras. - **Nodos descubiertos:** 3 nodos Linux que antes no se detectaban (`dtic-bitacoras`, `srv-ns8`, `srvv-sitio`). - **Cero falsos positivos:** Cámaras IP, firmware y Windows correctamente excluidos. ### Métricas de referencia | Métrica | Antes | Ahora | |---------|:-----:|:-----:| | Nodos Linux detectados | 14 | **17** | | Nodos Windows (excluidos) | 15 | 14 | | Total fichas en `nodos/` | 32 | 32 | | Fichas con tabla canónica | 0 | **32** (100%) | | Fichas con parseo regex (fallback) | 32 | **0** | | Regex en NodosInfo (operativas) | ~15 | **1** (canónica) | | Nodos con auth `key` | 13 | **16** | | Nodos con auth `candados` | 0 | **1** (srvv-nginx-rm) | | Líneas de scanner en `ops.rb` | ~65 | **~20** | | Falsos positivos en dry-run | ~2 | **0** | --- ## 📎 Referencias - [A01.P009 — Evolución Dron ADN](./A01.P009_Evolucion-Dron-ADN.md) — Plan maestro de AtomicDrones - `adn/tools/cli/dron/ops.rb` — **Módulo nuevo de operaciones** (Fase 2, activo) - `adn/tools/cli/dron/dispatcher.rb` — Dispatcher con `ops` registrado - `adn/tools/cli/dron/ejecutor.rb` — Ejecutor atómico con auto-bitácora - `adn/tools/cli/nodos/info.rb` — Módulo `NodosInfo` (scanner de fichas) - `adn/tools/candados/candados.rb` — Bóveda de credenciales - `adn/tools/bkps/lib/proc_linux.rb` — Implementación legacy (Fase 1, a deprecar) --- *Plan actualizado 2026-04-13 — Versión 3.0*