Files
dtic-DIIAA/tools/orquestador/dashboard/agregar_hitos_db.rb
T

413 lines
12 KiB
Ruby
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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