[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
This commit is contained in:
Ricardo Monla
2026-04-14 20:06:06 -03:00
parent bc1f42db1f
commit 892019fa22
59 changed files with 5438 additions and 42 deletions
@@ -0,0 +1,206 @@
#!/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