Created
October 31, 2024 00:32
-
-
Save 9000cats/72c7fdb213a4f99ecb1d304204de4966 to your computer and use it in GitHub Desktop.
PowerShell script for running Oracle RMAN backups, with a full backup on Sunday and incrementals Monday-Saturday.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<# | |
.SYNOPSIS | |
Performs automated RMAN (Recovery Manager) backups of Oracle databases with comprehensive logging and monitoring. | |
.DESCRIPTION | |
This script automates Oracle RMAN backup operations with enhanced logging capabilities. It performs | |
either full (level 0) or incremental (level 1) backups based on the day of the week, with full | |
backups scheduled for Sundays. Features include: | |
- Detailed logging with separate files for general and detailed logs | |
- Process monitoring and resource usage tracking | |
- Automatic cleanup of obsolete backups | |
- Archive log management | |
- Comprehensive error handling and validation | |
- System resource monitoring during backup | |
.PARAMETER LogPath | |
Path where log files will be stored. Default: "C:\Scripts\Log" | |
.PARAMETER RmanScriptPath | |
Path where temporary RMAN script files will be created. Default: "C:\Scripts" | |
.PARAMETER OracleSid | |
Oracle System ID (SID) for the target database. Default: "database" | |
.PARAMETER OraclePassword | |
Password for the Oracle SYS user. Default: "password" (Should be replaced with secure password management) | |
.NOTES | |
Version: 1.0 | |
Date: 10/30/2024 | |
Authors: Warren Held, ChatGPT o1-preview, Claude 3.5 Opus | |
Requires: PowerShell 5.1 or later | |
Oracle RMAN | |
.EXAMPLE | |
.\Run-RMANBackup.ps1 -OracleSid "PROD" -OraclePassword "SecurePass123" | |
.EXAMPLE | |
.\Run-RMANBackup.ps1 -LogPath "D:\Backup\Logs" -RmanScriptPath "D:\Backup\Scripts" -OracleSid "DEV" | |
#> | |
param ( | |
[string]$LogPath = "C:\Scripts\Log", | |
[string]$RmanScriptPath = "C:\Scripts", | |
[string]$OracleSid = "database", | |
[string]$OraclePassword = "password" # Better to use secure password management | |
) | |
# Create log directory if it doesn't exist | |
if (!(Test-Path $LogPath)) { | |
New-Item -ItemType Directory -Path $LogPath | Out-Null | |
} | |
# Initialize log file with timestamp and script version | |
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss" | |
$logFile = Join-Path $LogPath "rman_backup_$timestamp.log" | |
$detailedLogFile = Join-Path $LogPath "rman_backup_detailed_$timestamp.log" | |
function Write-Log { | |
param( | |
[Parameter(Mandatory=$true)] | |
[string]$Message, | |
[ValidateSet('INFO', 'WARNING', 'ERROR', 'DEBUG')] | |
[string]$Level = 'INFO' | |
) | |
$logMessage = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss'): [$Level] $Message" | |
Write-Output $logMessage | |
Add-Content -Path $logFile -Value $logMessage | |
Add-Content -Path $detailedLogFile -Value $logMessage | |
} | |
function Write-DetailedLog { | |
param([string]$Message) | |
$logMessage = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss'): $Message" | |
Add-Content -Path $detailedLogFile -Value $logMessage | |
} | |
function Test-RmanSuccess { | |
param([string[]]$Output) | |
Write-Log "Analyzing RMAN output for success/failure indicators" -Level "DEBUG" | |
# Check for common RMAN error patterns | |
$errorPatterns = @( | |
'RMAN-\d{5}:', # Any RMAN error | |
'ORA-\d{5}:', # Any Oracle error | |
'ERROR:', | |
'FATAL:', | |
'failed', | |
'incorrect username/password', | |
'connection failed', | |
'backup piece .* not found', | |
'CATALOG must be connected', | |
'backup failure', | |
'media recovery failed' | |
) | |
foreach ($line in $Output) { | |
foreach ($pattern in $errorPatterns) { | |
if ($line -match $pattern) { | |
Write-Log "Found error pattern '$pattern' in: $line" -Level "ERROR" | |
return $false | |
} | |
} | |
} | |
# Check for successful completion message | |
$successPatterns = @( | |
'RMAN> exit', | |
'completed successfully', | |
'Finished backup at' | |
) | |
$foundSuccess = $false | |
foreach ($line in $Output) { | |
foreach ($pattern in $successPatterns) { | |
if ($line -match $pattern) { | |
Write-Log "Found success pattern '$pattern' in: $line" -Level "DEBUG" | |
$foundSuccess = $true | |
} | |
} | |
} | |
return $foundSuccess | |
} | |
try { | |
# Log script start with environment details | |
Write-Log "=== RMAN Backup Script Started ===" -Level "INFO" | |
Write-Log "Script Version: 1.1 (Enhanced Logging)" -Level "INFO" | |
Write-Log "PowerShell Version: $($PSVersionTable.PSVersion)" -Level "INFO" | |
Write-Log "Script Parameters:" -Level "DEBUG" | |
Write-Log " LogPath: $LogPath" -Level "DEBUG" | |
Write-Log " RmanScriptPath: $RmanScriptPath" -Level "DEBUG" | |
Write-Log " OracleSid: $OracleSid" -Level "DEBUG" | |
# Check environment | |
Write-Log "Checking Oracle environment..." -Level "INFO" | |
$env:PATH -split ';' | ForEach-Object { Write-DetailedLog "PATH: $_" } | |
# Verify Oracle RMAN availability | |
if (-not (Get-Command rman -ErrorAction SilentlyContinue)) { | |
throw "RMAN command not found. Please ensure Oracle environment is properly configured." | |
} | |
Write-Log "RMAN command found in path" -Level "INFO" | |
# Log system resources | |
$systemInfo = Get-WmiObject Win32_OperatingSystem | |
Write-DetailedLog "Available Physical Memory: $([math]::Round($systemInfo.FreePhysicalMemory / 1MB, 2)) GB" | |
Write-DetailedLog "Available Virtual Memory: $([math]::Round($systemInfo.FreeVirtualMemory / 1MB, 2)) GB" | |
# Determine backup type | |
$dayOfWeek = (Get-Date).DayOfWeek | |
$isFullBackup = $dayOfWeek -eq "Sunday" | |
$backupType = if ($isFullBackup) { "FULL" } else { "INCR" } | |
Write-Log "Backup Type: $backupType (Day of week: $dayOfWeek)" -Level "INFO" | |
# Create backup tag | |
$shortDate = Get-Date -Format "yyMMdd_HHmm" | |
$backupTag = "${backupType}_${shortDate}" | |
Write-Log "Backup Tag: $backupTag" -Level "INFO" | |
# Create RMAN command file | |
$rmanCmdFile = Join-Path $RmanScriptPath "rman_backup_$timestamp.txt" | |
$rmanCommands = @" | |
connect target sys/$OraclePassword@$OracleSid | |
set echo on; | |
show all; | |
list backup summary; | |
CROSSCHECK ARCHIVELOG ALL; | |
report obsolete; | |
RUN { | |
BACKUP AS BACKUPSET | |
INCREMENTAL LEVEL $(if ($isFullBackup) { "0" } else { "1" }) | |
DATABASE | |
TAG '$backupTag' | |
PLUS ARCHIVELOG | |
DELETE INPUT; | |
DELETE OBSOLETE; | |
} | |
list backup summary; | |
exit; | |
"@ | |
$rmanCommands | Out-File -FilePath $rmanCmdFile -Encoding ASCII | |
Write-Log "Created RMAN command file at: $rmanCmdFile" -Level "INFO" | |
Write-DetailedLog "RMAN Commands:" | |
Write-DetailedLog $rmanCommands | |
# Execute RMAN | |
Write-Log "Starting RMAN backup execution..." -Level "INFO" | |
$startTime = Get-Date | |
Write-Log "Start Time: $startTime" -Level "INFO" | |
# Run RMAN with timeout | |
$rmanProcess = Start-Process rman -ArgumentList "cmdfile=`"$rmanCmdFile`"" -NoNewWindow -PassThru -RedirectStandardOutput "$LogPath\rman_output_$timestamp.txt" -RedirectStandardError "$LogPath\rman_error_$timestamp.txt" | |
# Monitor process and log memory usage | |
while (!$rmanProcess.HasExited) { | |
Start-Sleep -Seconds 30 | |
$processInfo = Get-Process -Id $rmanProcess.Id | |
Write-DetailedLog "RMAN Process Memory Usage: $([math]::Round($processInfo.WorkingSet / 1MB, 2)) MB" | |
Write-DetailedLog "RMAN Process CPU Time: $($processInfo.CPU)" | |
} | |
# Calculate execution time | |
$endTime = Get-Date | |
$executionTime = $endTime - $startTime | |
Write-Log "End Time: $endTime" -Level "INFO" | |
Write-Log "Total Execution Time: $($executionTime.ToString())" -Level "INFO" | |
# Process RMAN output | |
Write-Log "Reading RMAN output files..." -Level "DEBUG" | |
$rmanOutput = Get-Content "$LogPath\rman_output_$timestamp.txt" | |
$rmanError = Get-Content "$LogPath\rman_error_$timestamp.txt" | |
# Log all RMAN output to detailed log | |
Write-DetailedLog "=== RMAN STDOUT ===" | |
$rmanOutput | ForEach-Object { Write-DetailedLog $_ } | |
Write-DetailedLog "=== RMAN STDERR ===" | |
$rmanError | ForEach-Object { Write-DetailedLog $_ } | |
# Log only important RMAN messages to main log | |
$rmanOutput | Where-Object { | |
$_ -match "RMAN-|ORA-|starting|finished|ERROR|WARNING|completing|completed successfully|failed" | |
} | ForEach-Object { | |
Write-Log "RMAN: $_" -Level $( | |
if ($_ -match "ERROR|RMAN-|ORA-") { "ERROR" } | |
elseif ($_ -match "WARNING") { "WARNING" } | |
else { "INFO" } | |
) | |
} | |
# Check for successful completion | |
if (-not (Test-RmanSuccess -Output $rmanOutput)) { | |
throw "RMAN backup failed. Check log files for details." | |
} | |
Write-Log "RMAN backup completed successfully" -Level "INFO" | |
# RMAN command file cleanup | |
Write-Log "Cleaning up temporary files..." -Level "DEBUG" | |
Remove-Item $rmanCmdFile -Force | |
Write-Log "Removed RMAN command file" -Level "DEBUG" | |
# Archive logs if they're getting large | |
if ((Get-Item $detailedLogFile).Length -gt 10MB) { | |
$archiveDir = Join-Path $LogPath "Archive" | |
if (!(Test-Path $archiveDir)) { New-Item -ItemType Directory -Path $archiveDir } | |
Compress-Archive -Path $detailedLogFile -DestinationPath "$archiveDir\rman_backup_detailed_$timestamp.zip" | |
Remove-Item $detailedLogFile | |
Write-Log "Archived detailed log file due to size" -Level "INFO" | |
} | |
exit 0 | |
} | |
catch { | |
$errorMessage = $_.Exception.Message | |
$errorDetails = $_.Exception.ToString() | |
Write-Log "ERROR: $errorMessage" -Level "ERROR" | |
Write-DetailedLog "=== FULL ERROR DETAILS ===" | |
Write-DetailedLog $errorDetails | |
Write-Log "Backup process failed. Check detailed log for full error information." -Level "ERROR" | |
exit 1 | |
} | |
finally { | |
Write-Log "=== RMAN Backup Script Finished ===" -Level "INFO" | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment