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

541 lines
17 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/sincronizar.rb
# ==========================================
# Herramienta para sincronizar el dashboard local con producción
# Filosofía: "Menos es más" - herramienta pequeña, específica y reutilizable
require 'fileutils'
require 'time'
class DashboardSynchronizer
# Configuración de rutas
CONFIG = {
local: {
dashboard_dir: File.expand_path('../../../../dashboard', __FILE__),
index_file: 'index.html',
backups_dir: 'backups'
},
production: {
dashboard_dir: '/var/www/html/P2601',
index_file: 'index.html',
backups_dir: '/var/www/html/P2601/backups'
}
}.freeze
def initialize(options = {})
@dry_run = options[:dry_run] || false
@verbose = options[:verbose] || false
@backup = options[:backup] || true
@timestamp = Time.now.strftime('%Y%m%d_%H%M%S')
end
def sincronizar_a_produccion
puts "🔄 SINCRONIZANDO DASHBOARD A PRODUCCIÓN"
puts "=" * 60
# Verificar rutas locales
unless verificar_ruta_local
puts "❌ Error: No se puede acceder al dashboard local"
return false
end
# Verificar rutas de producción
unless verificar_ruta_produccion
puts "❌ Error: No se puede acceder al dashboard de producción"
puts " Ruta: #{CONFIG[:production][:dashboard_dir]}"
return false
end
# Crear backup en producción si está habilitado
if @backup && !@dry_run
unless crear_backup_produccion
puts "❌ Error: No se pudo crear backup en producción"
return false
end
end
# Sincronizar archivos
puts "📁 Sincronizando archivos..."
archivos_sincronizados = sincronizar_archivos
# Mostrar resumen
mostrar_resumen_sincronizacion(archivos_sincronizados)
archivos_sincronizados.any?
end
def verificar_estado
puts "🔍 VERIFICANDO ESTADO DE SINCRONIZACIÓN"
puts "=" * 60
estado = {
local: verificar_estado_local,
production: verificar_estado_produccion,
diferencias: comparar_archivos
}
mostrar_estado_verificacion(estado)
estado[:local][:valido] && estado[:production][:valido]
end
def listar_backups
puts "💾 LISTADO DE BACKUPS EN PRODUCCIÓN"
puts "=" * 60
backups_dir = CONFIG[:production][:backups_dir]
unless Dir.exist?(backups_dir)
puts "️ No existe el directorio de backups: #{backups_dir}"
return false
end
backups = Dir.glob(File.join(backups_dir, '*.html')).sort.reverse
if backups.empty?
puts "️ No se encontraron backups"
return true
end
puts "📂 Directorio: #{backups_dir}"
puts "📊 Total de backups: #{backups.size}"
puts
backups.each_with_index do |backup, i|
nombre = File.basename(backup)
tamano = File.size(backup) / 1024
modificacion = File.mtime(backup).strftime('%Y-%m-%d %H:%M:%S')
puts " #{i + 1}. #{nombre}"
puts " • Tamaño: #{tamano} KB"
puts " • Modificado: #{modificacion}"
puts
end
true
end
def restaurar_backup(nombre_backup = nil)
puts "🔄 RESTAURANDO BACKUP DESDE PRODUCCIÓN"
puts "=" * 60
backups_dir = CONFIG[:production][:backups_dir]
unless Dir.exist?(backups_dir)
puts "❌ Error: No existe el directorio de backups: #{backups_dir}"
return false
end
# Si no se especifica backup, listar disponibles
unless nombre_backup
backups = Dir.glob(File.join(backups_dir, '*.html')).sort.reverse
if backups.empty?
puts "❌ Error: No hay backups disponibles para restaurar"
return false
end
puts "📋 Backups disponibles:"
backups.each_with_index do |backup, i|
puts " #{i + 1}. #{File.basename(backup)}"
end
puts "\n️ Use: #{$0} restaurar NOMBRE_BACKUP"
return false
end
# Verificar que el backup existe
ruta_backup = File.join(backups_dir, nombre_backup)
unless File.exist?(ruta_backup)
puts "❌ Error: Backup no encontrado: #{nombre_backup}"
puts " Ruta: #{ruta_backup}"
return false
end
# Crear backup del estado actual local
if @backup && !@dry_run
puts "💾 Creando backup del estado local actual..."
crear_backup_local
end
# Restaurar backup
puts "📥 Restaurando backup: #{nombre_backup}"
archivo_local = File.join(CONFIG[:local][:dashboard_dir], CONFIG[:local][:index_file])
if @dry_run
puts " [DRY RUN] Se restauraría #{ruta_backup} a #{archivo_local}"
puts " [DRY RUN] Tamaño: #{File.size(ruta_backup)} bytes"
else
begin
FileUtils.cp(ruta_backup, archivo_local)
puts "✅ Backup restaurado exitosamente"
puts " • Origen: #{ruta_backup}"
puts " • Destino: #{archivo_local}"
puts " • Tamaño: #{File.size(archivo_local)} bytes"
rescue => e
puts "❌ Error al restaurar backup: #{e.message}"
return false
end
end
true
end
private
def verificar_ruta_local
dir = CONFIG[:local][:dashboard_dir]
archivo = File.join(dir, CONFIG[:local][:index_file])
unless Dir.exist?(dir)
puts "❌ Directorio local no existe: #{dir}"
return false
end
unless File.exist?(archivo)
puts "❌ Archivo local no existe: #{archivo}"
return false
end
true
end
def verificar_ruta_produccion
dir = CONFIG[:production][:dashboard_dir]
unless Dir.exist?(dir)
puts "❌ Directorio de producción no existe: #{dir}"
return false
end
# Verificar permisos de escritura
unless File.writable?(dir)
puts "❌ Sin permisos de escritura en: #{dir}"
return false
end
true
end
def crear_backup_produccion
puts "💾 Creando backup en producción..."
backups_dir = CONFIG[:production][:backups_dir]
archivo_produccion = File.join(CONFIG[:production][:dashboard_dir], CONFIG[:production][:index_file])
# Crear directorio de backups si no existe
unless Dir.exist?(backups_dir)
puts " Creando directorio de backups: #{backups_dir}"
FileUtils.mkdir_p(backups_dir)
end
# Nombre del backup
nombre_backup = "dashboard_prod_#{@timestamp}.html"
ruta_backup = File.join(backups_dir, nombre_backup)
# Crear backup
begin
FileUtils.cp(archivo_produccion, ruta_backup)
puts "✅ Backup creado: #{nombre_backup}"
puts " • Ruta: #{ruta_backup}"
puts " • Tamaño: #{File.size(ruta_backup)} bytes"
true
rescue => e
puts "❌ Error al crear backup: #{e.message}"
false
end
end
def crear_backup_local
backups_dir = File.join(CONFIG[:local][:dashboard_dir], CONFIG[:local][:backups_dir])
archivo_local = File.join(CONFIG[:local][:dashboard_dir], CONFIG[:local][:index_file])
# Crear directorio de backups si no existe
unless Dir.exist?(backups_dir)
FileUtils.mkdir_p(backups_dir)
end
# Nombre del backup
nombre_backup = "dashboard_local_#{@timestamp}.html"
ruta_backup = File.join(backups_dir, nombre_backup)
# Crear backup
FileUtils.cp(archivo_local, ruta_backup)
end
def sincronizar_archivos
archivos_sincronizados = []
# Archivo principal (index.html)
archivo_local = File.join(CONFIG[:local][:dashboard_dir], CONFIG[:local][:index_file])
archivo_produccion = File.join(CONFIG[:production][:dashboard_dir], CONFIG[:production][:index_file])
puts "📄 Sincronizando: #{CONFIG[:local][:index_file]}"
puts " • Local: #{archivo_local}"
puts " • Producción: #{archivo_produccion}"
# Verificar si hay diferencias
if archivos_iguales?(archivo_local, archivo_produccion)
puts " ️ Los archivos son idénticos, no se requiere sincronización"
else
if @dry_run
puts " [DRY RUN] Se copiaría #{archivo_local} a #{archivo_produccion}"
puts " [DRY RUN] Diferencia de tamaño: #{File.size(archivo_local) - File.size(archivo_produccion)} bytes"
else
begin
FileUtils.cp(archivo_local, archivo_produccion)
puts " ✅ Sincronizado exitosamente"
archivos_sincronizados << CONFIG[:local][:index_file]
rescue => e
puts " ❌ Error al sincronizar: #{e.message}"
end
end
end
# Buscar otros archivos en el directorio local
Dir.glob(File.join(CONFIG[:local][:dashboard_dir], '*')).each do |archivo_local|
next if File.directory?(archivo_local)
next if File.basename(archivo_local) == CONFIG[:local][:index_file]
archivo_produccion = File.join(CONFIG[:production][:dashboard_dir], File.basename(archivo_local))
puts "📄 Sincronizando: #{File.basename(archivo_local)}"
if @dry_run
puts " [DRY RUN] Se copiaría #{archivo_local} a #{archivo_produccion}"
else
begin
FileUtils.cp(archivo_local, archivo_produccion)
puts " ✅ Sincronizado exitosamente"
archivos_sincronizados << File.basename(archivo_local)
rescue => e
puts " ❌ Error al sincronizar: #{e.message}"
end
end
end
archivos_sincronizados
end
def archivos_iguales?(archivo1, archivo2)
return false unless File.exist?(archivo1) && File.exist?(archivo2)
return false unless File.size(archivo1) == File.size(archivo2)
# Comparación simple por tamaño y contenido
File.read(archivo1) == File.read(archivo2)
end
def verificar_estado_local
dir = CONFIG[:local][:dashboard_dir]
archivo = File.join(dir, CONFIG[:local][:index_file])
{
valido: File.exist?(archivo),
ruta: dir,
archivo: CONFIG[:local][:index_file],
existe: File.exist?(archivo),
tamano: File.exist?(archivo) ? File.size(archivo) : 0,
modificacion: File.exist?(archivo) ? File.mtime(archivo).strftime('%Y-%m-%d %H:%M:%S') : 'N/A'
}
end
def verificar_estado_produccion
dir = CONFIG[:production][:dashboard_dir]
archivo = File.join(dir, CONFIG[:production][:index_file])
{
valido: File.exist?(archivo),
ruta: dir,
archivo: CONFIG[:production][:index_file],
existe: File.exist?(archivo),
tamano: File.exist?(archivo) ? File.size(archivo) : 0,
modificacion: File.exist?(archivo) ? File.mtime(archivo).strftime('%Y-%m-%d %H:%M:%S') : 'N/A',
escribible: File.writable?(dir)
}
end
def comparar_archivos
archivo_local = File.join(CONFIG[:local][:dashboard_dir], CONFIG[:local][:index_file])
archivo_produccion = File.join(CONFIG[:production][:dashboard_dir], CONFIG[:production][:index_file])
return {} unless File.exist?(archivo_local) && File.exist?(archivo_produccion)
{
iguales: archivos_iguales?(archivo_local, archivo_produccion),
tamano_local: File.size(archivo_local),
tamano_produccion: File.size(archivo_produccion),
diferencia_tamano: File.size(archivo_local) - File.size(archivo_produccion),
modificacion_local: File.mtime(archivo_local),
modificacion_produccion: File.mtime(archivo_produccion),
mas_reciente: File.mtime(archivo_local) > File.mtime(archivo_produccion) ? 'local' : 'produccion'
}
end
def mostrar_resumen_sincronizacion(archivos_sincronizados)
puts "\n📊 RESUMEN DE SINCRONIZACIÓN"
puts "=" * 60
if @dry_run
puts "🔍 MODO SIMULACIÓN (DRY RUN)"
puts " No se realizaron cambios reales"
end
if archivos_sincronizados.empty?
puts "️ No se sincronizaron archivos"
puts " • Los archivos ya están sincronizados"
puts " • O hubo errores en la sincronización"
else
puts "✅ Archivos sincronizados: #{archivos_sincronizados.size}"
archivos_sincronizados.each do |archivo|
puts " • #{archivo}"
end
end
# Mostrar URLs
puts "\n🔗 URLs DEL DASHBOARD:"
puts " • Local: file://#{CONFIG[:local][:dashboard_dir]}/#{CONFIG[:local][:index_file]}"
puts " • Producción: https://ns8.frlr.utn.edu.ar/P2601/"
puts " • Bitácoras: https://ns8.frlr.utn.edu.ar/bitacoras/p2601"
puts "\n💡 RECOMENDACIONES:"
puts " 1. Verificar cambios en producción: https://ns8.frlr.utn.edu.ar/P2601/"
puts " 2. Usar 'verificar' para validar la sincronización"
puts " 3. Revisar backups si es necesario restaurar"
end
def mostrar_estado_verificacion(estado)
puts "🏠 ESTADO LOCAL:"
if estado[:local][:valido]
puts " ✅ Dashboard local válido"
puts " • Ruta: #{estado[:local][:ruta]}"
puts " • Archivo: #{estado[:local][:archivo]}"
puts " • Tamaño: #{estado[:local][:tamano]} bytes"
puts " • Modificación: #{estado[:local][:modificacion]}"
else
puts " ❌ Dashboard local no válido"
end
puts "\n🏭 ESTADO PRODUCCIÓN:"
if estado[:production][:valido]
puts " ✅ Dashboard de producción válido"
puts " • Ruta: #{estado[:production][:ruta]}"
puts " • Archivo: #{estado[:production][:archivo]}"
puts " • Tamaño: #{estado[:production][:tamano]} bytes"
puts " • Modificación: #{estado[:production][:modificacion]}"
puts " • Escribible: #{estado[:production][:escribible] ? '✅ Sí' : '❌ No'}"
else
puts " ❌ Dashboard de producción no válido"
end
if estado[:diferencias].any?
puts "\n🔍 COMPARACIÓN DE ARCHIVOS:"
if estado[:diferencias][:iguales]
puts " ✅ Los archivos son idénticos"
else
puts " ⚠️ Los archivos son diferentes"
puts " • Tamaño local: #{estado[:diferencias][:tamano_local]} bytes"
puts " • Tamaño producción: #{estado[:diferencias][:tamano_produccion]} bytes"
puts " • Diferencia: #{estado[:diferencias][:diferencia_tamano]} bytes"
puts " • Más reciente: #{estado[:diferencias][:mas_reciente]}"
end
end
puts "\n💡 RECOMENDACIONES:"
if estado[:local][:valido] && estado[:production][:valido]
if estado[:diferencias][:iguales]
puts " ✅ Los dashboards están sincronizados"
puts " • No se requiere acción"
else
puts " ⚠️ Los dashboards están desincronizados"
puts " • Use 'sincronizar' para actualizar producción"
puts " • Use 'sincronizar --dry-run' para simular"
end
else
puts " ❌ Hay problemas con los dashboards"
puts " • Verifique las rutas y permisos"
end
end
end
# Interfaz de línea de comandos
if __FILE__ == $0
begin
synchronizer = DashboardSynchronizer.new
# Parsear argumentos
command = ARGV[0] || 'verificar'
options = {
dry_run: ARGV.include?('--dry-run') || ARGV.include?('-n'),
verbose: ARGV.include?('--verbose') || ARGV.include?('-v'),
backup: !ARGV.include?('--no-backup')
}
# Extraer argumentos adicionales
extra_args = (ARGV[1..-1] || []).reject { |arg| arg.start_with?('--') || arg == '-n' || arg == '-v' }
case command
when 'sincronizar', 'sync'
synchronizer = DashboardSynchronizer.new(options)
success = synchronizer.sincronizar_a_produccion
when 'verificar', 'check'
success = synchronizer.verificar_estado
when 'backups', 'list-backups'
success = synchronizer.listar_backups
when 'restaurar', 'restore'
nombre_backup = extra_args[0]
synchronizer = DashboardSynchronizer.new(options)
success = synchronizer.restaurar_backup(nombre_backup)
when 'help', '--help', '-h'
puts "📖 USO:"
puts " #{$0} [comando] [argumentos...]"
puts
puts "COMANDOS:"
puts " sincronizar, sync - Sincronizar dashboard local a producción"
puts " verificar, check - Verificar estado de sincronización"
puts " backups, list-backups - Listar backups en producción"
puts " restaurar, restore [NOMBRE] - Restaurar backup desde producción"
puts " help - Mostrar esta ayuda"
puts
puts "OPCIONES:"
puts " --dry-run, -n - Simular sin hacer cambios"
puts " --verbose, -v - Mostrar detalles adicionales"
puts " --no-backup - No crear backups automáticos"
puts
puts "EJEMPLOS:"
puts " #{$0} sincronizar"
puts " #{$0} sincronizar --dry-run"
puts " #{$0} verificar"
puts " #{$0} backups"
puts " #{$0} restaurar dashboard_prod_20240306_114500.html"
puts
puts "🔗 URLs IMPORTANTES:"
puts " • Producción: https://ns8.frlr.utn.edu.ar/P2601/"
puts " • Bitácoras: https://ns8.frlr.utn.edu.ar/bitacoras/p2601"
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 cambios en: https://ns8.frlr.utn.edu.ar/P2601/"
puts " 2. Usar herramientas de orquestador para gestión continua"
puts " 3. Revisar backups periódicamente"
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