From b50cc2b8c46b1748f5b4af12e15def500a7908ae Mon Sep 17 00:00:00 2001 From: Marius Date: Tue, 14 Oct 2025 18:05:11 +0300 Subject: [PATCH] Oracle DR: Fix backup retention and monitoring for new naming convention MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: Backups accumulated on DR (73 files, 4 days) instead of keeping only 2 days - transfer_incremental.ps1 had no cleanup function (ran 2x/day without cleanup) - transfer_to_dr.ps1 cleanup had poor logging - oracle-backup-monitor-proxmox.sh couldn't detect new L0/L1 backup format Changes: - Add cleanup to transfer_incremental.ps1 (delete backups older than 2 days) - Improve cleanup logging in transfer_to_dr.ps1 (shows count before/after) - Update oracle-backup-monitor-proxmox.sh to detect both naming conventions: * Old: *FULL*.BKP, *INCR*.BKP * New: L0_*.BKP (Level 0), L1_*.BKP (Level 1) - Remove temporary files from /input/ directory Result: Monitor now correctly reports backup age, cleanup runs after each transfer 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- input/copy_existing_key_to_proxmox.ps1 | 78 ------- input/screenshot.jpg | Bin 42633 -> 0 bytes input/setup_ssh_keys_for_proxmox.ps1 | 89 -------- input/transfer_incremental.ps1 | 130 ----------- input/transfer_to_dr.ps1 | 201 ------------------ .../oracle-backup-monitor-proxmox.sh | 17 +- .../transfer_incremental.ps1 | 31 +++ .../standby-server-scripts/transfer_to_dr.ps1 | 18 +- 8 files changed, 57 insertions(+), 507 deletions(-) delete mode 100644 input/copy_existing_key_to_proxmox.ps1 delete mode 100644 input/screenshot.jpg delete mode 100644 input/setup_ssh_keys_for_proxmox.ps1 delete mode 100644 input/transfer_incremental.ps1 delete mode 100644 input/transfer_to_dr.ps1 diff --git a/input/copy_existing_key_to_proxmox.ps1 b/input/copy_existing_key_to_proxmox.ps1 deleted file mode 100644 index d1fd10a..0000000 --- a/input/copy_existing_key_to_proxmox.ps1 +++ /dev/null @@ -1,78 +0,0 @@ -# Copy Existing SSH Key to Proxmox -# Ruleaza acest script pe PRIMARY ca Administrator -# -# Acest script copiaza cheia publica SSH existenta din profilul SYSTEM pe Proxmox - -param( - [string]$ProxmoxHost = "10.0.20.202", - [string]$ProxmoxUser = "root" -) - -Write-Host "=========================================" -ForegroundColor Cyan -Write-Host "Copiere Cheie SSH Existenta -> Proxmox DR" -ForegroundColor Cyan -Write-Host "=========================================" -ForegroundColor Cyan -Write-Host "" - -$SystemSSHDir = "C:\Windows\System32\config\systemprofile\.ssh" -$PublicKeyPath = "$SystemSSHDir\id_rsa.pub" -$PrivateKeyPath = "$SystemSSHDir\id_rsa" - -# Verifica daca cheia exista -if (-not (Test-Path $PublicKeyPath)) { - Write-Host "X Cheia publica nu exista: $PublicKeyPath" -ForegroundColor Red - Write-Host " Scriptul trebuie rulat ca Administrator!" -ForegroundColor Yellow - exit 1 -} - -Write-Host "OK Cheie publica gasita: $PublicKeyPath" -ForegroundColor Green -Write-Host "" -Write-Host "-> Copiez cheia publica pe Proxmox ($ProxmoxHost)..." -ForegroundColor Yellow -Write-Host " IMPORTANT: Vei fi intrebat de parola root pentru Proxmox!" -ForegroundColor Cyan -Write-Host "" - -# Metoda simpla: folosim SCP pentru a copia fisierul temporar, apoi cat -$tempFile = "C:\Windows\Temp\temp_pubkey.pub" -Copy-Item $PublicKeyPath $tempFile -Force - -# Copiaza fisierul pe Proxmox -& scp $tempFile "${ProxmoxUser}@${ProxmoxHost}:/tmp/temp_pubkey.pub" - -if ($LASTEXITCODE -ne 0) { - Write-Host "X Eroare la copierea fisierului cu SCP" -ForegroundColor Red - Remove-Item $tempFile -Force -ErrorAction SilentlyContinue - exit 1 -} - -# Adauga cheia in authorized_keys -& ssh "${ProxmoxUser}@${ProxmoxHost}" "mkdir -p /root/.ssh; chmod 700 /root/.ssh; cat /tmp/temp_pubkey.pub >> /root/.ssh/authorized_keys; chmod 600 /root/.ssh/authorized_keys; rm /tmp/temp_pubkey.pub; echo 'SSH key added'" - -Remove-Item $tempFile -Force -ErrorAction SilentlyContinue - -if ($LASTEXITCODE -eq 0) { - Write-Host "OK Cheie publica copiata pe Proxmox!" -ForegroundColor Green -} else { - Write-Host "X Eroare la adaugarea cheii in authorized_keys" -ForegroundColor Red - exit 1 -} - -Write-Host "" -Write-Host "-> Testez conexiunea SSH fara parola..." -ForegroundColor Yellow - -# Testeaza conexiunea (cu cheia din profilul SYSTEM) -$testResult = & ssh -o StrictHostKeyChecking=no -i $PrivateKeyPath "${ProxmoxUser}@${ProxmoxHost}" "echo 'SSH connection OK'" 2>&1 - -if ($LASTEXITCODE -eq 0) { - Write-Host "OK Conexiune SSH functioneaza fara parola!" -ForegroundColor Green -} else { - Write-Host "X Conexiunea SSH nu functioneaza direct din acest cont" -ForegroundColor Yellow - Write-Host " Dar cheia a fost adaugata - scheduled tasks (SYSTEM) ar trebui sa functioneze" -ForegroundColor Yellow -} - -Write-Host "" -Write-Host "=========================================" -ForegroundColor Green -Write-Host "OK Setup complet!" -ForegroundColor Green -Write-Host "=========================================" -ForegroundColor Green -Write-Host "" -Write-Host "Cheia din profilul SYSTEM: $PrivateKeyPath" -ForegroundColor Cyan -Write-Host "Scheduled tasks vor folosi aceasta cheie automat." -ForegroundColor Yellow -Write-Host "" diff --git a/input/screenshot.jpg b/input/screenshot.jpg deleted file mode 100644 index f34dd8ead55c25d96fcd0848b0dea786d4bdac39..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 42633 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 Tdb;^7r{&Y_;WO(dn&1 - if ($LASTEXITCODE -ne 0) { - throw "SSH connection failed" - } - Write-Log "SSH connection OK" "SUCCESS" - - # Găsește backup-uri incrementale - $backupFiles = Get-IncrementalBackups - - if ($backupFiles.Count -eq 0) { - Write-Log "⚠️ No incremental backup files found (this might be normal if backup didn't run yet)" "WARNING" - exit 0 - } - - $totalSizeGB = ($backupFiles | Measure-Object -Property Length -Sum).Sum / 1GB - Write-Log "Found $($backupFiles.Count) incremental files, total size: $([math]::Round($totalSizeGB, 2)) GB" - - # Transfer fișiere - Write-Log "Starting file transfer..." - $successCount = 0 - $failCount = 0 - - foreach ($file in $backupFiles) { - $fileName = $file.Name - $fileSizeMB = [math]::Round($file.Length / 1MB, 2) - - # Check dacă fișierul există deja pe DR (skip duplicates) - Linux bash command - $checkCmd = "test -f '$DRPath/$fileName' && echo 'True' || echo 'False'" - $checkResult = & ssh -n -p $DRPort -i $SSHKeyPath "${DRUser}@${DRHost}" $checkCmd 2>&1 - - if ($checkResult -match "True") { - Write-Log "Skipping (already on DR): $fileName" "INFO" - $successCount++ - continue - } - - Write-Log "Transferring: $fileName ($fileSizeMB MB)" - - # SCP optimized: no compression (already compressed), fast cipher - $null = & scp -P $DRPort -i $SSHKeyPath -o StrictHostKeyChecking=no -o Compression=no -o Cipher=aes128-gcm@openssh.com ` - $file.FullName ` - "${DRUser}@${DRHost}:${DRPath}/$fileName" 2>&1 - - if ($LASTEXITCODE -eq 0) { - Write-Log "Transferred: $fileName" "SUCCESS" - $successCount++ - } else { - Write-Log "Failed to transfer: $fileName" "ERROR" - $failCount++ - } - } - - Write-Log "=========================================" - Write-Log "Transfer summary: $successCount succeeded, $failCount failed" - - if ($failCount -gt 0) { - Write-Log "⚠️ Some transfers failed!" "WARNING" - exit 1 - } - - Write-Log "=========================================" - Write-Log "INCREMENTAL Backup Transfer Completed Successfully" - Write-Log "=========================================" - Write-Log "Files transferred: $successCount/$($backupFiles.Count)" - - exit 0 - -} catch { - Write-Log "CRITICAL ERROR: $($_.Exception.Message)" "ERROR" - exit 1 -} diff --git a/input/transfer_to_dr.ps1 b/input/transfer_to_dr.ps1 deleted file mode 100644 index 0063cc6..0000000 --- a/input/transfer_to_dr.ps1 +++ /dev/null @@ -1,201 +0,0 @@ -# Transfer Oracle RMAN Backup towards DR Server -# Rulează după backup RMAN (03:00 AM) -# Copiază backup-uri de pe PRIMARY (10.0.20.36) către DR (10.0.20.37) - -param( - [string]$SourceFRA = "C:\Users\Oracle\recovery_area\ROA", - [string]$DRHost = "10.0.20.202", - [int]$DRPort = 22, - [string]$DRUser = "root", - [string]$DRPath = "/mnt/pve/oracle-backups/ROA/autobackup", - [string]$SSHKeyPath = "$env:USERPROFILE\.ssh\id_rsa", - [int]$RetentionDays = 2, - [string]$LogFile = "D:\rman_backup\logs\transfer_$(Get-Date -Format 'yyyyMMdd').log" -) - -$ErrorActionPreference = "Continue" - -function Write-Log { - param([string]$Message, [string]$Level = "INFO") - $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" - $logLine = "[$timestamp] [$Level] $Message" - Write-Host $logLine - Add-Content -Path $LogFile -Value $logLine -Encoding UTF8 -ErrorAction SilentlyContinue -} - -function Test-SSHConnection { - Write-Log "Testing SSH connection to $DRHost`:$DRPort..." - - try { - # Folosește -n pentru a nu citi din stdin (fix pentru blocare) - $null = & ssh -n -p $DRPort -i $SSHKeyPath -o StrictHostKeyChecking=no -o ConnectTimeout=10 "${DRUser}@${DRHost}" "exit 0" 2>&1 - - if ($LASTEXITCODE -eq 0) { - Write-Log "SSH connection successful" "SUCCESS" - return $true - } else { - Write-Log "SSH connection failed with exit code: $LASTEXITCODE" "ERROR" - return $false - } - } catch { - Write-Log "SSH connection error: $_" "ERROR" - return $false - } -} - -function Get-TodaysBackups { - Write-Log "Searching for today's backup files..." - - $today = Get-Date - $cutoffDate = $today.Date # Only today (after midnight) - $backupFiles = @() - - $searchPaths = @( - "$SourceFRA\BACKUPSET", - "$SourceFRA\AUTOBACKUP" - ) - - foreach ($path in $searchPaths) { - if (Test-Path $path) { - # Get files created TODAY only (exclude old backups) - $files = Get-ChildItem -Path $path -Recurse -File -ErrorAction SilentlyContinue | - Where-Object { - $_.LastWriteTime -gt $cutoffDate -and - $_.Name -notlike "*__TAG_*" # Exclude old uncompressed backups - } | - Sort-Object LastWriteTime -Descending - - $backupFiles += $files - } - } - - if ($backupFiles.Count -eq 0) { - Write-Log "No backup files found for today!" "WARNING" - return @() - } - - $totalSizeGB = ($backupFiles | Measure-Object -Property Length -Sum).Sum / 1GB - Write-Log "Found $($backupFiles.Count) files, total size: $([math]::Round($totalSizeGB, 2)) GB" - - return $backupFiles -} - -function Transfer-FileToDR { - param([System.IO.FileInfo]$File, [string]$DestPath) - - $fileName = $File.Name - $fileSizeMB = [math]::Round($File.Length / 1MB, 2) - - try { - # Check dacă fișierul există deja pe DR (skip duplicates) - Linux bash command - $checkCmd = "test -f '$DestPath/$fileName' && echo 'True' || echo 'False'" - $checkResult = & ssh -n -p $DRPort -i $SSHKeyPath "${DRUser}@${DRHost}" $checkCmd 2>&1 - - if ($checkResult -match "True") { - Write-Log "Skipping (already on DR): $fileName" "INFO" - return $true - } - - Write-Log "Transferring: $fileName ($fileSizeMB MB)" - - # SCP transfer - NO compression (files already compressed by RMAN) - # Use cipher aes128-gcm for better performance - $null = & scp -P $DRPort -i $SSHKeyPath -o StrictHostKeyChecking=no -o Compression=no -o Cipher=aes128-gcm@openssh.com $File.FullName "${DRUser}@${DRHost}:${DestPath}/" 2>&1 - - if ($LASTEXITCODE -eq 0) { - Write-Log "Transferred: $fileName" "SUCCESS" - return $true - } else { - Write-Log "Failed to transfer: $fileName (exit code: $LASTEXITCODE)" "ERROR" - return $false - } - } catch { - Write-Log "Transfer error for $fileName : $_" "ERROR" - return $false - } -} - -function Cleanup-OldBackupsOnDR { - Write-Log "Cleaning up old backups on DR (keeping last $RetentionDays days)..." - - try { - # Cleanup: șterge fișiere mai vechi de $RetentionDays zile - Linux find command - $cleanupCmd = "find '$DRPath' -type f -mtime +$RetentionDays -delete 2>/dev/null || true" - $result = & ssh -n -p $DRPort -i $SSHKeyPath "${DRUser}@${DRHost}" $cleanupCmd 2>&1 - - Write-Log "Cleanup completed on DR (removed files older than $RetentionDays days)" - } catch { - Write-Log "Cleanup warning: $_" "WARNING" - } -} - -# ==================== MAIN ==================== - -try { - Write-Log "=========================================" - Write-Log "Oracle DR Backup Transfer Started" - Write-Log "=========================================" - Write-Log "Source FRA: $SourceFRA" - Write-Log "DR Server: $DRHost" - Write-Log "DR Path: $DRPath" - - # Verificare prerequisite - if (-not (Test-Path $SourceFRA)) { - throw "Source FRA path not found: $SourceFRA" - } - - if (-not (Test-Path $SSHKeyPath)) { - throw "SSH key not found: $SSHKeyPath" - } - - # Test SSH connection - if (-not (Test-SSHConnection)) { - throw "Cannot connect to DR server via SSH" - } - - # Creare director pe DR - Linux mkdir command - Write-Log "Ensuring DR directory exists..." - $null = & ssh -n -p $DRPort -i $SSHKeyPath "${DRUser}@${DRHost}" "mkdir -p '$DRPath'" 2>&1 - - # Găsește backup-uri - $backupFiles = Get-TodaysBackups - - if ($backupFiles.Count -eq 0) { - throw "No backup files to transfer!" - } - - # Transfer fișiere - Write-Log "Starting file transfer..." - $successCount = 0 - $failCount = 0 - - foreach ($file in $backupFiles) { - if (Transfer-FileToDR -File $file -DestPath $DRPath) { - $successCount++ - } else { - $failCount++ - } - } - - Write-Log "Transfer summary: $successCount succeeded, $failCount failed" - - if ($failCount -gt 0) { - Write-Log "Some transfers failed!" "WARNING" - } - - # Cleanup old backups pe DR - Cleanup-OldBackupsOnDR - - Write-Log "=========================================" - Write-Log "DR Backup Transfer Completed Successfully" - Write-Log "=========================================" - Write-Log "Files transferred: $successCount/$($backupFiles.Count)" - Write-Log "DR Server: ${DRHost}:${DRPath}" - - exit 0 - -} catch { - Write-Log "CRITICAL ERROR: $($_.Exception.Message)" "ERROR" - Write-Log "Stack trace: $($_.ScriptStackTrace)" "ERROR" - exit 1 -} diff --git a/oracle/standby-server-scripts/oracle-backup-monitor-proxmox.sh b/oracle/standby-server-scripts/oracle-backup-monitor-proxmox.sh index f7d7fae..b3ec38e 100644 --- a/oracle/standby-server-scripts/oracle-backup-monitor-proxmox.sh +++ b/oracle/standby-server-scripts/oracle-backup-monitor-proxmox.sh @@ -294,7 +294,9 @@ check_backups() { [ -z "$total_size" ] && total_size="0G" total_size_label="$total_size" - local latest_full=$(find "$BACKUP_PATH" -maxdepth 1 -type f -name '*FULL*.BKP' -printf '%T@ %p\n' | sort -nr | head -1 | cut -d' ' -f2-) + # Search for FULL backups (both old and new naming conventions) + # Old format: *FULL*.BKP, New format: L0_*.BKP + local latest_full=$(find "$BACKUP_PATH" -maxdepth 1 -type f \( -name '*FULL*.BKP' -o -name 'L0_*.BKP' \) -printf '%T@ %p\n' | sort -nr | head -1 | cut -d' ' -f2-) if [ -n "$latest_full" ]; then local full_timestamp=$(stat -c %Y "$latest_full") local current_timestamp=$(date +%s) @@ -310,7 +312,10 @@ check_backups() { errors+=("No FULL backup found") fi - local latest_cumulative=$(find "$BACKUP_PATH" -maxdepth 1 -type f \( -name '*INCR*.BKP' -o -name '*INCREMENTAL*.BKP' -o -name '*CUMULATIVE*.BKP' \) -printf '%T@ %p\n' | sort -nr | head -1 | cut -d' ' -f2-) + # Search for INCREMENTAL backups (both old and new naming conventions) + # Old format: *INCR*.BKP, *INCREMENTAL*.BKP, *CUMULATIVE*.BKP + # New format: L1_*.BKP + local latest_cumulative=$(find "$BACKUP_PATH" -maxdepth 1 -type f \( -name '*INCR*.BKP' -o -name '*INCREMENTAL*.BKP' -o -name '*CUMULATIVE*.BKP' -o -name 'L1_*.BKP' \) -printf '%T@ %p\n' | sort -nr | head -1 | cut -d' ' -f2-) if [ -n "$latest_cumulative" ]; then local cumulative_timestamp=$(stat -c %Y "$latest_cumulative") local current_timestamp=$(date +%s) @@ -323,10 +328,10 @@ check_backups() { fi fi - # Collect ALL FULL backups + # Collect ALL FULL backups (both old and new naming conventions) local -a full_backups=() local -a full_backup_entries=() - if readarray -t full_backups < <(find "$BACKUP_PATH" -maxdepth 1 -type f -name '*FULL*.BKP' -printf '%T@ %p\n' | sort -nr | cut -d' ' -f2-); then + if readarray -t full_backups < <(find "$BACKUP_PATH" -maxdepth 1 -type f \( -name '*FULL*.BKP' -o -name 'L0_*.BKP' \) -printf '%T@ %p\n' | sort -nr | cut -d' ' -f2-); then for backup_file in "${full_backups[@]}"; do [ -z "$backup_file" ] && continue local backup_name=$(basename "$backup_file") @@ -337,10 +342,10 @@ check_backups() { done fi - # Collect ALL INCREMENTAL backups + # Collect ALL INCREMENTAL backups (both old and new naming conventions) local -a incr_backups=() local -a incr_backup_entries=() - if readarray -t incr_backups < <(find "$BACKUP_PATH" -maxdepth 1 -type f \( -name '*INCR*.BKP' -o -name '*INCREMENTAL*.BKP' -o -name '*CUMULATIVE*.BKP' \) -printf '%T@ %p\n' | sort -nr | cut -d' ' -f2-); then + if readarray -t incr_backups < <(find "$BACKUP_PATH" -maxdepth 1 -type f \( -name '*INCR*.BKP' -o -name '*INCREMENTAL*.BKP' -o -name '*CUMULATIVE*.BKP' -o -name 'L1_*.BKP' \) -printf '%T@ %p\n' | sort -nr | cut -d' ' -f2-); then for backup_file in "${incr_backups[@]}"; do [ -z "$backup_file" ] && continue local backup_name=$(basename "$backup_file") diff --git a/oracle/standby-server-scripts/transfer_incremental.ps1 b/oracle/standby-server-scripts/transfer_incremental.ps1 index 6e8de82..5a46fed 100644 --- a/oracle/standby-server-scripts/transfer_incremental.ps1 +++ b/oracle/standby-server-scripts/transfer_incremental.ps1 @@ -48,6 +48,34 @@ function Get-IncrementalBackups { return $backupFiles } +function Cleanup-OldBackupsOnDR { + param([int]$RetentionDays = 2) + + Write-Log "Cleaning up old backups on DR (keeping last $RetentionDays days)..." + + try { + # Count fișiere înainte de cleanup + $countBefore = & ssh -n -p $DRPort -i $SSHKeyPath "${DRUser}@${DRHost}" "find '$DRPath' -name '*.BKP' -type f | wc -l" 2>&1 + Write-Log "Backups before cleanup: $countBefore" + + # Cleanup: șterge fișiere mai vechi de $RetentionDays zile + $cleanupCmd = "find '$DRPath' -name '*.BKP' -type f -mtime +$RetentionDays -delete 2>&1" + $result = & ssh -n -p $DRPort -i $SSHKeyPath "${DRUser}@${DRHost}" $cleanupCmd 2>&1 + + if ($LASTEXITCODE -ne 0) { + Write-Log "Cleanup warning: $result" "WARNING" + } + + # Count fișiere după cleanup + $countAfter = & ssh -n -p $DRPort -i $SSHKeyPath "${DRUser}@${DRHost}" "find '$DRPath' -name '*.BKP' -type f | wc -l" 2>&1 + $deleted = [int]$countBefore - [int]$countAfter + + Write-Log "Cleanup completed: Deleted $deleted old backup files, $countAfter remaining" + } catch { + Write-Log "Cleanup error: $_" "WARNING" + } +} + try { Write-Log "=========================================" Write-Log "Oracle INCREMENTAL Backup Transfer Started" @@ -117,6 +145,9 @@ try { exit 1 } + # Cleanup old backups pe DR (keep last 2 days) + Cleanup-OldBackupsOnDR -RetentionDays 2 + Write-Log "=========================================" Write-Log "INCREMENTAL Backup Transfer Completed Successfully" Write-Log "=========================================" diff --git a/oracle/standby-server-scripts/transfer_to_dr.ps1 b/oracle/standby-server-scripts/transfer_to_dr.ps1 index 0063cc6..465ae0b 100644 --- a/oracle/standby-server-scripts/transfer_to_dr.ps1 +++ b/oracle/standby-server-scripts/transfer_to_dr.ps1 @@ -119,13 +119,25 @@ function Cleanup-OldBackupsOnDR { Write-Log "Cleaning up old backups on DR (keeping last $RetentionDays days)..." try { + # Count fișiere înainte de cleanup + $countBefore = & ssh -n -p $DRPort -i $SSHKeyPath "${DRUser}@${DRHost}" "find '$DRPath' -name '*.BKP' -type f | wc -l" 2>&1 + Write-Log "Backups before cleanup: $countBefore" + # Cleanup: șterge fișiere mai vechi de $RetentionDays zile - Linux find command - $cleanupCmd = "find '$DRPath' -type f -mtime +$RetentionDays -delete 2>/dev/null || true" + $cleanupCmd = "find '$DRPath' -name '*.BKP' -type f -mtime +$RetentionDays -delete 2>&1" $result = & ssh -n -p $DRPort -i $SSHKeyPath "${DRUser}@${DRHost}" $cleanupCmd 2>&1 - Write-Log "Cleanup completed on DR (removed files older than $RetentionDays days)" + if ($LASTEXITCODE -ne 0) { + Write-Log "Cleanup warning: $result" "WARNING" + } + + # Count fișiere după cleanup + $countAfter = & ssh -n -p $DRPort -i $SSHKeyPath "${DRUser}@${DRHost}" "find '$DRPath' -name '*.BKP' -type f | wc -l" 2>&1 + $deleted = [int]$countBefore - [int]$countAfter + + Write-Log "Cleanup completed: Deleted $deleted old backup files, $countAfter remaining" } catch { - Write-Log "Cleanup warning: $_" "WARNING" + Write-Log "Cleanup error: $_" "WARNING" } }