#!/usr/bin/env ruby # frozen_string_literal: true # ========================================================== # adn/tools/cli/dasuten_orquestador.rb — Orquestador Fase 8b # ========================================================== # Flujo completo: srvv-fenix → Google Drive → dasu-sql4 # Uso: ruby adn/tools/cli/dasuten_orquestador.rb [upload|download|restore] # ========================================================== require 'optparse' require_relative 'dron/dispatcher' PROJECT_ROOT = File.expand_path('../..', __dir__) CLI_DIR = File.join(PROJECT_ROOT, 'adn', 'tools', 'cli') ADN_RUN = File.join(PROJECT_ROOT, 'adn', 'tools', 'run') opciones = { paso: 'all', dry_run: false, nodo: nil } OptionParser.new do |opts| opts.banner = "Uso: #{ADN_RUN} dasuten_orquestador [opciones]" opts.on("--paso PASO", %w[upload download restore all], "Ejecutar solo este paso (upload|download|restore|all)") { |p| opciones[:paso] = p } opts.on("--nodo NOMBRE", "Nodo para bitácora del dron") { |n| opciones[:nodo] = n } opts.on("--dry-run", "Simulación sin ejecutar") { opciones[:dry_run] = true } end.parse!(ARGV) puts "\n#{'='*60}" puts "🛸 ORQUESTADOR DASUTEN — Fase 8b (Refresco BD vía Google Drive)" puts '='*60 puts " Paso seleccionado: #{opciones[:paso]}" puts " Dry-run: #{opciones[:dry_run]}" puts " Nodo: #{opciones[:nodo] || 'auto-detect'}" puts '='*60 def lanzar_dron(cmd, nota, nodo: nil) puts "\n🛸 Lanzando dron: #{nota}" puts " Comando: #{cmd}" args = ["lanzar", "--nota", nota] args << "--nodo" << nodo if nodo args << "--" << cmd # Ejecutar dron directamente require_relative 'dron/ejecutor' resultado = Dron::Ejecutor.lanzar( cmd: cmd, nota: nota, nodo: nodo, timeout: 1800 # 30 minutos max ) resultado end caso = opciones[:paso] # ────────────────────────────────────────────────────────────── # PASO 1: UPLOAD desde srvv-fenix a Google Drive # ────────────────────────────────────────────────────────────── if caso == 'all' || caso == 'upload' puts "\n" + "─"*60 puts "📤 PASO 1: Upload desde srvv-fenix → Google Drive" puts "─"*60 cmd_upload = "ruby #{CLI_DIR}/dasuten_backup_upload.rb" if opciones[:dry_run] puts "[DRY-RUN] Ejecutaría: #{cmd_upload}" else resultado = lanzar_dron(cmd_upload, "📤 Upload backup DASUTEN a Drive", nodo: 'srvv-fenix') if resultado[:exit_code] != 0 puts "\n❌ Upload fallido. Deteniendo orquestación." exit 1 end end end # ────────────────────────────────────────────────────────────── # PASO 2: DOWNLOAD desde Google Drive a dasu-sql4 # ────────────────────────────────────────────────────────────── if caso == 'all' || caso == 'download' puts "\n" + "─"*60 puts "📥 PASO 2: Download desde Google Drive → dasu-sql4" puts "─"*60 cmd_download = "ruby #{CLI_DIR}/dasuten_backup_download.rb" if opciones[:dry_run] puts "[DRY-RUN] Ejecutaría: #{cmd_download}" else resultado = lanzar_dron(cmd_download, "📥 Download backup DASUTEN desde Drive", nodo: 'dasu-sql4') if resultado[:exit_code] != 0 puts "\n❌ Download fallido. Deteniendo orquestación." exit 1 end end end # ────────────────────────────────────────────────────────────── # PASO 3: RESTORE en dasu-sql4 # ────────────────────────────────────────────────────────────── if caso == 'all' || caso == 'restore' puts "\n" + "─"*60 puts "💾 PASO 3: Restore + DBCC CHECKDB en dasu-sql4" puts "─"*60 cmd_restore = <<~SQL.gsub("\n", ' ').strip powershell -Command " $query = @' RESTORE DATABASE sysdasuten FROM DISK = 'F:\\BACKUP\\sysdasuten_compressed_ADN.bak' WITH REPLACE, MOVE 'sysdasuten' TO 'F:\\DATA\\sysdasuten.mdf', MOVE 'sysdasuten_log' TO 'F:\\LOG\\sysdasuten_log.ldf'; DBCC CHECKDB('sysdasuten'); '@ sqlcmd -S localhost -Q $query " SQL if opciones[:dry_run] puts "[DRY-RUN] Ejecutaría: #{cmd_restore}" else resultado = lanzar_dron(cmd_restore, "💾 Restore + CHECKDB DASUTEN", nodo: 'dasu-sql4') if resultado[:exit_code] != 0 puts "\n❌ Restore fallido. Verificar logs." exit 1 end end end puts "\n" + "═"*60 puts "✅ Orquestación completada exitosamente!" puts "═"*60