Files
dtic-DIIAA/adn/tools/cli/drones/orquestador_pipeline_diferencial.rb
Ricardo Monla 892019fa22 [A04.P005] DASUTEN: Pipeline refactorizado + Documentación + Informes
Refactorización del pipeline de backups DASUTEN:

1. Flujo simplificado (un solo destino):
   - Backup directo a X:\ (share srv-ns8)
   - Eliminado paso intermedio E:\BK_SQL\sysdasuten\
   - Reducido de 3 ubicaciones a 2 (X: → /var/tmp/)

2. Módulo común DasuExecutor (lib/dasu_executor.rb):
   - Centraliza ejecución PowerShell en dasu-sql4
   - Centraliza verificación de archivos en fenix
   - Elimina código repetido en 6 drones (-52% líneas)

3. Drones refactorizados:
   - dasuten_exportar_srvv-fenix.rb (FULL → X:)
   - dasuten_exportar_diferencial_srvv-fenix.rb (DIF → X:)
   - dasuten_transferir_srvv-fenix-srv-ns8.rb (simplificado)
   - dasuten_restaurar_dasu-sql4.rb (usa DasuExecutor)
   - dasuten_restaurar_diferencial_dasu-sql4.rb (usa DasuExecutor)
   - dasuten_download_drive-dasu-sql4.rb (usa DasuExecutor)
   - dasuten_verificar-integridad_dasu-sql4.rb (usa DasuExecutor)

4. Integración BKPs mantenida:
   - T8-T15: Tareas individuales
   - C7: dasuten_full (pipeline semanal)
   - C8: dasuten_diferencial (pipeline diario)

5. Documentación actualizada:
   - A04.P006_Backups-DASUTEN.md: flujo simplificado + módulo común
   - Lecciones aprendidas agregadas

6. Nuevos archivos:
   - Informes técnicos DASUTEN (docs/informes/2026-04_DASUTEN-migracion/)
   - A01.P011/P012: Documentación de drones y pipeline
   - proc_linux.rb, ops.rb: Utilitarios del ecosistema

7. Scripts legacy (para referencia):
   - orquestador_pipeline.rb, orquestador_pipeline_diferencial.rb
   - dasuten_upload_srv-ns8-drive.rb
2026-04-14 20:06:06 -03:00

207 lines
7.0 KiB
Ruby
Raw Permalink 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
# ==========================================================
# adn/tools/cli/drones/orquestador_pipeline_diferencial.rb
# ==========================================================
# Orquestador de Pipeline DIFERENCIAL DASUTEN
# ==========================================================
# Flujo: exportar_dif → transferir → upload → download → restaurar_dif → verificar
# Para refrescos diarios (solo cambios desde último backup completo)
# ==========================================================
require 'optparse'
require 'json'
require 'time'
require 'shellwords'
CLI_DIR = File.expand_path(__dir__)
ADN_RUN = File.join(File.dirname(File.dirname(File.dirname(CLI_DIR))), 'tools', 'run')
# Configuración
opciones = {
paso_inicial: 'exportar_dif',
dry_run: false,
timeout: 900, # 15 min por dron (suficiente para diferencial)
nodo_bitacora: 'dasuten-pipeline-dif'
}
OptionParser.new do |opts|
opts.banner = "Uso: #{ADN_RUN} dron lanzar --nota 'Pipeline Diferencial DASUTEN' -- ruby adn/tools/cli/drones/orquestador_pipeline_diferencial.rb [opciones]"
opts.on("--paso PASO", %w[exportar_dif transferir upload download restaurar_dif verificar], "Comenzar desde este paso") { |p| opciones[:paso_inicial] = p }
opts.on("--dry-run", "Simulación sin ejecutar drones") { opciones[:dry_run] = true }
opts.on("--timeout N", Integer, "Timeout por dron en segundos") { |t| opciones[:timeout] = t }
end.parse!(ARGV)
# Estados del pipeline diferencial (6 drones atómicos)
ESTADOS = {
exportar_dif: {
script: 'dasuten_exportar_diferencial_srvv-fenix.rb',
nodo: 'srvv-fenix',
nota: '📤 Exportar backup DIFERENCIAL DASUTEN',
output_file: '/tmp/dron_export_dif_output.json',
siguiente: :transferir
},
transferir: {
script: 'dasuten_transferir_srvv-fenix-srv-ns8.rb',
nodo: 'srv-ns8',
nota: '🔄 Transferir SMB (fenix → ns8)',
output_file: '/tmp/dron_transfer_output.json',
siguiente: :upload
},
upload: {
script: 'dasuten_upload_srv-ns8-drive.rb',
nodo: 'srv-ns8',
nota: '☁️ Upload a Google Drive',
output_file: '/tmp/dron_upload_output.json',
siguiente: :download
},
download: {
script: 'dasuten_download_drive-dasu-sql4.rb',
nodo: 'dasu-sql4',
nota: '📥 Download desde Google Drive',
output_file: '/tmp/dron_download_output.json',
siguiente: :restaurar_dif
},
restaurar_dif: {
script: 'dasuten_restaurar_diferencial_dasu-sql4.rb',
nodo: 'dasu-sql4',
nota: '💾 Restaurar backup DIFERENCIAL',
output_file: '/tmp/dron_restore_dif_output.json',
siguiente: :verificar
},
verificar: {
script: 'dasuten_verificar-integridad_dasu-sql4.rb',
nodo: 'dasu-sql4',
nota: '✅ Verificar integridad BD',
output_file: '/tmp/dron_verify_output.json',
siguiente: nil # Final
}
}.freeze
puts "\n" + "="*70
puts "🛸 ORQUESTADOR PIPELINE DIFERENCIAL DASUTEN (Refresco diario)"
puts "="*70
puts " Paso inicial: #{opciones[:paso_inicial]}"
puts " Dry-run: #{opciones[:dry_run]}"
puts " Timeout: #{opciones[:timeout]}s por dron"
puts " Drones: #{ESTADOS.keys.join(' → ')}"
puts "="*70
puts ""
puts " ️ Este pipeline usa backup DIFERENCIAL (solo cambios)"
puts " Esperado: ~5-10 min total (vs ~35 min del completo)"
puts "="*70
# ──────────────────────────────────────────────────────────────
# Función para lanzar un dron y esperar su resultado
# ──────────────────────────────────────────────────────────────
def lanzar_dron(script, nodo, nota, timeout, dry_run)
cmd = "ruby #{File.join(File.dirname(__FILE__), script)}"
puts "\n" + "-"*70
puts "🛸 Lanzando dron: #{nota}"
puts " Nodo: #{nodo}"
puts " Script: #{script}"
puts "-"*70
# Lanzar dron ejecutor
dron_cmd = "#{ADN_RUN} dron lanzar --nota #{Shellwords.escape(nota)} --nodo #{Shellwords.escape(nodo)} --timeout #{timeout} -- #{cmd}"
puts " Comando: #{dron_cmd}"
if dry_run
puts " [DRY-RUN] No ejecutado"
return true, {}
end
exito = system(dron_cmd)
codigo = $?.exitstatus
if exito
puts "\n✅ Dron completado exitosamente"
# Leer output del dron (buscar en todos los archivos posibles)
output_files = ['/tmp/dron_export_dif_output.json', '/tmp/dron_transfer_output.json',
'/tmp/dron_upload_output.json', '/tmp/dron_download_output.json',
'/tmp/dron_restore_dif_output.json', '/tmp/dron_verify_output.json']
output_file = output_files.find { |f| File.exist?(f) }
if output_file && File.exist?(output_file)
resultado = JSON.parse(File.read(output_file))
puts " Output: #{resultado.inspect}"
return true, resultado
else
puts " [!] No se encontró archivo de output"
return true, {}
end
else
puts "\n❌ Dron fallido (código: #{codigo})"
return false, {}
end
end
# ──────────────────────────────────────────────────────────────
# Ejecutar pipeline
# ──────────────────────────────────────────────────────────────
paso_actual = opciones[:paso_inicial].to_sym
contexto = {} # Para pasar datos entre drones
duraciones = []
begin
loop do
break unless ESTADOS[paso_actual]
config = ESTADOS[paso_actual]
# Lanzar dron
exito, resultado = lanzar_dron(
config[:script],
config[:nodo],
config[:nota],
opciones[:timeout],
opciones[:dry_run]
)
unless exito
puts "\n❌ Pipeline fallido en paso: #{paso_actual}"
exit 1
end
# Guardar contexto para siguiente paso
contexto.merge!(resultado) if resultado
duraciones << resultado[:duracion] if resultado[:duracion]
# Decidir siguiente paso
if config[:siguiente]
puts "\n➡️ Pasando a: #{config[:siguiente]}"
paso_actual = config[:siguiente]
else
puts "\n✅ Pipeline completado exitosamente!"
break
end
end
# Resumen final
puts "\n" + "="*70
puts "📊 RESUMEN DEL PIPELINE DIFERENCIAL"
puts "="*70
if contexto[:pipeline_completo] || contexto[:estado_integridad] == 'OK'
puts " Estado: ✅ COMPLETADO"
puts " Integridad BD: #{contexto[:estado_integridad] || 'N/A'}"
else
puts " Estado: ⏳ EN PROGRESO"
end
puts " Backup Drive: #{contexto[:drive_ruta] || contexto[:drive_origen] || 'N/A'}"
puts " Duración total: #{duraciones.sum.round(2)}s (#{duraciones.count} drones)"
puts " Tipo: #{contexto[:backup_tipo] || 'differential'}"
puts "="*70
rescue => e
puts "\n❌ Error en orquestador: #{e.message}"
puts e.backtrace.first(5).join("\n")
exit 1
end