From 900457a778c45100eb5751610bcfb8150a56653a Mon Sep 17 00:00:00 2001 From: Ricardo Monla Date: Tue, 14 Apr 2026 19:44:13 -0300 Subject: [PATCH] =?UTF-8?q?[A04.P006]=20Exportar=20backup=20en=20doble=20u?= =?UTF-8?q?bicaci=C3=B3n=20(fenix=20+=20share=20ns8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cambios realizados: 1. dasuten_exportar_srvv-fenix.rb: - Genera backup en disco local: E:\BK_SQL\sysdasuten\ - Copia automáticamente a unidad de red X: (share srv-ns8) - Usa PowerShell para copiar al share mapeado - Verifica ambos destinos 2. dasuten_transferir_srvv-fenix-srv-ns8.rb: - Simplificado: ya no descarga desde fenix vía SMB - El backup ya está en /mnt/ns8Disco2/bkps-dasuten/ - Copia local: share → /var/tmp/ (para rclone upload) Flujo actualizado: 1. SQL Server → E:\BK_SQL\sysdasuten\archivo.bak (local fenix) 2. PowerShell → X:\archivo.bak (share srv-ns8) 3. Transfer dron → /var/tmp/archivo.bak (para rclone) 4. Upload dron → Google Drive Requisito en srvv-fenix: net use X: \\10.0.10.8\bkps-dasuten /user:rmonla Co-Authored-By: Claude Opus 4.6 --- .../cli/drones/dasuten_exportar_srvv-fenix.rb | 85 +++++++++++----- .../dasuten_transferir_srvv-fenix-srv-ns8.rb | 96 +++++++------------ 2 files changed, 96 insertions(+), 85 deletions(-) diff --git a/adn/tools/cli/drones/dasuten_exportar_srvv-fenix.rb b/adn/tools/cli/drones/dasuten_exportar_srvv-fenix.rb index e38de078..4cbe7a07 100755 --- a/adn/tools/cli/drones/dasuten_exportar_srvv-fenix.rb +++ b/adn/tools/cli/drones/dasuten_exportar_srvv-fenix.rb @@ -15,13 +15,21 @@ require 'json' # Nombre del archivo con timestamp TIMESTAMP = Time.now.strftime('%Y%m%d_%H%M%S') -BACKUP_DIR = "E:\\BK_SQL\\sysdasuten" -BACKUP_FILE = "#{BACKUP_DIR}\\sysdasuten_FULL_#{TIMESTAMP}.bak" +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: #{BACKUP_FILE}" -puts " Tipo: FULL (completo)" +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) @@ -47,43 +55,74 @@ begin ) puts "[+] Conectado a SQL Server en #{sql_host} (auth: Windows)" - # Crear directorio si no existe - puts "\n[0/3] Verificando directorio..." - mkdir_sql = "EXEC master.sys.xp_create_subdir '#{BACKUP_DIR}'" + # 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 asegurado: #{BACKUP_DIR}" + puts "[+] Directorio local asegurado: #{BACKUP_DIR_FENIX}" - # Generar backup comprimido - puts "[1/2] Generando backup comprimido..." + # 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_FILE}' WITH INIT, COMPRESSION, STATS=10") + 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 = (Time.now - start).round(2) - puts "[+] Backup generado en #{duracion} segundos" + duracion_local = (Time.now - start).round(2) + puts "[+] Backup local generado en #{duracion_local} segundos" - # Obtener tamaño del archivo (vía PowerShell remoto) - puts "[2/2] Verificando archivo..." - size_result = client.execute("EXEC master.sys.xp_fileexist '#{BACKUP_FILE}'") - file_info = client.execute("SELECT size = (SELECT size FROM sys.database_files WHERE name = 'sysdasuten')") + # 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}" - puts "[+] Archivo verificado: #{BACKUP_FILE}" + # 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: BACKUP_FILE, - backup_ruta_ns8: "/mnt/ns8Disco2/bkps-dasuten/#{File.basename(BACKUP_FILE)}", + 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: duracion, + duracion_total: duracion_total, timestamp: Time.now.iso8601, - siguiente_paso: 'transferir' + siguiente_paso: 'upload_drive' } - puts "\n✅ Export completado" + puts "\n✅ Export completado (doble ubicación: local + share)" puts " Output: #{output.to_json}" # Escribir output para que el orquestador lo lea diff --git a/adn/tools/cli/drones/dasuten_transferir_srvv-fenix-srv-ns8.rb b/adn/tools/cli/drones/dasuten_transferir_srvv-fenix-srv-ns8.rb index ee0c2580..29804142 100644 --- a/adn/tools/cli/drones/dasuten_transferir_srvv-fenix-srv-ns8.rb +++ b/adn/tools/cli/drones/dasuten_transferir_srvv-fenix-srv-ns8.rb @@ -4,95 +4,66 @@ # ========================================================== # adn/tools/cli/drones/dasuten_transferir_srvv-fenix-srv-ns8.rb # ========================================================== -# Dron: Transferencia SMB desde srvv-fenix a srv-ns8 -# Flujo: srvv-fenix (E:\BK_SQL\) → srv-ns8 (/var/tmp/) -# Usa: smbclient con credenciales de candados +# Dron: Preparar backup desde share de srv-ns8 para upload +# Flujo: /mnt/ns8Disco2/bkps-dasuten/ → /var/tmp/ (para rclone) +# Nota: El backup ya está en el share gracias al dron de export # ========================================================== require 'time' require 'json' -require 'shellwords' -require 'pathname' - -# Ruta por defecto (backup completo) -BACKUP_REMOTO_DEFAULT = "sysdasuten_compressed_ADN.bak" -BACKUP_LOCAL_DEFAULT = "/var/tmp/sysdasuten_compressed_ADN.bak" +require 'fileutils' # Si existe output del dron anterior (export o export_dif), leer la ruta EXPORT_OUTPUT_FILE = '/tmp/dron_export_output.json' EXPORT_DIF_OUTPUT_FILE = '/tmp/dron_export_dif_output.json' -backup_remoto_name = BACKUP_REMOTO_DEFAULT -backup_local = BACKUP_LOCAL_DEFAULT +backup_ns8_unix = nil +backup_name = nil if File.exist?(EXPORT_DIF_OUTPUT_FILE) output = JSON.parse(File.read(EXPORT_DIF_OUTPUT_FILE)) - if output['backup_ruta'] - # Extraer solo el nombre del archivo de la ruta completa (E:\BK_SQL\archivo.bak) - # Usar split para manejar tanto backslashes como forward slashes - backup_remoto_name = output['backup_ruta'].split(/[\\\/]/).last - backup_local = "/var/tmp/#{backup_remoto_name}" - puts "[*] Dron: Transferencia SMB srvv-fenix → srv-ns8 (DIFERENCIAL)" + if output['backup_ruta_ns8'] || output['backup_ruta_ns8_unix'] + backup_ns8_unix = output['backup_ruta_ns8_unix'] || output['backup_ruta_ns8'] + backup_name = File.basename(backup_ns8_unix) + puts "[*] Dron: Preparar backup DIFERENCIAL para upload" end elsif File.exist?(EXPORT_OUTPUT_FILE) output = JSON.parse(File.read(EXPORT_OUTPUT_FILE)) - if output['backup_ruta'] - backup_remoto_name = output['backup_ruta'].split(/[\\\/]/).last - backup_local = "/var/tmp/#{backup_remoto_name}" - puts "[*] Dron: Transferencia SMB srvv-fenix → srv-ns8 (COMPLETO)" + if output['backup_ruta_ns8'] || output['backup_ruta_ns8_unix'] + backup_ns8_unix = output['backup_ruta_ns8_unix'] || output['backup_ruta_ns8'] + backup_name = File.basename(backup_ns8_unix) + puts "[*] Dron: Preparar backup COMPLETO para upload" end end -BACKUP_SERVER = "//10.0.10.200/BK_SQL" -BACKUP_FILE = backup_remoto_name +unless backup_ns8_unix + puts "[!] Error: No se encontró ruta de backup en output de export" + exit 1 +end -# Directorio de destino en srv-ns8 (compartido para backups DASUTEN) -# Nota: Este directorio está expuesto vía Samba para acceso desde srvv-fenix BACKUP_NS8_DIR = "/mnt/ns8Disco2/bkps-dasuten" +BACKUP_LOCAL = "/var/tmp/#{backup_name}" -puts "[*] Dron: Transferencia SMB srvv-fenix → srv-ns8" -puts " Share: #{BACKUP_SERVER}" -puts " Archivo: #{BACKUP_FILE}" -puts " Destino: #{backup_local}" +puts " Share: #{BACKUP_NS8_DIR}" +puts " Archivo: #{backup_name}" +puts " Destino: #{BACKUP_LOCAL}" begin - # Autorizar candados primero - system("ruby adn/tools/candados/candados.rb authorize >/dev/null 2>&1") - - # Obtener credencial de candados - pass_cmd = "ruby adn/tools/candados/candados.rb get utnlarioja.intranet:monlaricardo 2>/dev/null" - pass = `#{pass_cmd}`.strip - - if pass.empty? - puts "[!] No se pudo obtener credencial de candados" - exit 1 - end - - # Transferencia SMB - puts "\n[1/2] Descargando vía SMB..." + puts "\n[1/2] Copiando desde share a /var/tmp..." start = Time.now - # Escapar contraseña para smbclient (evitar problemas con $ y caracteres especiales) - pass_escaped = pass.gsub('$', '\\$') - cmd_smb = "smbclient #{BACKUP_SERVER} -U \"monlaricardo%#{pass_escaped}\" -W UTNlarioja -c \"lcd /var/tmp; get #{BACKUP_FILE}\" 2>&1" - system(cmd_smb) + # Copiar archivo del share al directorio temporal para rclone + FileUtils.cp(backup_ns8_unix, BACKUP_LOCAL) - if $?.success? - duracion = (Time.now - start).round(2) - puts "[+] Transferencia completada en #{duracion} segundos" - else - puts "[!] Error en transferencia SMB (código: #{$?.exitstatus})" - exit 1 - end + duracion = (Time.now - start).round(2) + puts "[+] Copia completada en #{duracion} segundos" # Verificar archivo local puts "\n[2/2] Verificando archivo..." - if File.exist?(backup_local) - tamaño_mb = (File.size(backup_local) / 1024.0 / 1024.0).round(2) + if File.exist?(BACKUP_LOCAL) + tamaño_mb = (File.size(BACKUP_LOCAL) / 1024.0 / 1024.0).round(2) puts "[+] Archivo verificado: #{tamaño_mb} MB" - puts " Nota: Para copiar al share SMB, ejecutar desde srvv-fenix:" - puts " net use X: \\\\10.0.10.8\\bkps-dasuten /user:rmonla " - puts " copy #{backup_local} X:\\#{backup_remoto_name}" + puts " Ruta: #{BACKUP_LOCAL}" else puts "[!] Archivo no encontrado en destino" exit 1 @@ -101,20 +72,21 @@ begin # Output para orquestador resultado = { paso: 'transfer_complete', - backup_local: backup_local, - backup_ns8_share: "#{BACKUP_NS8_DIR}/#{backup_remoto_name}", + backup_local: BACKUP_LOCAL, + backup_ns8_share: backup_ns8_unix, nodo: 'srv-ns8', duracion: duracion, timestamp: Time.now.iso8601, siguiente_paso: 'upload_drive' } - puts "\n✅ Transferencia completada" + puts "\n✅ Backup listo para upload" puts " Output: #{resultado.to_json}" File.write('/tmp/dron_transfer_output.json', resultado.to_json) rescue => e puts "[!] Error fatal: #{e.message}" + puts e.backtrace.first(5).join("\n") exit 1 end