Feat(ADN): Implementado sistema de configuración persistente y subcomando 'predeterminar' para backups
This commit is contained in:
@@ -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
@@ -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
|
|
||||||
|
|||||||
@@ -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
|
||||||
Reference in New Issue
Block a user