#!/usr/bin/env ruby # frozen_string_literal: true # ========================================================== # adn/tools/cli/drones/dasuten_restaurar_dasu-sql4.rb # ========================================================== # Dron: Restaurar backup en dasu-sql4 # Flujo: RESTORE DATABASE (sin DBCC CHECKDB) # Entrada: Backup en F:\BACKUP\ # Salida: restore_complete (listo para verificación) # ========================================================== require 'time' require 'json' require 'base64' require 'tempfile' require 'shellwords' DOWNLOAD_OUTPUT = '/tmp/dron_download_output.json' BACKUP_DIR_WINDOWS = 'F:\\BACKUP' # Leer ruta del backup desde el output del download if File.exist?(DOWNLOAD_OUTPUT) output = JSON.parse(File.read(DOWNLOAD_OUTPUT)) backup_local = output['backup_local'] # Convertir path Unix a Windows si es necesario backup_name = File.basename(backup_local) LOCAL_BACKUP = "#{BACKUP_DIR_WINDOWS}\\#{backup_name}" puts "[*] Dron: Restaurar backup DASUTEN" else LOCAL_BACKUP = "#{BACKUP_DIR_WINDOWS}\\sysdasuten_FULL_*.bak" puts "[*] Dron: Restaurar backup DASUTEN (buscar más reciente)" end TARGET_IP = "192.168.1.11" TARGET_USER = "Administrador" TARGET_PASS = "UTNlarioja00DASU" puts " Nodo: dasu-sql4" puts " Backup: #{LOCAL_BACKUP}" # PowerShell script para restore (sin checkdb) ps_script = <<~'PSCMD' $ErrorActionPreference = "Stop" $BACKUP_DIR = "F:\BACKUP" $BACKUP_PATTERN = "sysdasuten_FULL_*.bak" $JSON_PATH = "C:\temp\dron_restore_output.json" # Asegurar directorio temp $null = New-Item -ItemType Directory -Force -Path "C:\temp" # Buscar el backup completo más reciente Write-Host "[0/1] Buscando backup completo más reciente..." $backupFile = Get-ChildItem "$BACKUP_DIR\$BACKUP_PATTERN" | Sort-Object LastWriteTime -Descending | Select-Object -First 1 if (-not $backupFile) { Write-Host "[!] No se encontró backup completo en $BACKUP_DIR" exit 1 } $LOCAL_BACKUP = $backupFile.FullName Write-Host " Archivo: $LOCAL_BACKUP" Write-Host " Tamaño: $([math]::Round($backupFile.Length / 1MB, 2)) MB" Write-Host "[1/1] Ejecutando RESTORE DATABASE..." $start = Get-Date # Ruta completa a sqlcmd (SQL Server 2019) $sqlcmd = "C:\Program Files\Microsoft SQL Server\Client SDK\ODBC\170\Tools\Binn\sqlcmd.exe" # Comando RESTORE $restoreSql = @" RESTORE DATABASE sysdasuten FROM DISK = '$LOCAL_BACKUP' WITH REPLACE, MOVE 'sysdasuten' TO 'F:\DATA\sysdasuten.mdf', MOVE 'sysdasuten_log' TO 'F:\LOG\sysdasuten_log.ldf' "@ Write-Host " SQL: $restoreSql" # Ejecutar restore $restoreOutput = & $sqlcmd -S localhost -Q $restoreSql 2>&1 $exitoRestore = $LASTEXITCODE -eq 0 $duracionRestore = [math]::Round((New-TimeSpan -Start $start -End (Get-Date)).TotalSeconds, 2) if (-not $exitoRestore) { Write-Host "[!] Error en restore:" Write-Host $restoreOutput exit 1 } Write-Host "[+] Restore completado en $duracionRestore segundos" $duracionTotal = $duracionRestore # Output JSON $res = @{ paso = "restore_complete" backup_ruta = $LOCAL_BACKUP nodo = "dasu-sql4" duracion_total = $duracionTotal duracion_restore = $duracionRestore estado_integridad = "PENDING_CHECKDB" timestamp = (Get-Date -Format "o") siguiente_paso = "verificar_integridad" } Write-Host "`n✅ Restauración completada (pendiente CHECKDB)" Write-Host " Output: $($res | ConvertTo-Json -Compress)" $res | ConvertTo-Json | Out-File -FilePath $JSON_PATH -Encoding utf8 PSCMD # Codificar script en base64 ps_encoded = Base64.strict_encode64(ps_script) # Script bash que se ejecuta en srv-dasu bash_script = <<~BASHHEREDOC #!/bin/bash export SSHPASS="#{TARGET_PASS}" sshpass -e ssh -o StrictHostKeyChecking=no -p 7022 #{TARGET_USER}@#{TARGET_IP} \ "powershell -Command \\\"Invoke-Expression ([System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String('#{ps_encoded}')))\\\"" BASHHEREDOC # Guardar script bash en archivo temporal bash_file = Tempfile.new(['restore', '.sh']) bash_file.write(bash_script) bash_file.chmod(0755) bash_file.close remote_bash_path = "/tmp/dron_restore_#{Time.now.to_i}.sh" puts "\n[1/3] Copiando script a srv-dasu..." scp_cmd = "scp -o StrictHostKeyChecking=no #{bash_file.path} srv-dasu:#{remote_bash_path}" unless system(scp_cmd) puts "[!] Error al copiar script" bash_file.unlink exit 1 end puts "\n[2/3] Ejecutando en dasu-sql4 vía srv-dasu..." ssh_cmd = "ssh srv-dasu 'bash #{remote_bash_path}'" output = `#{ssh_cmd}` output = output.encode('UTF-8', invalid: :replace, undef: :replace, replace: '?') if output puts output # Limpieza bash_file.unlink system("ssh srv-dasu 'rm -f #{remote_bash_path}' 2>/dev/null") if $?.success? puts "\n[3/3] Leyendo output JSON..." json_ps = 'Get-Content C:\temp\dron_restore_output.json -Raw' json_encoded = Base64.strict_encode64(json_ps) json_script = <<~JSONBASH.strip #!/bin/bash export SSHPASS="#{TARGET_PASS}" sshpass -e ssh -o StrictHostKeyChecking=no -p 7022 #{TARGET_USER}@#{TARGET_IP} \ "powershell -Command \\\"Invoke-Expression ([System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String('#{json_encoded}')))\\\"" JSONBASH json_file = Tempfile.new(['json', '.sh']) json_file.write(json_script) json_file.chmod(0755) json_file.close remote_json_path = "/tmp/dron_json_#{Time.now.to_i}.sh" system("scp -o StrictHostKeyChecking=no #{json_file.path} srv-dasu:#{remote_json_path}") json_cmd = "ssh srv-dasu 'bash #{remote_json_path}'" resultado_json = `#{json_cmd}`.strip resultado_json = resultado_json.encode('UTF-8', invalid: :replace, undef: :replace, replace: '?') if resultado_json json_file.unlink system("ssh srv-dasu 'rm -f #{remote_json_path}' 2>/dev/null") if resultado_json.empty? puts "[!] No se pudo leer output JSON" exit 1 end json_match = resultado_json.match(/\{.*\}/m) resultado_json = json_match ? json_match[0] : resultado_json puts " Output: #{resultado_json}" resultado = JSON.parse(resultado_json) puts "\n✅ Restauración completada" puts " Backup: #{resultado['backup_ruta']}" puts " Estado: #{resultado['estado_integridad']}" puts " Duración: #{resultado['duracion_total']}s" File.write('/tmp/dron_restore_output.json', resultado_json) puts " JSON guardado en: /tmp/dron_restore_output.json" else puts "[!] Error en ejecución (código: #{$?.exitstatus})" exit 1 end