Reestructuración modular integral de servicios, reorganización de gestión en docs/ y limpieza profunda del repositorio

This commit is contained in:
Ricardo Monla
2026-03-06 18:03:07 -03:00
parent 15725e0b3c
commit ac9bdf516b
286 changed files with 36187 additions and 112775 deletions
+412
View File
@@ -0,0 +1,412 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
# tools/orquestador/dashboard/agregar_hitos_db.rb
# ==============================================
# Herramienta para agregar hitos de fase 5 a la base de datos PostgreSQL
# de dtic-BITACORAs (contenedor Docker)
# Filosofía: "Menos es más" - herramienta pequeña, específica y reutilizable
require 'pg'
require 'time'
class HitosDBManager
# Configuración de conexión a PostgreSQL
DB_CONFIG = {
host: 'localhost',
port: 5433,
dbname: 'dtic_bitacoras',
user: 'dtic_user',
password: 'dtic_pass_2026'
}.freeze
# Hitos de fase 5 para agregar
HITOS_FASE_5 = [
{
id_hito: 'BKP02',
titulo: '🔒 Backup Automático de VMs en Proxmox',
descripcion: 'Configurar backups automáticos VZDump programados para DC y SQL Server. Establecer retención de 7 días, almacenamiento en NAS/SMB compartido.',
estado: '⏳',
horas_presencial: 2,
horas_remoto: 2,
nodo_nombre: 'srv-dasu'
},
{
id_hito: 'DB02',
titulo: '🗄️ Backup Automático de Base de Datos sysdasuten',
descripcion: 'Configurar jobs de SQL Server Agent para backup diario completo y log cada 15 minutos. Verificar restauración periódica en ambiente de pruebas.',
estado: '⏳',
horas_presencial: 0,
horas_remoto: 2,
nodo_nombre: 'sql-dasuten'
},
{
id_hito: 'MON01',
titulo: '📊 Monitoreo de Recursos del Hipervisor',
descripcion: 'Implementar sistema de monitoreo básico para CPU, RAM, almacenamiento y red de srv-dasu. Alertas por email/Telegram para recursos críticos.',
estado: '⏳',
horas_presencial: 0,
horas_remoto: 3,
nodo_nombre: 'srv-dasu'
},
{
id_hito: 'DOC01',
titulo: '📋 Documentación Operativa del Sistema',
descripcion: 'Crear manual de operaciones: procedimientos de backup/restore, acceso remoto, resolución de problemas comunes, contactos de soporte, diagramas de red.',
estado: '⏳',
horas_presencial: 0,
horas_remoto: 6,
nodo_nombre: 'srv-ns8'
},
{
id_hito: 'PERF01',
titulo: '⚡ Pruebas de Rendimiento del Sistema',
descripcion: 'Realizar pruebas de carga simulada en sistema DASUTEN. Medir tiempos de respuesta, consumo de recursos, identificar cuellos de botella. Generar reporte de performance.',
estado: '⏳',
horas_presencial: 2,
horas_remoto: 2,
nodo_nombre: 'pcv-dasu0'
}
].freeze
def initialize
@conn = nil
end
def conectar_db
puts "🔌 Conectando a PostgreSQL..."
puts " • Host: #{DB_CONFIG[:host]}"
puts " • Puerto: #{DB_CONFIG[:port]}"
puts " • Base de datos: #{DB_CONFIG[:dbname]}"
begin
@conn = PG.connect(DB_CONFIG)
puts "✅ Conexión exitosa"
true
rescue PG::Error => e
puts "❌ Error de conexión: #{e.message}"
puts " Verifique que:"
puts " 1. El contenedor PostgreSQL esté ejecutándose"
puts " 2. El puerto 5433 esté accesible"
puts " 3. Las credenciales sean correctas"
false
end
end
def desconectar_db
@conn&.close
puts "🔌 Conexión cerrada"
end
def verificar_estructura
puts "🔍 Verificando estructura de la base de datos..."
tablas_requeridas = ['proyectos', 'fases', 'hitos', 'nodos']
tablas_encontradas = []
tablas_requeridas.each do |tabla|
resultado = @conn.exec("SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_schema = 'bitacoras' AND table_name = $1)", [tabla])
existe = resultado[0]['exists'] == 't'
tablas_encontradas << tabla if existe
puts "#{tabla}: #{existe ? '✅' : '❌'}"
end
if tablas_encontradas.size == tablas_requeridas.size
puts "✅ Todas las tablas requeridas existen"
true
else
puts "❌ Faltan tablas requeridas"
false
end
end
def obtener_proyecto_p2601
puts "📋 Buscando proyecto P2601..."
resultado = @conn.exec("SELECT id, codigo, nombre FROM bitacoras.proyectos WHERE codigo = $1", ['P2601'])
if resultado.ntuples.zero?
puts "❌ Proyecto P2601 no encontrado"
nil
else
proyecto = resultado[0]
puts "✅ Proyecto encontrado: #{proyecto['codigo']} - #{proyecto['nombre']}"
proyecto
end
end
def obtener_fase_5(proyecto_id)
puts "🔍 Buscando fase 5 del proyecto..."
resultado = @conn.exec("SELECT id, numero, nombre FROM bitacoras.fases WHERE proyecto_id = $1 AND numero = 5", [proyecto_id])
if resultado.ntuples.zero?
puts "❌ Fase 5 no encontrada"
nil
else
fase = resultado[0]
puts "✅ Fase encontrada: #{fase['numero']} - #{fase['nombre']}"
fase
end
end
def listar_hitos_existentes(fase_id)
puts "📋 Listando hitos existentes en fase 5..."
resultado = @conn.exec("SELECT id_hito, titulo, estado FROM bitacoras.hitos WHERE fase_id = $1 ORDER BY id_hito", [fase_id])
if resultado.ntuples.zero?
puts " ️ No hay hitos en esta fase"
[]
else
puts " 📊 Hitos encontrados: #{resultado.ntuples}"
resultado.each do |hito|
puts "#{hito['id_hito']}: #{hito['titulo']} (#{hito['estado']})"
end
resultado.to_a
end
end
def agregar_hitos_fase_5
puts "🎯 AGREGANDO HITOS A FASE 5"
puts "=" * 60
# Conectar a la base de datos
unless conectar_db
puts "❌ No se pudo conectar a la base de datos"
return false
end
begin
# Verificar estructura
unless verificar_estructura
puts "❌ La estructura de la base de datos no es válida"
return false
end
# Obtener proyecto P2601
proyecto = obtener_proyecto_p2601
return false unless proyecto
# Obtener fase 5
fase = obtener_fase_5(proyecto['id'])
return false unless fase
# Listar hitos existentes
hitos_existentes = listar_hitos_existentes(fase['id'])
ids_existentes = hitos_existentes.map { |h| h['id_hito'] }
# Filtrar hitos que no existen
hitos_a_agregar = HITOS_FASE_5.reject { |h| ids_existentes.include?(h[:id_hito]) }
if hitos_a_agregar.empty?
puts "️ Todos los hitos de fase 5 ya existen en la base de datos"
return true
end
puts "📝 Agregando #{hitos_a_agregar.size} hitos nuevos..."
# Agregar cada hito
hitos_agregados = []
hitos_a_agregar.each do |hito|
if agregar_hito(fase['id'], hito)
hitos_agregados << hito[:id_hito]
end
end
# Mostrar resultados
mostrar_resultados(hitos_agregados, hitos_a_agregar.size)
hitos_agregados.any?
ensure
desconectar_db
end
end
def agregar_hito(fase_id, hito)
puts " Agregando hito: #{hito[:id_hito]}"
begin
# Obtener ID del nodo si se especifica
nodo_id = nil
if hito[:nodo_nombre]
resultado = @conn.exec("SELECT id FROM bitacoras.nodos WHERE nombre = $1", [hito[:nodo_nombre]])
nodo_id = resultado[0]['id'] if resultado.ntuples > 0
end
# Insertar hito
@conn.exec(
"INSERT INTO bitacoras.hitos (fase_id, id_hito, titulo, descripcion, estado, horas_presencial, horas_remoto, nodo_id)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)",
[
fase_id,
hito[:id_hito],
hito[:titulo],
hito[:descripcion],
hito[:estado],
hito[:horas_presencial],
hito[:horas_remoto],
nodo_id
]
)
puts "#{hito[:id_hito]} agregado exitosamente"
true
rescue PG::Error => e
puts " ❌ Error al agregar #{hito[:id_hito]}: #{e.message}"
false
end
end
def mostrar_resultados(hitos_agregados, total_intentos)
puts "\n📊 RESULTADOS DE LA OPERACIÓN"
puts "=" * 60
if hitos_agregados.empty?
puts "️ No se agregaron hitos nuevos"
else
puts "✅ Hitos agregados exitosamente: #{hitos_agregados.size}/#{total_intentos}"
hitos_agregados.each do |id_hito|
puts "#{id_hito}"
end
end
# Mostrar hitos de fase 5
puts "\n🎯 HITOS DE FASE 5 COMPLETOS:"
HITOS_FASE_5.each do |hito|
existe = hitos_agregados.include?(hito[:id_hito]) ? '✅' : '📋'
puts " #{existe} #{hito[:id_hito]}: #{hito[:titulo]}"
end
puts "\n🔗 Dashboard actualizado: https://ns8.frlr.utn.edu.ar/bitacoras/p2601"
end
def ver_estado_actual
puts "🔍 ESTADO ACTUAL DEL DASHBOARD P2601"
puts "=" * 60
unless conectar_db
puts "❌ No se pudo conectar a la base de datos"
return false
end
begin
# Obtener proyecto P2601
proyecto = obtener_proyecto_p2601
return false unless proyecto
# Obtener todas las fases
resultado = @conn.exec("SELECT id, numero, nombre FROM bitacoras.fases WHERE proyecto_id = $1 ORDER BY numero", [proyecto['id']])
puts "📋 PROYECTO: #{proyecto['codigo']} - #{proyecto['nombre']}"
puts "📊 FASES: #{resultado.ntuples}"
resultado.each do |fase|
puts "\n📁 Fase #{fase['numero']}: #{fase['nombre']}"
# Obtener hitos de esta fase
hitos = @conn.exec("SELECT id_hito, titulo, estado, horas_presencial, horas_remoto FROM bitacoras.hitos WHERE fase_id = $1 ORDER BY id_hito", [fase['id']])
if hitos.ntuples.zero?
puts " ️ Sin hitos"
else
puts " 📝 Hitos: #{hitos.ntuples}"
hitos.each do |hito|
estado_emoji = hito['estado']
horas = " (P:#{hito['horas_presencial']}h, R:#{hito['horas_remoto']}h)"
puts "#{estado_emoji} #{hito['id_hito']}: #{hito['titulo']}#{horas}"
end
end
end
# Métricas generales
puts "\n📈 MÉTRICAS GENERALES:"
metricas = @conn.exec("
SELECT
COUNT(*) as total_hitos,
COUNT(*) FILTER (WHERE h.estado = '✅') as completados,
COUNT(*) FILTER (WHERE h.estado != '✅') as pendientes,
COALESCE(SUM(h.horas_presencial), 0) as total_presencial,
COALESCE(SUM(h.horas_remoto), 0) as total_remoto
FROM bitacoras.hitos h
JOIN bitacoras.fases f ON h.fase_id = f.id
WHERE f.proyecto_id = $1
", [proyecto['id']])
if metricas.ntuples > 0
m = metricas[0]
puts " • Total hitos: #{m['total_hitos']}"
puts " • Completados: #{m['completados']}"
puts " • Pendientes: #{m['pendientes']}"
puts " • Horas presenciales: #{m['total_presencial']}h"
puts " • Horas remotas: #{m['total_remoto']}h"
puts " • Total horas: #{m['total_presencial'].to_i + m['total_remoto'].to_i}h"
end
true
ensure
desconectar_db
end
end
end
# Interfaz de línea de comandos
if __FILE__ == $0
begin
manager = HitosDBManager.new
# Parsear argumentos
command = ARGV[0] || 'agregar'
case command
when 'agregar', 'add'
success = manager.agregar_hitos_fase_5
when 'estado', 'status'
success = manager.ver_estado_actual
when 'help', '--help', '-h'
puts "📖 USO:"
puts " #{$0} [comando]"
puts
puts "COMANDOS:"
puts " agregar, add - Agregar hitos de fase 5 a la base de datos"
puts " estado, status - Ver estado actual del dashboard P2601"
puts " help - Mostrar esta ayuda"
puts
puts "DESCRIPCIÓN:"
puts " Esta herramienta agrega hitos de fase 5 al dashboard P2601"
puts " que se encuentra en: https://ns8.frlr.utn.edu.ar/bitacoras/p2601"
puts
puts "REQUISITOS:"
puts " 1. Contenedor PostgreSQL de dtic-BITACORAs ejecutándose"
puts " 2. Puerto 5433 accesible localmente"
puts " 3. Credenciales de base de datos configuradas"
puts
puts "HITOS DE FASE 5:"
HitosDBManager::HITOS_FASE_5.each do |hito|
puts "#{hito[:id_hito]}: #{hito[:titulo]}"
end
puts
success = true
else
puts "❌ Comando '#{command}' no reconocido"
puts " Use '#{$0} help' para ver la ayuda"
success = false
end
if success
puts "\n🚀 PRÓXIMOS PASOS:"
puts " 1. Verificar dashboard: https://ns8.frlr.utn.edu.ar/bitacoras/p2601"
puts " 2. Los cambios deberían reflejarse automáticamente"
puts " 3. Actualizar estados según progreso real"
puts
puts "📅 #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}"
puts "=" * 60
else
exit 1
end
rescue => e
puts "❌ Error: #{e.message}"
puts e.backtrace if ENV['DEBUG']
exit 1
end
end