Feat(ADN): Implementado sistema de configuración persistente y subcomando 'predeterminar' para backups

This commit is contained in:
Ricardo Monla
2026-03-09 14:28:44 -03:00
parent 412a379c62
commit af5f193fab
3 changed files with 169 additions and 174 deletions
+45 -8
View File
@@ -3,13 +3,21 @@
require 'optparse'
require 'fileutils'
require 'date'
require_relative '../core/configurador'
module ADN
class SubcomandoBackup
def initialize(args, logger)
@args = args
@logger = logger
@opciones = { modo: 'stop', storage: 'local', compress: 'gzip' }
# Cargar valores predeterminados desde config.yml
defaults = Configurador.obtener('backup.defaults') || {}
@opciones = {
modo: defaults['modo'] || 'stop',
storage: defaults['storage'] || 'local',
compress: defaults['compress'] || 'gzip'
}
end
def ejecutar
@@ -19,9 +27,12 @@ module ADN
opts.separator "Ejecuta un backup remoto siguiendo el protocolo ADN"
opts.separator ""
opts.separator "Opciones:"
opts.on("--mode MODO", "Modo de backup (stop, snapshot, suspend. Default: stop)") do |m|
opts.on("--mode MODO", "Modo de backup (stop, snapshot, suspend. Default: #{@opciones[:modo]})") do |m|
@opciones[:modo] = m
end
opts.on("--storage STORAGE", "Nombre del storage en Proxmox (Default: #{@opciones[:storage]})") do |s|
@opciones[:storage] = s
end
opts.on("-h", "--help", "Mostrar esta ayuda") do
puts opts
exit
@@ -42,6 +53,11 @@ module ADN
exit 1
end
if @args[0] == 'predeterminar'
comando_predeterminar(@args[1..-1])
return
end
nodo = @args[0]
ejecutar_backup(nodo)
end
@@ -100,13 +116,11 @@ module ADN
end
contenido = File.read(ruta_nodo)
# Buscamos VMID (puede decir "VM ID: 100" o "vmid: 100")
vmid = contenido.match(/(?:VM ID|vmid):\s*(\d+)/i)&.[](1)
# Buscamos VMID (puede decir "VM ID: 100" o "vmid: 100" o "- **VMID**: `100`")
vmid = contenido.match(/(?:VM ID|vmid|VMID)\*\*?:?\s*`?(\d+)`?/i)&.[](1)
# Buscamos el host (especificamente el link o nombre entre backticks)
# Formato: - **Padre/Host**: `srv-dasu`
host_name = contenido.match(/Padre\/Host\*\*:?\s*`([^`]+)`/)&.[](1)
host_name ||= contenido.match(/Host\*\*:?\s*`([^`]+)`/)&.[](1)
host_name = contenido.match(/(?:Padre\/Host|Host Anfitrión|Anfitrión|Host)\*\*:?\s*`([^`]+)`/i)&.[](1)
unless vmid && host_name
@logger.error("Contexto incompleto en #{nombre_nodo}.md: VMID=#{vmid}, Host=#{host_name}")
@@ -179,7 +193,7 @@ module ADN
contenido = File.read(ruta)
# Actualizar la línea de tiempo
linea_nueva = "| 08:31 | #{hora} | #{desc} | [R] #{estado} |"
linea_nueva = "| #{hora} (Fin) | | #{desc} | [R] #{estado} |"
# Buscamos la tarea BKP-ADN
nuevo_contenido = contenido.gsub(/#### ⏳ - BKP-ADN/, "#### #{estado} - BKP-ADN")
@@ -187,5 +201,28 @@ module ADN
File.write(ruta, nuevo_contenido)
end
def comando_predeterminar(args)
if args.length < 2
puts "#{Color::YELLOW}Uso: adn backup predeterminar <parametro> <valor>#{Color::RESET}"
puts "Ej: adn backup predeterminar modo snapshot"
exit 1
end
param = args[0].downcase
valor = args[1].downcase
# Normalizar parámetro (ej: 'modo' -> 'modo', 'storage' -> 'storage')
param = 'modo' if param == 'mode'
unless ['modo', 'storage', 'compress'].include?(param)
@logger.error("Parámetro no predeterminable: #{param}")
exit 1
end
Configurador.establecer("backup.defaults.#{param}", valor)
@logger.exito("Valor predeterminado establecido: backup.#{param} = #{valor}")
puts "#{Color::GREEN}✓ Se ha predeterminado el #{param} a '#{valor}' para futuros backups.#{Color::RESET}"
end
end
end
+74 -166
View File
@@ -1,198 +1,106 @@
# config.yml — Configuración centralizada para el sistema ADN
# =============================================================
# Este archivo define la configuración para todas las herramientas
# del sistema ADN. Todas las rutas son relativas al directorio
# raíz del proyecto (dtic-srv-NS8).
#
# 📅 Última actualización: 2026-03-07 (Fase 1: Núcleo Autónomo)
# 📚 Documentación: docs/plan/260307-1400_Plan_Mejoras_ADN.md
# ─── Rutas del proyecto ─────────────────────────────────────
---
rutas:
# Directorio raíz del proyecto (se calcula automáticamente)
proyecto: "../.."
# Directorios de contenido
adn: "../../adn"
bitacoras: "../../bitacoras"
nodos: "../../nodos"
proyectos: "../../proyectos"
logs: "../../logs/adn"
tmp: "../../tmp"
# Herramientas existentes
ns8_candados: "../ns8-candados/ns8-candados.rb"
# ─── Configuración del validador ────────────────────────────
validador:
# Modos de operación
watch_mode: false # Monitorear cambios en tiempo real (Fase 1)
strict_mode: true # Rechazar errores graves inmediatamente
# Validaciones específicas
validar_formato_tiempo: true # Formato HH:MM (sin "hs")
validar_iconografia: true # Iconos de estado correctos
validar_metricas_hibridas: true # Presencia de [Físico/Remoto:]
validar_referencias_rollover: true # Hipervínculos bidireccionales
# Hooks Git
pre_commit_hook: true # Rechazar commits con errores (Fase 1)
pre_push_hook: false # Validación antes de push (Fase 2)
# Notificaciones
notificar_desktop: false # Notificaciones desktop (libnotify)
notificar_email: false # Notificaciones email (Fase 3)
# ─── Sistema de triggers ────────────────────────────────────
watch_mode: false
strict_mode: true
validar_formato_tiempo: true
validar_iconografia: true
validar_metricas_hibridas: true
validar_referencias_rollover: true
pre_commit_hook: true
pre_push_hook: false
notificar_desktop: false
notificar_email: false
triggers:
enabled: false # Habilitado en Fase 2
config_file: "triggers.yml" # Reglas de triggers (Fase 2)
# Triggers predefinidos (se implementarán)
detectar_cambio_estado: true # Estado ⏳ → ✅ dispara resumen
detectar_nueva_bitacora: true # Crear bitácora dispara rollover
detectar_modificacion_nodo: true # Cambio en nodo actualiza ontología
# Ejecución automática
ejecutar_en_cron: false # Tareas periódicas via cron (Fase 2)
intervalo_cron: "*/5 * * * *" # Cada 5 minutos
# ─── Motor de contexto ──────────────────────────────────────
enabled: false
config_file: triggers.yml
detectar_cambio_estado: true
detectar_nueva_bitacora: true
detectar_modificacion_nodo: true
ejecutar_en_cron: false
intervalo_cron: "*/5 * * * *"
contexto:
cache_habilitado: true # Cache de análisis topológico
cache_habilitado: true
cache_ruta: "../../tmp/contexto-cache.json"
cache_ttl: 300 # Tiempo de vida en segundos (5 min)
# Análisis automático
analizar_topologia: true # Analizar ontología y proyectos
sugerir_comandos_ssh: true # Generar comandos con ProxyJump
detectar_requisitos_acceso: true # Identificar saltos de red
# ─── Dashboard web ──────────────────────────────────────────
cache_ttl: 300
analizar_topologia: true
sugerir_comandos_ssh: true
detectar_requisitos_acceso: true
dashboard:
enabled: false # Habilitado en Fase 3
port: 4567 # Puerto del servidor Sinatra
host: "localhost" # Host de acceso
auto_open: true # Abrir navegador automáticamente
# Características
websockets: true # Actualizaciones en tiempo real
metricas_tiempo_real: true # Gráficos de métricas híbridas
alertas_visuales: true # Alertas de inconsistencias
# Integración
api_json: true # API JSON para integración externa
integracion_dashboard_principal: true # Con dashboard/index.html
# ─── Sistema de logging ─────────────────────────────────────
enabled: false
port: 4567
host: localhost
auto_open: true
websockets: true
metricas_tiempo_real: true
alertas_visuales: true
api_json: true
integracion_dashboard_principal: true
logging:
nivel: "INFO" # DEBUG, INFO, EXITO, ADVERTENCIA, ERROR
nivel: INFO
directorio: "../../logs/adn"
# Formatos
formato_archivo: "json" # JSON estructurado
formato_terminal: "color" # color, texto, none
# Rotación
rotacion_diaria: true # Nuevo archivo cada día
retencion_dias: 30 # Conservar logs por 30 días
# Depuración
debug_detallado: false # Backtraces completos (ADN_DEBUG=1)
# ─── Sistema de backup y versionado ─────────────────────────
formato_archivo: json
formato_terminal: color
rotacion_diaria: true
retencion_dias: 30
debug_detallado: false
backup:
enabled: false # Habilitado en Fase 3
enabled: false
directorio_destino: "../../dtic-BKPs/adn"
# Versionado semántico
versionado_semantico: true # adn-1.2.3.tar.gz
formato_version: "major.features.fixes"
# Frecuencia
automatico: false # Backup automático
frecuencia: "daily" # daily, weekly, monthly
# Cifrado
cifrar: true # Integrar con ns8-candados
comprimir: true # Comprimir en .tar.gz
# ─── Proceso de evolución del ADN ───────────────────────────
versionado_semantico: true
formato_version: major.features.fixes
automatico: false
frecuencia: daily
cifrar: true
comprimir: true
defaults:
modo: stop
evolucion:
# Flujo de trabajo formal
propuestas_directorio: "../../docs/plan"
periodo_revision_horas: 48 # 48 horas para revisión
# Control de versiones
tag_commits: true # Tag [ADN-EVOL] en commits
requerir_aprobacion: true # Requerir aprobación explícita
# Comunicación
notificar_bitacoras: true # Comunicar a bitácoras activas
actualizar_indice: true # Actualizar 00_indice.md automáticamente
# ─── Seguridad e integración ────────────────────────────────
periodo_revision_horas: 48
tag_commits: true
requerir_aprobacion: true
notificar_bitacoras: true
actualizar_indice: true
seguridad:
# Integración con ns8-candados
usar_ns8_candados: true # Usar bóveda de secretos
evitar_texto_plano: true # Nunca mostrar secretos
# Auditoría
auditar_uso_secretos: false # Log de uso de secretos (Fase 3)
log_accesos: true # Registrar accesos a herramientas
# Permisos
verificar_permisos_archivos: true # Verificar chmod 0600 en configs
# ─── Configuración de desarrollo ────────────────────────────
usar_ns8_candados: true
evitar_texto_plano: true
auditar_uso_secretos: false
log_accesos: true
verificar_permisos_archivos: true
desarrollo:
# Pruebas
ejecutar_pruebas: false # Ejecutar spec/ al desarrollar
cobertura_minima: 80 # % mínimo de cobertura
# Debug
modo_verbose: false # Salida detallada
simular_errores: false # Simular errores para testing
# Métricas
reportar_metricas_desempeno: false # Tiempos de ejecución, etc.
# ─── Integración con dtic-BITACORAs ──────────────────────────
ejecutar_pruebas: false
cobertura_minima: 80
modo_verbose: false
simular_errores: false
reportar_metricas_desempeno: false
dtic_bitacoras:
# Configuración de API
api_url: "http://localhost:3002" # URL base de la API
api_timeout: 30 # Timeout en segundos
# Sincronización automática
sincronizacion_automatica: false # Sincronizar .md → DB automáticamente
sincronizar_al_iniciar: true # Sincronizar al iniciar herramientas
# Endpoints principales
api_url: http://localhost:3002
api_timeout: 30
sincronizacion_automatica: false
sincronizar_al_iniciar: true
endpoints:
salud: "/health"
nodos: "/api/nodos"
bitacoras: "/api/bitacoras"
entradas: "/api/entradas"
proyectos: "/api/proyectos"
# Formato de datos
formato_estado_default: "⏳"
formato_modo_default: "P"
# Cache
formato_modo_default: P
cache_habilitado: true
cache_ttl: 300 # 5 minutos
# ─── Reducción de dependencia de IA ─────────────────────────
cache_ttl: 300
autonomia:
# KPIs objetivo (8 semanas)
kpi_acciones_ia_dia: 2 # Meta: <2 acciones IA/día
kpi_triggers_automaticos: 90 # Meta: 90% triggers automáticos
kpi_scripts_sin_ia: 85 # Meta: 85% scripts sin IA
# Transición
fase_actual: 1 # 1-4 según plan
modo_transicion: "coexistencia" # coexistencia, transferencia, autonomia
# Monitoreo
reportar_metricas_autonomia: true # Generar reportes periódicos
kpi_acciones_ia_dia: 2
kpi_triggers_automaticos: 90
kpi_scripts_sin_ia: 85
fase_actual: 1
modo_transicion: coexistencia
reportar_metricas_autonomia: true
+50
View File
@@ -0,0 +1,50 @@
# frozen_string_literal: true
require 'yaml'
require 'fileutils'
module ADN
class Configurador
CONFIG_PATH = File.join(__dir__, '..', 'config', 'config.yml')
def self.leer
return {} unless File.exist?(CONFIG_PATH)
YAML.load_file(CONFIG_PATH) || {}
rescue StandardError => e
warn "Error leyendo configuración: #{e.message}"
{}
end
def self.guardar(config)
FileUtils.mkdir_p(File.dirname(CONFIG_PATH))
File.write(CONFIG_PATH, config.to_yaml)
true
rescue StandardError => e
warn "Error guardando configuración: #{e.message}"
false
end
def self.obtener(ruta_punto, default = nil)
config = leer
partes = ruta_punto.split('.')
valor = config
partes.each do |p|
valor = valor[p] if valor.is_a?(Hash)
return default if valor.nil?
end
valor
end
def self.establecer(ruta_punto, nuevo_valor)
config = leer
partes = ruta_punto.split('.')
temp = config
partes[0...-1].each do |p|
temp[p] ||= {}
temp = temp[p]
end
temp[partes.last] = nuevo_valor
guardar(config)
end
end
end