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 'optparse'
require 'fileutils' require 'fileutils'
require 'date' require 'date'
require_relative '../core/configurador'
module ADN module ADN
class SubcomandoBackup class SubcomandoBackup
def initialize(args, logger) def initialize(args, logger)
@args = args @args = args
@logger = logger @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 end
def ejecutar def ejecutar
@@ -19,9 +27,12 @@ module ADN
opts.separator "Ejecuta un backup remoto siguiendo el protocolo ADN" opts.separator "Ejecuta un backup remoto siguiendo el protocolo ADN"
opts.separator "" opts.separator ""
opts.separator "Opciones:" 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 @opciones[:modo] = m
end 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 opts.on("-h", "--help", "Mostrar esta ayuda") do
puts opts puts opts
exit exit
@@ -42,6 +53,11 @@ module ADN
exit 1 exit 1
end end
if @args[0] == 'predeterminar'
comando_predeterminar(@args[1..-1])
return
end
nodo = @args[0] nodo = @args[0]
ejecutar_backup(nodo) ejecutar_backup(nodo)
end end
@@ -100,13 +116,11 @@ module ADN
end end
contenido = File.read(ruta_nodo) contenido = File.read(ruta_nodo)
# Buscamos VMID (puede decir "VM ID: 100" o "vmid: 100") # Buscamos VMID (puede decir "VM ID: 100" o "vmid: 100" o "- **VMID**: `100`")
vmid = contenido.match(/(?:VM ID|vmid):\s*(\d+)/i)&.[](1) vmid = contenido.match(/(?:VM ID|vmid|VMID)\*\*?:?\s*`?(\d+)`?/i)&.[](1)
# Buscamos el host (especificamente el link o nombre entre backticks) # Buscamos el host (especificamente el link o nombre entre backticks)
# Formato: - **Padre/Host**: `srv-dasu` host_name = contenido.match(/(?:Padre\/Host|Host Anfitrión|Anfitrión|Host)\*\*:?\s*`([^`]+)`/i)&.[](1)
host_name = contenido.match(/Padre\/Host\*\*:?\s*`([^`]+)`/)&.[](1)
host_name ||= contenido.match(/Host\*\*:?\s*`([^`]+)`/)&.[](1)
unless vmid && host_name unless vmid && host_name
@logger.error("Contexto incompleto en #{nombre_nodo}.md: VMID=#{vmid}, Host=#{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) contenido = File.read(ruta)
# Actualizar la línea de tiempo # 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 # Buscamos la tarea BKP-ADN
nuevo_contenido = contenido.gsub(/#### ⏳ - BKP-ADN/, "#### #{estado} - BKP-ADN") nuevo_contenido = contenido.gsub(/#### ⏳ - BKP-ADN/, "#### #{estado} - BKP-ADN")
@@ -187,5 +201,28 @@ module ADN
File.write(ruta, nuevo_contenido) File.write(ruta, nuevo_contenido)
end 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
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: rutas:
# Directorio raíz del proyecto (se calcula automáticamente)
proyecto: "../.." proyecto: "../.."
# Directorios de contenido
adn: "../../adn" adn: "../../adn"
bitacoras: "../../bitacoras" bitacoras: "../../bitacoras"
nodos: "../../nodos" nodos: "../../nodos"
proyectos: "../../proyectos" proyectos: "../../proyectos"
logs: "../../logs/adn" logs: "../../logs/adn"
tmp: "../../tmp" tmp: "../../tmp"
# Herramientas existentes
ns8_candados: "../ns8-candados/ns8-candados.rb" ns8_candados: "../ns8-candados/ns8-candados.rb"
# ─── Configuración del validador ────────────────────────────
validador: validador:
# Modos de operación watch_mode: false
watch_mode: false # Monitorear cambios en tiempo real (Fase 1) strict_mode: true
strict_mode: true # Rechazar errores graves inmediatamente validar_formato_tiempo: true
validar_iconografia: true
# Validaciones específicas validar_metricas_hibridas: true
validar_formato_tiempo: true # Formato HH:MM (sin "hs") validar_referencias_rollover: true
validar_iconografia: true # Iconos de estado correctos pre_commit_hook: true
validar_metricas_hibridas: true # Presencia de [Físico/Remoto:] pre_push_hook: false
validar_referencias_rollover: true # Hipervínculos bidireccionales notificar_desktop: false
notificar_email: false
# 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 ────────────────────────────────────
triggers: triggers:
enabled: false # Habilitado en Fase 2 enabled: false
config_file: "triggers.yml" # Reglas de triggers (Fase 2) config_file: triggers.yml
detectar_cambio_estado: true
# Triggers predefinidos (se implementarán) detectar_nueva_bitacora: true
detectar_cambio_estado: true # Estado ⏳ → ✅ dispara resumen detectar_modificacion_nodo: true
detectar_nueva_bitacora: true # Crear bitácora dispara rollover ejecutar_en_cron: false
detectar_modificacion_nodo: true # Cambio en nodo actualiza ontología intervalo_cron: "*/5 * * * *"
# Ejecución automática
ejecutar_en_cron: false # Tareas periódicas via cron (Fase 2)
intervalo_cron: "*/5 * * * *" # Cada 5 minutos
# ─── Motor de contexto ──────────────────────────────────────
contexto: contexto:
cache_habilitado: true # Cache de análisis topológico cache_habilitado: true
cache_ruta: "../../tmp/contexto-cache.json" cache_ruta: "../../tmp/contexto-cache.json"
cache_ttl: 300 # Tiempo de vida en segundos (5 min) cache_ttl: 300
analizar_topologia: true
# Análisis automático sugerir_comandos_ssh: true
analizar_topologia: true # Analizar ontología y proyectos detectar_requisitos_acceso: true
sugerir_comandos_ssh: true # Generar comandos con ProxyJump
detectar_requisitos_acceso: true # Identificar saltos de red
# ─── Dashboard web ──────────────────────────────────────────
dashboard: dashboard:
enabled: false # Habilitado en Fase 3 enabled: false
port: 4567 # Puerto del servidor Sinatra port: 4567
host: "localhost" # Host de acceso host: localhost
auto_open: true # Abrir navegador automáticamente auto_open: true
websockets: true
# Características metricas_tiempo_real: true
websockets: true # Actualizaciones en tiempo real alertas_visuales: true
metricas_tiempo_real: true # Gráficos de métricas híbridas api_json: true
alertas_visuales: true # Alertas de inconsistencias integracion_dashboard_principal: true
# Integración
api_json: true # API JSON para integración externa
integracion_dashboard_principal: true # Con dashboard/index.html
# ─── Sistema de logging ─────────────────────────────────────
logging: logging:
nivel: "INFO" # DEBUG, INFO, EXITO, ADVERTENCIA, ERROR nivel: INFO
directorio: "../../logs/adn" directorio: "../../logs/adn"
formato_archivo: json
# Formatos formato_terminal: color
formato_archivo: "json" # JSON estructurado rotacion_diaria: true
formato_terminal: "color" # color, texto, none retencion_dias: 30
debug_detallado: false
# 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 ─────────────────────────
backup: backup:
enabled: false # Habilitado en Fase 3 enabled: false
directorio_destino: "../../dtic-BKPs/adn" directorio_destino: "../../dtic-BKPs/adn"
versionado_semantico: true
# Versionado semántico formato_version: major.features.fixes
versionado_semantico: true # adn-1.2.3.tar.gz automatico: false
formato_version: "major.features.fixes" frecuencia: daily
cifrar: true
# Frecuencia comprimir: true
automatico: false # Backup automático defaults:
frecuencia: "daily" # daily, weekly, monthly modo: stop
# Cifrado
cifrar: true # Integrar con ns8-candados
comprimir: true # Comprimir en .tar.gz
# ─── Proceso de evolución del ADN ───────────────────────────
evolucion: evolucion:
# Flujo de trabajo formal
propuestas_directorio: "../../docs/plan" propuestas_directorio: "../../docs/plan"
periodo_revision_horas: 48 # 48 horas para revisión periodo_revision_horas: 48
tag_commits: true
# Control de versiones requerir_aprobacion: true
tag_commits: true # Tag [ADN-EVOL] en commits notificar_bitacoras: true
requerir_aprobacion: true # Requerir aprobación explícita actualizar_indice: true
# Comunicación
notificar_bitacoras: true # Comunicar a bitácoras activas
actualizar_indice: true # Actualizar 00_indice.md automáticamente
# ─── Seguridad e integración ────────────────────────────────
seguridad: seguridad:
# Integración con ns8-candados usar_ns8_candados: true
usar_ns8_candados: true # Usar bóveda de secretos evitar_texto_plano: true
evitar_texto_plano: true # Nunca mostrar secretos auditar_uso_secretos: false
log_accesos: true
# Auditoría verificar_permisos_archivos: true
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 ────────────────────────────
desarrollo: desarrollo:
# Pruebas ejecutar_pruebas: false
ejecutar_pruebas: false # Ejecutar spec/ al desarrollar cobertura_minima: 80
cobertura_minima: 80 # % mínimo de cobertura modo_verbose: false
simular_errores: false
# Debug reportar_metricas_desempeno: false
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 ──────────────────────────
dtic_bitacoras: dtic_bitacoras:
# Configuración de API api_url: http://localhost:3002
api_url: "http://localhost:3002" # URL base de la API api_timeout: 30
api_timeout: 30 # Timeout en segundos sincronizacion_automatica: false
sincronizar_al_iniciar: true
# Sincronización automática
sincronizacion_automatica: false # Sincronizar .md → DB automáticamente
sincronizar_al_iniciar: true # Sincronizar al iniciar herramientas
# Endpoints principales
endpoints: endpoints:
salud: "/health" salud: "/health"
nodos: "/api/nodos" nodos: "/api/nodos"
bitacoras: "/api/bitacoras" bitacoras: "/api/bitacoras"
entradas: "/api/entradas" entradas: "/api/entradas"
proyectos: "/api/proyectos" proyectos: "/api/proyectos"
# Formato de datos
formato_estado_default: "⏳" formato_estado_default: "⏳"
formato_modo_default: "P" formato_modo_default: P
# Cache
cache_habilitado: true cache_habilitado: true
cache_ttl: 300 # 5 minutos cache_ttl: 300
# ─── Reducción de dependencia de IA ─────────────────────────
autonomia: autonomia:
# KPIs objetivo (8 semanas) kpi_acciones_ia_dia: 2
kpi_acciones_ia_dia: 2 # Meta: <2 acciones IA/día kpi_triggers_automaticos: 90
kpi_triggers_automaticos: 90 # Meta: 90% triggers automáticos kpi_scripts_sin_ia: 85
kpi_scripts_sin_ia: 85 # Meta: 85% scripts sin IA fase_actual: 1
modo_transicion: coexistencia
# Transición reportar_metricas_autonomia: true
fase_actual: 1 # 1-4 según plan
modo_transicion: "coexistencia" # coexistencia, transferencia, autonomia
# Monitoreo
reportar_metricas_autonomia: true # Generar reportes periódicos
+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