#!/usr/bin/env ruby # frozen_string_literal: true # ========================================================== # adn/tools/cli/drones/dasuten_exportar_srvv-fenix.rb # ========================================================== # Dron: Exportar backup en srvv-fenix # Flujo: srvv-fenix → Backup comprimido en E:\BK_SQL\ # Salida: Marca evento como 'export_complete' con ruta del backup # ========================================================== require 'tiny_tds' require 'time' require 'json' # Nombre del archivo con timestamp TIMESTAMP = Time.now.strftime('%Y%m%d_%H%M%S') BACKUP_FILE = "sysdasuten_FULL_#{TIMESTAMP}.bak" # Directorio local en fenix BACKUP_DIR_FENIX = "E:\\BK_SQL\\sysdasuten" BACKUP_LOCAL = "#{BACKUP_DIR_FENIX}\\#{BACKUP_FILE}" # Unidad de red mapeada en srvv-fenix (share de srv-ns8) BACKUP_DIR_NS8 = "X:" BACKUP_NS8 = "#{BACKUP_DIR_NS8}\\#{BACKUP_FILE}" puts "[*] Dron: Exportar backup COMPLETO DASUTEN" puts " Nodo: srvv-fenix" puts " Destino local: #{BACKUP_LOCAL}" puts " Destino share (X:): #{BACKUP_NS8}" puts " Tipo: FULL (completo, con copia en doble ubicación)" begin # Autorizar candados primero (necesario para acceder a la bóveda) system("ruby adn/tools/candados/candados.rb authorize >/dev/null 2>&1") # Obtener credencial de candados (patrón usado en otros drones) pass_cmd = "ruby adn/tools/candados/candados.rb get utnlarioja.intranet:monlaricardo 2>/dev/null" sql_pass = `#{pass_cmd}`.strip sql_user = 'UTNlarioja\\monlaricardo' sql_host = '10.0.10.200' if sql_pass.empty? puts "[!] Error: No se pudo obtener credencial de candados (utnlarioja.intranet:monlaricardo)" exit 1 end client = TinyTds::Client.new( username: sql_user, password: sql_pass, host: sql_host, timeout: 600 ) puts "[+] Conectado a SQL Server en #{sql_host} (auth: Windows)" # Crear directorio local si no existe puts "\n[0/4] Verificando directorios..." mkdir_sql = "EXEC master.sys.xp_create_subdir '#{BACKUP_DIR_FENIX}'" client.execute(mkdir_sql) puts "[+] Directorio local asegurado: #{BACKUP_DIR_FENIX}" # Generar backup comprimido en disco local puts "[1/4] Generando backup en disco local..." start = Time.now result = client.execute("BACKUP DATABASE sysdasuten TO DISK = '#{BACKUP_LOCAL}' WITH INIT, COMPRESSION, STATS=10") result.each { |r| puts " #{r['message']}" if r['message'] } duracion_local = (Time.now - start).round(2) puts "[+] Backup local generado en #{duracion_local} segundos" # Verificar archivo local puts "[2/4] Verificando archivo local..." file_result = client.execute("EXEC master.sys.xp_fileexist '#{BACKUP_LOCAL}'") puts "[+] Archivo local verificado: #{BACKUP_LOCAL}" # Copiar a unidad de red X: (share de srv-ns8) puts "[3/4] Copiando a unidad de red X: (srv-ns8)..." begin copy_ps = <<~PSCMD.strip Copy-Item -Path "#{BACKUP_LOCAL}" -Destination "#{BACKUP_NS8}" -Force Write-Host "Copiado: #{BACKUP_FILE} → X:\\#{BACKUP_FILE}" PSCMD client.execute("EXEC xp_cmdshell 'powershell -Command \"#{copy_ps.gsub('"', '\"')}\"'") puts "[+] Copia a share completada: #{BACKUP_NS8}" rescue => e puts "[!] Advertencia: No se pudo copiar a X: - #{e.message}" end # Verificar archivo en share puts "[4/4] Verificando archivo en share..." begin verify_ps = <<~PSCMD.strip if (Test-Path "#{BACKUP_NS8}") { $size = (Get-Item "#{BACKUP_NS8}").Length Write-Host "Verificado: #{BACKUP_NS8} ($([math]::Round($size/1MB, 2)) MB)" } else { Write-Host "ERROR: No existe #{BACKUP_NS8}" exit 1 } PSCMD client.execute("EXEC xp_cmdshell 'powershell -Command \"#{verify_ps.gsub('"', '\"')}\"'") rescue => e puts "[!] Advertencia: No se pudo verificar en X: - #{e.message}" end duracion_total = (Time.now - start).round(2) # Output para orquestador output = { paso: 'export_full_complete', backup_ruta_fenix: BACKUP_LOCAL, backup_ruta_ns8: BACKUP_NS8, backup_ruta_ns8_unix: "/mnt/ns8Disco2/bkps-dasuten/#{BACKUP_FILE}", backup_tipo: 'full', nodo: 'srvv-fenix', nodo_destino: 'srv-ns8', duracion_total: duracion_total, timestamp: Time.now.iso8601, siguiente_paso: 'upload_drive' } puts "\n✅ Export completado (doble ubicación: local + share)" puts " Output: #{output.to_json}" # Escribir output para que el orquestador lo lea File.write('/tmp/dron_export_output.json', output.to_json) rescue => e puts "[!] Error fatal: #{e.message}" exit 1 end