PROBLEMA:
- Script fallaba con "Invalid escape character syntax" en línea 71
- Ruby interpretaba \P, \M, \S como escapes inválidos
- Pipeline bkps fallaba en paso 5/6 (Restaurar diferencial)
- Orquestador continuaba al paso 6 aunque paso 5 fallaba
SOLUCIÓN:
- Escapar correctamente ruta de sqlcmd.exe: "C:\\Program Files\\..."
- Heredoc mantiene interpolación #{backup_name} para PowerShell
RESULTADO:
- Restore diferencial: ✅ 0.14s
- DBCC CHECKDB: ✅ 241.68s — Estado: OK
- Pipeline diferencial completo: 39.49 MB restaurados
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
148 lines
5.2 KiB
Ruby
148 lines
5.2 KiB
Ruby
#!/usr/bin/env ruby
|
|
# frozen_string_literal: true
|
|
|
|
# ==========================================================
|
|
# adn/tools/cli/drones/dasuten_restaurar_diferencial_dasu-sql4.rb
|
|
# ==========================================================
|
|
# Dron: Restaurar backup DIFERENCIAL en dasu-sql4
|
|
# Flujo: RESTORE DATABASE ... WITH DIFFERENTIAL (sobre BD existente)
|
|
# Entrada: Backup diferencial en F:\BACKUP\
|
|
# Salida: differential_restore_complete
|
|
# ==========================================================
|
|
|
|
require 'time'
|
|
require 'json'
|
|
require_relative 'lib/dasu_executor'
|
|
|
|
DOWNLOAD_OUTPUT = '/tmp/dron_download_output.json'
|
|
JSON_PATH = 'C:\temp\dron_restore_dif_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']
|
|
backup_name = output['backup_name'] || File.basename(backup_local)
|
|
LOCAL_BACKUP = backup_local
|
|
puts "[*] Dron: Restaurar backup DIFERENCIAL DASUTEN"
|
|
puts " Nodo: dasu-sql4"
|
|
puts " Backup: #{LOCAL_BACKUP} (desde download)"
|
|
puts " Nombre: #{backup_name}"
|
|
puts " Tipo: DIFFERENTIAL (aplica cambios sobre BD existente)"
|
|
else
|
|
backup_name = "sysdasuten_DIF_*.bak"
|
|
LOCAL_BACKUP = "#{BACKUP_DIR_WINDOWS}\\#{backup_name}"
|
|
puts "[*] Dron: Restaurar backup DIFERENCIAL DASUTEN (buscar más reciente)"
|
|
puts " Nodo: dasu-sql4"
|
|
puts " Backup: #{LOCAL_BACKUP} (búsqueda por patrón)"
|
|
puts " Tipo: DIFFERENTIAL (aplica cambios sobre BD existente)"
|
|
end
|
|
|
|
# PowerShell script para restore diferencial
|
|
ps_script = <<~PSCMD.strip
|
|
$ErrorActionPreference = "Stop"
|
|
$BACKUP_DIR = "F:\\BACKUP"
|
|
$BACKUP_NAME = "#{backup_name}"
|
|
$LOCAL_BACKUP = "$BACKUP_DIR\\$BACKUP_NAME"
|
|
$JSON_PATH = "C:\\temp\\dron_restore_dif_output.json"
|
|
|
|
$null = New-Item -ItemType Directory -Force -Path "C:\\temp"
|
|
|
|
Write-Host "[1/3] Verificando backup diferencial específico..."
|
|
Write-Host " Archivo esperado: $LOCAL_BACKUP"
|
|
|
|
if (-not (Test-Path $LOCAL_BACKUP)) {
|
|
Write-Host "[!] No se encontró backup en $LOCAL_BACKUP"
|
|
Write-Host " Buscando backup diferencial más reciente como fallback..."
|
|
$BACKUP_PATTERN = "sysdasuten_DIF_*.bak"
|
|
$backupFile = Get-ChildItem "$BACKUP_DIR\\$BACKUP_PATTERN" | Sort-Object Name -Descending | Select-Object -First 1
|
|
if (-not $backupFile) {
|
|
Write-Host "[!] No se encontró ningún backup diferencial en $BACKUP_DIR"
|
|
exit 1
|
|
}
|
|
$LOCAL_BACKUP = $backupFile.FullName
|
|
Write-Host " Fallback: $LOCAL_BACKUP"
|
|
}
|
|
|
|
$backupFile = Get-Item $LOCAL_BACKUP
|
|
Write-Host " Archivo: $LOCAL_BACKUP"
|
|
Write-Host " Tamaño: $([math]::Round($backupFile.Length / 1MB, 2)) MB"
|
|
|
|
$sqlcmd = "C:\\Program Files\\Microsoft SQL Server\\Client SDK\\ODBC\\170\\Tools\\Binn\\sqlcmd.exe"
|
|
|
|
Write-Host "`n[2/3] Ejecutando RESTORE DATABASE ... WITH DIFFERENTIAL..."
|
|
$start = Get-Date
|
|
|
|
$restoreSql = @"
|
|
RESTORE DATABASE sysdasuten
|
|
FROM DISK = '$LOCAL_BACKUP'
|
|
WITH NORECOVERY, REPLACE
|
|
"@
|
|
|
|
Write-Host " SQL: $restoreSql"
|
|
|
|
$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 diferencial completado en $duracionRestore segundos"
|
|
|
|
Write-Host "`n[3/3] Poniendo base de datos ONLINE..."
|
|
|
|
$recoverySql = "RESTORE DATABASE sysdasuten WITH RECOVERY"
|
|
$recoveryOutput = & $sqlcmd -S localhost -Q $recoverySql 2>&1
|
|
$exitoRecovery = $LASTEXITCODE -eq 0
|
|
$duracionRecovery = [math]::Round((New-TimeSpan -Start (Get-Date) -End (Get-Date)).TotalSeconds, 2)
|
|
|
|
if ($exitoRecovery) {
|
|
Write-Host "[+] Base de datos ONLINE"
|
|
} else {
|
|
Write-Host "[!] Error en recovery:"
|
|
Write-Host $recoveryOutput
|
|
exit 1
|
|
}
|
|
|
|
$duracionTotal = [math]::Round($duracionRestore + $duracionRecovery, 2)
|
|
|
|
$res = @{
|
|
paso = "differential_restore_complete"
|
|
backup_ruta = $LOCAL_BACKUP
|
|
backup_tipo = "differential"
|
|
nodo = "dasu-sql4"
|
|
duracion_total = $duracionTotal
|
|
duracion_restore = $duracionRestore
|
|
duracion_recovery = $duracionRecovery
|
|
estado_integridad = "PENDING_CHECKDB"
|
|
timestamp = (Get-Date -Format "o")
|
|
siguiente_paso = "verificar_integridad"
|
|
}
|
|
|
|
Write-Host "`n✅ Restauración diferencial completada"
|
|
Write-Host " Estado: BD ONLINE"
|
|
Write-Host " Output: $($res | ConvertTo-Json -Compress)"
|
|
|
|
$res | ConvertTo-Json | Out-File -FilePath $JSON_PATH -Encoding utf8
|
|
PSCMD
|
|
|
|
puts "\n[1/2] Ejecutando restore diferencial en dasu-sql4..."
|
|
DasuExecutor.execute_ps_on_dasu(ps_script, output_path: JSON_PATH)
|
|
|
|
puts "\n[2/2] Leyendo output JSON..."
|
|
resultado = DasuExecutor.read_json_from_dasu(JSON_PATH)
|
|
|
|
puts " Output: #{resultado.to_json}"
|
|
|
|
puts "\n✅ Restauración diferencial completada"
|
|
puts " Backup: #{resultado['backup_ruta']}"
|
|
puts " Estado: #{resultado['estado_integridad']}"
|
|
puts " Duración total: #{resultado['duracion_total']}s"
|
|
|
|
File.write('/tmp/dron_restore_dif_output.json', resultado.to_json)
|
|
puts " JSON guardado en: /tmp/dron_restore_dif_output.json"
|