Reestructuración modular integral de servicios, reorganización de gestión en docs/ y limpieza profunda del repositorio
This commit is contained in:
@@ -0,0 +1,485 @@
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
# tools/actualizar_dashboard_p2601.rb — Herramienta para actualizar dashboard P2601 con hitos adicionales
|
||||
# ================================================================================
|
||||
# Esta herramienta actualiza el dashboard P2601 (dashboard/index.html) agregando
|
||||
# hitos adicionales propuestos para la fase de implementación final del proyecto DASUTEN.
|
||||
#
|
||||
# Características:
|
||||
# • Lee el dashboard HTML actual
|
||||
# • Agrega hitos adicionales basados en análisis del proyecto
|
||||
# • Mantiene la estructura y estilo existentes
|
||||
# • Integra con herramientas ADN para seguimiento
|
||||
# • Crea backup del archivo original
|
||||
#
|
||||
# Uso:
|
||||
# ruby tools/actualizar_dashboard_p2601.rb [OPCIONES]
|
||||
#
|
||||
# Opciones:
|
||||
# --dry-run Mostrar cambios sin aplicar
|
||||
# --backup Crear backup antes de modificar
|
||||
# --hitos N Agregar solo N hitos (por defecto: todos)
|
||||
# --categoria CAT Filtrar por categoría (infraestructura, monitoreo, etc.)
|
||||
# --help Mostrar ayuda
|
||||
|
||||
require 'nokogiri'
|
||||
require 'fileutils'
|
||||
require 'optparse'
|
||||
require 'date'
|
||||
|
||||
class ActualizadorDashboardP2601
|
||||
VERSION = "1.0.0"
|
||||
|
||||
# Hitos adicionales propuestos para la fase de implementación
|
||||
HITOS_ADICIONALES = [
|
||||
{
|
||||
id: "BKP02",
|
||||
fecha: "Pendiente",
|
||||
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.",
|
||||
tags: [
|
||||
'<span class="tag tag-remote">Remoto: ~2h</span>',
|
||||
'<span class="tag tag-phys">Requiere acceso físico</span>'
|
||||
],
|
||||
estado: "pending", # pending, active, done
|
||||
categoria: "infraestructura",
|
||||
prioridad: "alta",
|
||||
prerequisitos: ["FÍSICO"]
|
||||
},
|
||||
{
|
||||
id: "MON01",
|
||||
fecha: "Pendiente",
|
||||
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.",
|
||||
tags: [
|
||||
'<span class="tag tag-remote">Remoto: ~3h</span>',
|
||||
'<span class="tag">Herramientas ADN</span>'
|
||||
],
|
||||
estado: "pending",
|
||||
categoria: "monitoreo",
|
||||
prioridad: "media",
|
||||
prerequisitos: ["FÍSICO"]
|
||||
},
|
||||
{
|
||||
id: "DB02",
|
||||
fecha: "Pendiente",
|
||||
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.",
|
||||
tags: [
|
||||
'<span class="tag tag-remote">Remoto: ~2h</span>',
|
||||
'<span class="tag">SQL Agent</span>'
|
||||
],
|
||||
estado: "pending",
|
||||
categoria: "base_datos",
|
||||
prioridad: "alta",
|
||||
prerequisitos: ["ADDOM"]
|
||||
},
|
||||
{
|
||||
id: "SEC01",
|
||||
fecha: "Pendiente",
|
||||
titulo: "Auditoría de Seguridad del Dominio",
|
||||
descripcion: "Revisar y configurar políticas de auditoría en DC. Logs de acceso, cambios en AD, intentos fallidos. Centralización de logs en syslog server.",
|
||||
tags: [
|
||||
'<span class="tag tag-remote">Remoto: ~4h</span>',
|
||||
'<span class="tag">Windows Event Log</span>'
|
||||
],
|
||||
estado: "pending",
|
||||
categoria: "seguridad",
|
||||
prioridad: "media",
|
||||
prerequisitos: ["GPO"]
|
||||
},
|
||||
{
|
||||
id: "DOC01",
|
||||
fecha: "Pendiente",
|
||||
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.",
|
||||
tags: [
|
||||
'<span class="tag tag-remote">Remoto: ~6h</span>',
|
||||
'<span class="tag">Herramientas ADN</span>',
|
||||
'<span class="tag">Git</span>'
|
||||
],
|
||||
estado: "pending",
|
||||
categoria: "documentacion",
|
||||
prioridad: "media",
|
||||
prerequisitos: ["UAT"]
|
||||
},
|
||||
{
|
||||
id: "RED01",
|
||||
fecha: "Pendiente",
|
||||
titulo: "Optimización de Red y Firewall",
|
||||
descripcion: "Revisar reglas de firewall en todas las VMs. Optimizar rutas Tailscale. Configurar QoS para tráfico crítico. Documentar reglas de firewall.",
|
||||
tags: [
|
||||
'<span class="tag tag-remote">Remoto: ~3h</span>',
|
||||
'<span class="tag">Tailscale</span>',
|
||||
'<span class="tag">Windows Firewall</span>'
|
||||
],
|
||||
estado: "pending",
|
||||
categoria: "red",
|
||||
prioridad: "baja",
|
||||
prerequisitos: ["NET01"]
|
||||
},
|
||||
{
|
||||
id: "PERF01",
|
||||
fecha: "Pendiente",
|
||||
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.",
|
||||
tags: [
|
||||
'<span class="tag tag-phys">Físico: 2h</span>',
|
||||
'<span class="tag tag-remote">Remoto: ~2h</span>',
|
||||
'<span class="tag">SQL Profiler</span>'
|
||||
],
|
||||
estado: "pending",
|
||||
categoria: "performance",
|
||||
prioridad: "media",
|
||||
prerequisitos: ["CLI"]
|
||||
},
|
||||
{
|
||||
id: "MIG02",
|
||||
fecha: "Pendiente",
|
||||
titulo: "Plan de Migración de Datos Final",
|
||||
descripcion: "Desarrollar script/procedimiento para migración incremental de datos desde sistema antiguo. Validar integridad de datos migrados. Crear rollback plan.",
|
||||
tags: [
|
||||
'<span class="tag tag-remote">Remoto: ~8h</span>',
|
||||
'<span class="tag">SSIS</span>',
|
||||
'<span class="tag">PowerShell</span>'
|
||||
],
|
||||
estado: "pending",
|
||||
categoria: "migracion",
|
||||
prioridad: "alta",
|
||||
prerequisitos: ["P02.APP"]
|
||||
}
|
||||
].freeze
|
||||
|
||||
def initialize(opciones = {})
|
||||
@opciones = opciones
|
||||
@dashboard_path = '/home/rmonla/Documentos/GitHub/dtic-srv-NS8/dashboard/index.html'
|
||||
@backup_path = nil
|
||||
@cambios = []
|
||||
@errores = []
|
||||
end
|
||||
|
||||
def ejecutar
|
||||
mostrar_encabezado
|
||||
|
||||
unless validar_entorno
|
||||
puts "❌ No se puede continuar debido a errores en el entorno."
|
||||
exit 1
|
||||
end
|
||||
|
||||
if @opciones[:dry_run]
|
||||
puts "🔍 MODO SIMULACIÓN (dry-run) - No se aplicarán cambios reales"
|
||||
puts "-" * 50
|
||||
end
|
||||
|
||||
contenido_original = leer_dashboard
|
||||
return if contenido_original.nil?
|
||||
|
||||
if @opciones[:backup] && !@opciones[:dry_run]
|
||||
crear_backup(contenido_original)
|
||||
end
|
||||
|
||||
hitos_filtrados = filtrar_hitos
|
||||
puts "📋 Hitos a agregar: #{hitos_filtrados.size} de #{HITOS_ADICIONALES.size} disponibles"
|
||||
|
||||
contenido_actualizado = agregar_hitos_al_dashboard(contenido_original, hitos_filtrados)
|
||||
|
||||
if contenido_actualizado && !@opciones[:dry_run]
|
||||
guardar_dashboard(contenido_actualizado)
|
||||
elsif @opciones[:dry_run]
|
||||
mostrar_cambios_simulados(contenido_actualizado)
|
||||
end
|
||||
|
||||
mostrar_resumen
|
||||
rescue => e
|
||||
puts "❌ Error fatal: #{e.message}"
|
||||
puts e.backtrace if @opciones[:debug]
|
||||
exit 1
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def mostrar_encabezado
|
||||
puts "🔄 ACTUALIZADOR DE DASHBOARD P2601 - v#{VERSION}"
|
||||
puts "=" * 70
|
||||
puts "Proyecto: DASUTEN (P2601)"
|
||||
puts "Fecha: #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}"
|
||||
puts "Dashboard: #{@dashboard_path}"
|
||||
puts ""
|
||||
end
|
||||
|
||||
def validar_entorno
|
||||
puts "🔍 Validando entorno..."
|
||||
|
||||
# Verificar que el dashboard existe
|
||||
unless File.exist?(@dashboard_path)
|
||||
@errores << "Dashboard no encontrado en: #{@dashboard_path}"
|
||||
puts "❌ #{@errores.last}"
|
||||
return false
|
||||
end
|
||||
|
||||
# Verificar permisos de escritura
|
||||
unless File.writable?(@dashboard_path)
|
||||
@errores << "Sin permisos de escritura en: #{@dashboard_path}"
|
||||
puts "❌ #{@errores.last}"
|
||||
return false
|
||||
end
|
||||
|
||||
# Verificar que es un archivo HTML válido
|
||||
contenido = File.read(@dashboard_path, 500)
|
||||
unless contenido.include?('<!DOCTYPE html>') || contenido.include?('<html')
|
||||
@errores << "El archivo no parece ser un HTML válido"
|
||||
puts "⚠️ #{@errores.last}"
|
||||
end
|
||||
|
||||
puts "✅ Entorno validado correctamente"
|
||||
true
|
||||
end
|
||||
|
||||
def leer_dashboard
|
||||
puts "📖 Leyendo dashboard actual..."
|
||||
|
||||
begin
|
||||
contenido = File.read(@dashboard_path)
|
||||
puts "✅ Dashboard leído (#{contenido.length} bytes, #{contenido.lines.count} líneas)"
|
||||
contenido
|
||||
rescue => e
|
||||
@errores << "Error leyendo dashboard: #{e.message}"
|
||||
puts "❌ #{@errores.last}"
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def crear_backup(contenido)
|
||||
timestamp = Time.now.strftime('%Y%m%d_%H%M%S')
|
||||
@backup_path = "#{@dashboard_path}.backup_#{timestamp}"
|
||||
|
||||
puts "💾 Creando backup en: #{@backup_path}"
|
||||
|
||||
begin
|
||||
File.write(@backup_path, contenido)
|
||||
puts "✅ Backup creado exitosamente"
|
||||
rescue => e
|
||||
@errores << "Error creando backup: #{e.message}"
|
||||
puts "⚠️ #{@errores.last}"
|
||||
@backup_path = nil
|
||||
end
|
||||
end
|
||||
|
||||
def filtrar_hitos
|
||||
hitos = HITOS_ADICIONALES.dup
|
||||
|
||||
# Filtrar por categoría si se especificó
|
||||
if @opciones[:categoria]
|
||||
categoria = @opciones[:categoria].downcase
|
||||
hitos.select! { |h| h[:categoria] == categoria }
|
||||
puts " • Filtrado por categoría: #{categoria} (#{hitos.size} hitos)"
|
||||
end
|
||||
|
||||
# Limitar número de hitos si se especificó
|
||||
if @opciones[:hitos] && @opciones[:hitos] > 0
|
||||
limite = [@opciones[:hitos], hitos.size].min
|
||||
hitos = hitos.first(limite)
|
||||
puts " • Limitado a: #{limite} hitos"
|
||||
end
|
||||
|
||||
# Ordenar por prioridad (alta -> media -> baja)
|
||||
orden_prioridad = { 'alta' => 1, 'media' => 2, 'baja' => 3 }
|
||||
hitos.sort_by! { |h| orden_prioridad[h[:prioridad]] }
|
||||
|
||||
hitos
|
||||
end
|
||||
|
||||
def agregar_hitos_al_dashboard(contenido, hitos)
|
||||
puts "✏️ Agregando hitos al dashboard..."
|
||||
|
||||
return contenido if hitos.empty?
|
||||
|
||||
# Buscar el final de la sección de timeline
|
||||
timeline_end_marker = '</div><!-- Timeline de Hitos -->'
|
||||
|
||||
unless contenido.include?(timeline_end_marker)
|
||||
timeline_end_marker = '</div>'
|
||||
puts "⚠️ Marcador de timeline no encontrado, usando marcador genérico"
|
||||
end
|
||||
|
||||
# Generar HTML para los nuevos hitos
|
||||
hitos_html = hitos.map { |hito| generar_html_hito(hito) }.join("\n\n")
|
||||
|
||||
# Insertar los nuevos hitos antes del marcador de fin
|
||||
nuevo_contenido = contenido.gsub(timeline_end_marker) do |match|
|
||||
"#{hitos_html}\n\n#{match}"
|
||||
end
|
||||
|
||||
@cambios = hitos.map { |h| h[:id] }
|
||||
puts "✅ #{hitos.size} hitos preparados para agregar: #{@cambios.join(', ')}"
|
||||
|
||||
nuevo_contenido
|
||||
end
|
||||
|
||||
def generar_html_hito(hito)
|
||||
clase_dot = case hito[:estado]
|
||||
when 'done' then 'tl-dot done'
|
||||
when 'active' then 'tl-dot active'
|
||||
else 'tl-dot pending'
|
||||
end
|
||||
|
||||
<<~HTML
|
||||
<div class="tl-item">
|
||||
<div class="#{clase_dot}"></div>
|
||||
<div class="tl-content">
|
||||
<div class="tl-header"><span class="tl-id">#{hito[:id]}</span><span class="tl-date">#{hito[:fecha]}</span></div>
|
||||
<div class="tl-title">#{hito[:titulo]}</div>
|
||||
<div class="tl-desc">#{hito[:descripcion]}</div>
|
||||
<div class="tl-tags">#{hito[:tags].join}</div>
|
||||
</div>
|
||||
</div>
|
||||
HTML
|
||||
end
|
||||
|
||||
def guardar_dashboard(contenido)
|
||||
puts "💾 Guardando dashboard actualizado..."
|
||||
|
||||
begin
|
||||
# Crear backup temporal
|
||||
temp_backup = "#{@dashboard_path}.tmp"
|
||||
File.write(temp_backup, contenido)
|
||||
|
||||
# Verificar que el HTML es válido
|
||||
doc = Nokogiri::HTML(contenido)
|
||||
if doc.errors.empty?
|
||||
# Reemplazar el archivo original
|
||||
FileUtils.mv(temp_backup, @dashboard_path)
|
||||
puts "✅ Dashboard actualizado exitosamente"
|
||||
puts " • Nuevo tamaño: #{contenido.length} bytes"
|
||||
puts " • Nuevas líneas: #{contenido.lines.count}"
|
||||
else
|
||||
@errores << "HTML inválido generado"
|
||||
puts "❌ #{@errores.last}"
|
||||
FileUtils.rm(temp_backup) if File.exist?(temp_backup)
|
||||
end
|
||||
rescue => e
|
||||
@errores << "Error guardando dashboard: #{e.message}"
|
||||
puts "❌ #{@errores.last}"
|
||||
end
|
||||
end
|
||||
|
||||
def mostrar_cambios_simulados(contenido)
|
||||
puts "📋 CAMBIOS SIMULADOS (dry-run):"
|
||||
puts "-" * 50
|
||||
|
||||
if @cambios.any?
|
||||
puts "✅ Se agregarían #{@cambios.size} hitos:"
|
||||
@cambios.each_with_index do |hito_id, index|
|
||||
hito = HITOS_ADICIONALES.find { |h| h[:id] == hito_id }
|
||||
puts " #{index + 1}. #{hito_id}: #{hito[:titulo]}"
|
||||
puts " Categoría: #{hito[:categoria]}, Prioridad: #{hito[:prioridad]}"
|
||||
puts " Prerequisitos: #{hito[:prerequisitos].join(', ')}" if hito[:prerequisitos].any?
|
||||
end
|
||||
|
||||
puts "\n📊 Estadísticas del nuevo contenido:"
|
||||
puts " • Tamaño: #{contenido.length} bytes"
|
||||
puts " • Líneas: #{contenido.lines.count}"
|
||||
|
||||
# Mostrar ejemplo del HTML generado
|
||||
puts "\n📝 Ejemplo de HTML generado:"
|
||||
ejemplo = contenido.lines.grep(/tl-item/).first(3)
|
||||
ejemplo.each { |line| puts " #{line.strip}" }
|
||||
else
|
||||
puts "ℹ️ No se generarían cambios"
|
||||
end
|
||||
end
|
||||
|
||||
def mostrar_resumen
|
||||
puts "\n" + "=" * 70
|
||||
puts "📊 RESUMEN DE LA ACTUALIZACIÓN"
|
||||
puts "=" * 70
|
||||
|
||||
if @opciones[:dry_run]
|
||||
puts "🔍 MODO SIMULACIÓN - No se aplicaron cambios reales"
|
||||
else
|
||||
puts "🔄 CAMBIOS APLICADOS:"
|
||||
if @cambios.any?
|
||||
puts "✅ #{@cambios.size} hitos agregados exitosamente"
|
||||
@cambios.each { |h| puts " • #{h}" }
|
||||
else
|
||||
puts "ℹ️ No se agregaron hitos nuevos"
|
||||
end
|
||||
|
||||
if @backup_path
|
||||
puts "💾 Backup creado en: #{@backup_path}"
|
||||
end
|
||||
end
|
||||
|
||||
if @errores.any?
|
||||
puts "\n⚠️ ERRORES ENCONTRADOS (#{@errores.size}):"
|
||||
@errores.each_with_index do |error, i|
|
||||
puts " #{i + 1}. #{error}"
|
||||
end
|
||||
end
|
||||
|
||||
puts "\n🚀 PRÓXIMOS PASOS SUGERIDOS:"
|
||||
puts " 1. Verificar dashboard en: https://ns8.frlr.utn.edu.ar/bitacoras/p2601"
|
||||
puts " 2. Revisar que los nuevos hitos se muestren correctamente"
|
||||
puts " 3. Actualizar estado de hitos según progreso real"
|
||||
puts " 4. Integrar con herramientas ADN para seguimiento automático"
|
||||
puts " 5. Documentar cambios en bitácora del proyecto"
|
||||
|
||||
puts "\n📅 " + Time.now.strftime('%Y-%m-%d %H:%M:%S')
|
||||
puts "=" * 70
|
||||
end
|
||||
end
|
||||
|
||||
# Parsear opciones de línea de comandos
|
||||
if __FILE__ == $0
|
||||
opciones = {
|
||||
dry_run: false,
|
||||
backup: true,
|
||||
hitos: nil,
|
||||
categoria: nil,
|
||||
debug: false
|
||||
}
|
||||
|
||||
OptionParser.new do |opts|
|
||||
opts.banner = "Uso: ruby tools/actualizar_dashboard_p2601.rb [OPCIONES]"
|
||||
|
||||
opts.on("--dry-run", "Mostrar cambios sin aplicar") do
|
||||
opciones[:dry_run] = true
|
||||
end
|
||||
|
||||
opts.on("--no-backup", "No crear backup antes de modificar") do
|
||||
opciones[:backup] = false
|
||||
end
|
||||
|
||||
opts.on("--hitos N", Integer, "Agregar solo N hitos (por defecto: todos)") do |n|
|
||||
opciones[:hitos] = n
|
||||
end
|
||||
|
||||
opts.on("--categoria CAT", String, "Filtrar por categoría (infraestructura, monitoreo, etc.)") do |cat|
|
||||
opciones[:categoria] = cat
|
||||
end
|
||||
|
||||
opts.on("--debug", "Mostrar información de depuración") do
|
||||
opciones[:debug] = true
|
||||
end
|
||||
|
||||
opts.on("-h", "--help", "Mostrar ayuda") do
|
||||
puts opts
|
||||
puts "\nEjemplos:"
|
||||
puts " ruby tools/actualizar_dashboard_p2601.rb --dry-run"
|
||||
puts " ruby tools/actualizar_dashboard_p2601.rb --hitos 3 --categoria infraestructura"
|
||||
puts " ruby tools/actualizar_dashboard_p2601.rb --no-backup"
|
||||
exit
|
||||
end
|
||||
end.parse!
|
||||
|
||||
# Ejecutar actualizador
|
||||
begin
|
||||
actualizador = ActualizadorDashboardP2601.new(opciones)
|
||||
actualizador.ejecutar
|
||||
rescue => e
|
||||
puts "❌ Error fatal: #{e.message}"
|
||||
puts e.backtrace if opciones[:debug]
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user