Reorganize oracle/ and chatbot/ into proxmox/ per LXC/VM structure

- Move oracle/migration-scripts/ to proxmox/lxc108-oracle/migration/
- Move oracle/roa/ and oracle/roa-romconstruct/ to proxmox/lxc108-oracle/sql/
- Move oracle/standby-server-scripts/ to proxmox/vm109-windows-dr/
- Move chatbot/ to proxmox/lxc104-flowise/
- Update proxmox/README.md with new structure and navigation
- Update all documentation with correct directory references
- Remove unused input/claude-agent-sdk/ files

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Marius
2026-01-27 17:28:53 +02:00
parent 4d51d5b2d2
commit a567f75f25
51 changed files with 233 additions and 1706 deletions

View File

@@ -0,0 +1,36 @@
# Add PRIMARY SYSTEM user SSH key to DR VM
# Run this on DR VM (10.0.20.37) as Administrator
$systemKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQD3EdHswdNDuDC9kJdUli2zGGPVlEWJjmqtb4eABYWwjQnWqjGp8oAFbQ+r2TxR544WtEyhDL9BU6oO3EFH957DBGQJHJvfRgx2VnkNZEzN/XX/7HK6Cp5rlINGGp26PjHulKkZjARmjC3YK0aUFEkiyNyUBqhtQpmcYP4+wjUfiiO2xUkF9mzGplbWGK3ZmEdkWNd5BNddqxmxyLvd2KHAo8F7Vux9SyPWqZ8bwiDyidAMDU7kCXS/RabUMl2LGajzFbRnR87YA7cIaVFl/IWExO/fsYlgkwmmmIAMdjINp0IWDdydnmG1XNNhM8h/BKY/eK3uile8CvyEbmbuf0+ePm3Ex9vTjn4jYN2vFE148FgQGGTibibJ+sXFoQ87VFNGEuog/V0aajVk/xcOihszsEvzD2IV/tSKJFdI6klnYLuieuMZf7Dvs/6sC1l3dnsBtcpvjnU48altRRZvQzaJf3gIkG1lRGBgyW1n+WHe/7StIveYTVNFtx+fcnqB8gm9fZQxBp2WRbLNFpY/Qj+6BF66b1A2ZxH/3F9Z/6VT91EActOf+AMxjsI+09d7IRYIvzr8OxMPYOHU2bglp3o86xZEMUXfcjB8Sw/8KMsCjBp3ABEN9/bwv1496aw9IC67ZBQ2cDDfgdBej5DAkT4NS2XIx7wbM7sBtLYjcXMi7w== administrator@ROA-CARAPETRU2"
$authKeysFile = "C:\ProgramData\ssh\administrators_authorized_keys"
Write-Host "Adding PRIMARY SYSTEM user SSH key to DR VM..." -ForegroundColor Cyan
# Check if key already exists
$currentContent = Get-Content $authKeysFile -ErrorAction SilentlyContinue
if ($currentContent -match "administrator@ROA-CARAPETRU2") {
Write-Host "Key already exists in authorized_keys" -ForegroundColor Yellow
} else {
# Add the key
Add-Content -Path $authKeysFile -Value $systemKey
Write-Host "Key added successfully" -ForegroundColor Green
}
# Show current keys
Write-Host ""
Write-Host "Current authorized keys:" -ForegroundColor Cyan
Get-Content $authKeysFile | ForEach-Object {
if ($_ -match "ssh-rsa .+ (.+)$") {
Write-Host " - $($matches[1])" -ForegroundColor White
}
}
# Restart SSH service
Write-Host ""
Write-Host "Restarting SSH service..." -ForegroundColor Yellow
Restart-Service sshd
Write-Host "SSH service restarted" -ForegroundColor Green
Write-Host ""
Write-Host "Done! SYSTEM user from PRIMARY can now connect via SSH." -ForegroundColor Green

View File

@@ -0,0 +1,133 @@
# Oracle Database Complete Cleanup Script (PowerShell)
# Purpose: Remove all database files and services to restore DR VM to clean state
# Run as: Administrator
# Location: D:\oracle\scripts\cleanup_database.ps1
#
# Parameters:
# /SILENT - Non-interactive mode
# /AFTER - Cleanup AFTER restore (shutdown instance + stop service before deleting files)
$ErrorActionPreference = "Continue"
$env:ORACLE_HOME = "C:\Users\Administrator\Downloads\WINDOWS.X64_193000_db_home"
$env:ORACLE_SID = "ROA"
$env:PATH = "$env:ORACLE_HOME\bin;$env:PATH"
Write-Host "============================================"
Write-Host "Oracle Database Cleanup Script"
Write-Host "============================================"
Write-Host ""
Write-Host "This script will:"
Write-Host " 1. Stop and delete Oracle service"
Write-Host " 2. Delete all database files (datafiles, control files, redo logs)"
Write-Host " 3. Delete local FRA (backups are on F:\, safe to delete)"
Write-Host " 4. Delete trace files"
Write-Host " 5. Leave VM in completely clean state (no service, no DB files)"
Write-Host ""
# Check parameters
$silent = $args -contains "/SILENT" -or $args -contains "/AUTO"
$afterRestore = $args -contains "/AFTER"
if (-not $silent) {
Write-Host "WARNING: This will DELETE the entire database!" -ForegroundColor Red
Write-Host "Starting cleanup in 3 seconds... (Press Ctrl+C to cancel)"
Start-Sleep -Seconds 3
}
# Create directories
New-Item -ItemType Directory -Path "D:\oracle\temp" -Force | Out-Null
New-Item -ItemType Directory -Path "D:\oracle\logs" -Force | Out-Null
Write-Host ""
Write-Host "[1/6] Shutting down database and stopping service..."
# Check if Oracle service exists
$service = Get-Service -Name "OracleServiceROA" -ErrorAction SilentlyContinue
if ($service) {
Write-Host " Oracle service found, ensuring clean shutdown..."
# Shutdown instance using SQL*Plus (always, not just /AFTER)
$shutdownSQL = "WHENEVER SQLERROR CONTINUE`nSHUTDOWN ABORT;`nEXIT;"
try {
$shutdownSQL | & sqlplus -S / as sysdba 2>&1 | Out-Null
Start-Sleep -Seconds 2
Write-Host " Instance shut down (ABORT for fast cleanup)"
} catch {
Write-Host " Shutdown command sent (errors ignored)"
}
# ALWAYS stop Oracle service to ensure clean state
if ($service.Status -eq "Running") {
Write-Host " Stopping Oracle service to ensure clean state..."
try {
Stop-Service -Name "OracleServiceROA" -Force -ErrorAction Stop
Start-Sleep -Seconds 3
Write-Host " Service stopped successfully"
} catch {
Write-Host " WARNING: Failed to stop service: $_" -ForegroundColor Yellow
}
} else {
Write-Host " Service already stopped"
}
# Force kill any remaining Oracle processes to ensure clean state
Write-Host " Cleaning up any remaining Oracle processes..."
Get-Process -Name "sqlplus" -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue
Get-Process -Name "oracle" -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue
Start-Sleep -Seconds 2
Write-Host " All Oracle processes terminated"
} else {
Write-Host " Oracle service not found, will be created during restore"
}
Write-Host "[2/6] Oracle service stopped (clean state for restore)"
Write-Host " Service will be started fresh during restore"
Write-Host " This ensures no state inconsistencies (prevents ORA-00600)"
Write-Host "[3/6] Deleting database files + SPFILE..."
Write-Host " Deleting datafiles..."
Remove-Item "C:\Users\oracle\oradata\ROA\*.dbf" -Force -ErrorAction SilentlyContinue
Write-Host " Deleting control files..."
Remove-Item "C:\Users\oracle\oradata\ROA\*.ctl" -Force -ErrorAction SilentlyContinue
Write-Host " Deleting redo logs..."
Remove-Item "C:\Users\oracle\oradata\ROA\*.log" -Force -ErrorAction SilentlyContinue
Write-Host " Deleting SPFILE (ensures PFILE-based startup)..."
Remove-Item "$env:ORACLE_HOME\database\SPFILE*.ORA" -Force -ErrorAction SilentlyContinue
Write-Host "[4/6] Deleting local FRA (backups are on F:\)..."
if (Test-Path "C:\Users\oracle\recovery_area\ROA") {
Remove-Item "C:\Users\oracle\recovery_area\ROA" -Recurse -Force -ErrorAction SilentlyContinue
New-Item -ItemType Directory -Path "C:\Users\oracle\recovery_area\ROA" -Force | Out-Null
Write-Host " FRA cleared"
} else {
New-Item -ItemType Directory -Path "C:\Users\oracle\recovery_area\ROA" -Force | Out-Null
Write-Host " FRA directory created"
}
Write-Host "[5/6] Deleting trace files (to save space)..."
Remove-Item "C:\Users\oracle\diag\rdbms\roa\ROA\trace\*.trc" -Force -ErrorAction SilentlyContinue
Remove-Item "C:\Users\oracle\diag\rdbms\roa\ROA\trace\*.trm" -Force -ErrorAction SilentlyContinue
Write-Host " Trace files deleted"
Write-Host ""
Write-Host "============================================"
Write-Host "Database Cleanup Complete!"
Write-Host "============================================"
Write-Host ""
Write-Host "Current state:"
Write-Host " [YES] Oracle software installed"
Write-Host " [YES] PFILE exists (C:\Users\oracle\admin\ROA\pfile\initROA.ora)"
Write-Host " [YES] Oracle service (STOPPED for clean restore)"
Write-Host " [NO] SPFILE (deleted to ensure PFILE startup)"
Write-Host " [NO] Database files (will be restored from backups)"
Write-Host " [NO] Control files (will be restored from backups)"
Write-Host " [NO] Datafiles (will be restored from backups)"
Write-Host ""
Write-Host "VM is now in CLEAN STATE (service stopped, ready for fresh start)!"
Write-Host ""
Write-Host "Next step: Run D:\oracle\scripts\rman_restore_from_zero.ps1"
Write-Host " (It will start the service fresh and restore the database)"
Write-Host ""
exit 0

View File

@@ -0,0 +1,158 @@
# Configure Oracle Listener on DR VM
# Run this script AFTER Oracle installation
# Run AS ADMINISTRATOR on DR VM (10.0.20.37)
$ErrorActionPreference = "Stop"
$ORACLE_HOME = "C:\Users\Administrator\Downloads\WINDOWS.X64_193000_db_home"
$ORACLE_BASE = "C:\Users\oracle"
$DR_IP = "10.0.20.37"
$LISTENER_PORT = 1521
Write-Host "=== Configure Oracle Listener on DR VM ===" -ForegroundColor Cyan
Write-Host ""
# Set environment
$env:ORACLE_HOME = $ORACLE_HOME
$env:ORACLE_BASE = $ORACLE_BASE
$env:PATH = "$ORACLE_HOME\bin;$env:PATH"
# Step 1: Create network admin directory
Write-Host "[1/5] Creating network admin directory..." -ForegroundColor Yellow
$netAdminDir = "$ORACLE_HOME\network\admin"
if (!(Test-Path $netAdminDir)) {
New-Item -ItemType Directory -Path $netAdminDir -Force | Out-Null
}
Write-Host " Directory: $netAdminDir" -ForegroundColor Green
# Step 2: Create listener.ora
Write-Host "[2/5] Creating listener.ora..." -ForegroundColor Yellow
$listenerOra = @"
# Listener Configuration for DR VM
# Generated: $(Get-Date -Format "yyyy-MM-dd HH:mm:ss")
LISTENER =
(DESCRIPTION_LIST =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = $DR_IP)(PORT = $LISTENER_PORT))
(ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC1521))
)
)
SID_LIST_LISTENER =
(SID_LIST =
(SID_DESC =
(GLOBAL_DBNAME = ROA)
(ORACLE_HOME = $($ORACLE_HOME -replace '\\', '/'))
(SID_NAME = ROA)
)
)
# Listener control parameters
INBOUND_CONNECT_TIMEOUT_LISTENER = 120
SUBSCRIBE_FOR_NODE_DOWN_EVENT_LISTENER = OFF
VALID_NODE_CHECKING_REGISTRATION_LISTENER = OFF
# Logging
LOG_DIRECTORY_LISTENER = $($ORACLE_BASE -replace '\\', '/')/diag/tnslsnr/ORACLE-DR/listener/alert
TRACE_DIRECTORY_LISTENER = $($ORACLE_BASE -replace '\\', '/')/diag/tnslsnr/ORACLE-DR/listener/trace
TRACE_LEVEL_LISTENER = OFF
"@
$listenerOra | Out-File -FilePath "$netAdminDir\listener.ora" -Encoding ASCII -Force
Write-Host " Created: $netAdminDir\listener.ora" -ForegroundColor Green
# Step 3: Create tnsnames.ora
Write-Host "[3/5] Creating tnsnames.ora..." -ForegroundColor Yellow
$tnsnamesOra = @"
# TNS Names Configuration for DR VM
# Generated: $(Get-Date -Format "yyyy-MM-dd HH:mm:ss")
ROA =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = $DR_IP)(PORT = $LISTENER_PORT))
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = ROA)
)
)
# Localhost connection
ROA_LOCAL =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = localhost)(PORT = $LISTENER_PORT))
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = ROA)
)
)
"@
$tnsnamesOra | Out-File -FilePath "$netAdminDir\tnsnames.ora" -Encoding ASCII -Force
Write-Host " Created: $netAdminDir\tnsnames.ora" -ForegroundColor Green
# Step 4: Create sqlnet.ora
Write-Host "[4/5] Creating sqlnet.ora..." -ForegroundColor Yellow
$sqlnetOra = @"
# SQL*Net Configuration for DR VM
# Generated: $(Get-Date -Format "yyyy-MM-dd HH:mm:ss")
NAMES.DIRECTORY_PATH = (TNSNAMES, EZCONNECT, HOSTNAME)
# Security settings
SQLNET.AUTHENTICATION_SERVICES = (NTS)
SQLNET.EXPIRE_TIME = 10
# Encryption (optional, enable if needed)
# SQLNET.ENCRYPTION_SERVER = REQUIRED
# SQLNET.CRYPTO_CHECKSUM_SERVER = REQUIRED
"@
$sqlnetOra | Out-File -FilePath "$netAdminDir\sqlnet.ora" -Encoding ASCII -Force
Write-Host " Created: $netAdminDir\sqlnet.ora" -ForegroundColor Green
# Step 5: Start listener
Write-Host "[5/5] Starting Oracle Listener..." -ForegroundColor Yellow
# Stop listener if already running
try {
& lsnrctl stop 2>&1 | Out-Null
Start-Sleep -Seconds 2
} catch {
# Listener not running, continue
}
# Start listener
try {
$output = & lsnrctl start 2>&1 | Out-String
if ($output -match "completed successfully" -or $output -match "successfully") {
Write-Host " Listener started successfully" -ForegroundColor Green
} else {
Write-Host " WARNING: Check listener status manually" -ForegroundColor Yellow
Write-Host $output -ForegroundColor Gray
}
} catch {
Write-Host " ERROR: Failed to start listener: $_" -ForegroundColor Red
exit 1
}
Write-Host ""
Write-Host "=== Listener Configuration Complete ===" -ForegroundColor Green
Write-Host ""
# Verify listener status
Write-Host "Listener Status:" -ForegroundColor Cyan
& lsnrctl status
Write-Host ""
Write-Host "Configuration files created:" -ForegroundColor Yellow
Write-Host " $netAdminDir\listener.ora" -ForegroundColor White
Write-Host " $netAdminDir\tnsnames.ora" -ForegroundColor White
Write-Host " $netAdminDir\sqlnet.ora" -ForegroundColor White
Write-Host ""
Write-Host "Test connectivity:" -ForegroundColor Yellow
Write-Host " tnsping ROA" -ForegroundColor White
Write-Host " sqlplus sys/password@ROA as sysdba" -ForegroundColor White
Write-Host ""
Write-Host "Next step: Create RMAN restore script" -ForegroundColor Cyan
Write-Host ""

View File

@@ -0,0 +1,78 @@
# Copy Existing SSH Key to Proxmox
# Rulează acest script pe PRIMARY ca Administrator
#
# Acest script copiază cheia publică SSH existentă din profilul SYSTEM pe Proxmox
param(
[string]$ProxmoxHost = "10.0.20.202",
[string]$ProxmoxUser = "root"
)
Write-Host "=========================================" -ForegroundColor Cyan
Write-Host "Copiere Cheie SSH Existentă → 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"
# Verifică dacă cheia există
if (-not (Test-Path $PublicKeyPath)) {
Write-Host "✗ Cheia publică nu există: $PublicKeyPath" -ForegroundColor Red
Write-Host " Scriptul trebuie rulat ca Administrator!" -ForegroundColor Yellow
exit 1
}
Write-Host "✓ Cheie publică găsită: $PublicKeyPath" -ForegroundColor Green
Write-Host ""
Write-Host "→ Copiez cheia publică pe Proxmox ($ProxmoxHost)..." -ForegroundColor Yellow
Write-Host " IMPORTANT: Vei fi întrebat de parola root pentru Proxmox!" -ForegroundColor Cyan
Write-Host ""
# Metodă simplă: folosim SCP pentru a copia fișierul temporar, apoi cat
$tempFile = "C:\Windows\Temp\temp_pubkey.pub"
Copy-Item $PublicKeyPath $tempFile -Force
# Copiază fișierul pe Proxmox
& scp $tempFile "${ProxmoxUser}@${ProxmoxHost}:/tmp/temp_pubkey.pub"
if ($LASTEXITCODE -ne 0) {
Write-Host "✗ Eroare la copierea fișierului cu SCP" -ForegroundColor Red
Remove-Item $tempFile -Force -ErrorAction SilentlyContinue
exit 1
}
# Adaugă cheia în 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 "✓ Cheie publică copiată pe Proxmox!" -ForegroundColor Green
} else {
Write-Host "✗ Eroare la adăugarea cheii în authorized_keys" -ForegroundColor Red
exit 1
}
Write-Host ""
Write-Host "→ Testez conexiunea SSH fără parolă..." -ForegroundColor Yellow
# Testează 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 "✓ Conexiune SSH funcționează fără parolă!" -ForegroundColor Green
} else {
Write-Host "✗ Conexiunea SSH nu funcționează direct din acest cont" -ForegroundColor Yellow
Write-Host " Dar cheia a fost adăugată - scheduled tasks (SYSTEM) ar trebui să funcționeze" -ForegroundColor Yellow
}
Write-Host ""
Write-Host "=========================================" -ForegroundColor Green
Write-Host "✓ 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 această cheie automat." -ForegroundColor Yellow
Write-Host ""

View File

@@ -0,0 +1,80 @@
# Fix SSH Keys by recreating through SSH service
# Run as Administrator on DR VM (10.0.20.37)
$ErrorActionPreference = "Stop"
Write-Host "=== Fix SSH Keys via Service Method ===" -ForegroundColor Cyan
Write-Host ""
# Step 1: Stop SSH service
Write-Host "[1/4] Stopping SSH service..." -ForegroundColor Yellow
Stop-Service sshd
Start-Sleep -Seconds 2
Write-Host " SSH service stopped" -ForegroundColor Green
# Step 2: Delete the problematic file while service is stopped
Write-Host "[2/4] Deleting old authorized_keys file..." -ForegroundColor Yellow
$authKeysFile = "C:\ProgramData\ssh\administrators_authorized_keys"
if (Test-Path $authKeysFile) {
# Try to take ownership first
takeown /F $authKeysFile /A
icacls $authKeysFile /grant Administrators:F
Remove-Item $authKeysFile -Force
Write-Host " Old file deleted" -ForegroundColor Green
} else {
Write-Host " File doesn't exist" -ForegroundColor Gray
}
# Step 3: Create new file with both keys
Write-Host "[3/4] Creating new authorized_keys file..." -ForegroundColor Yellow
$bothKeys = @"
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC88mX/oQnAoU54kazp6iYmmg91IR8pbnYV3aw5aJfSsiSBUjqo+XbvrWRvq//lli48k2kuNfq8olKrPvqKHcIccbcbgFrES5k2ErSyXjvbUlxuyHFRIfBoXvAhMMX6LZR+4Qc0i3VThQ1PgY0tYDbf2XQBAyrog5EU9H/q2NzJEulTs7kSR0FIt1goWXqKJYLA9Pn7Ardt7doPzR8EH/spB8hXctO0BaAorX3p3rd4bvOZoOcht4pTmyJBRzoZRRlscCZRCOxjQDk+y4v9eOPzwMc0dRlVxIbqt8Sua5khGTlmeQTmDqxCmdtgrTNWT4hwPVG1L4Jfw2bgX3IqCGKB4juDUF+Eh6hrQeuTIF7xbCIGGy9N/lKIKO3vr4sTf51gVM9CWJ0bE/CTKbiRPfWbUXIUA4yZ96gJf0QAqcIcutnntomdtkdV8G1RYVKSQEE4oxF3mCRxR+1d5Fn/UXGlms9Q2u/QAq7n5BYLPczUFSkdBdfITOqiCIzlX8WpPD7v/vt8Wsbyf24B/FSYvp+X0AcX5qQbNeljChAxqRy6VNhmh5ucUkMFxfUSTWij+AVqmCPvxVVFKPw32G6jN59BmwirmIxd0i6wTRj3rrUuyO/6+kjErjthkYKFIDBAgdCnV0rrkrPRNKmbS0DtgRcID3ILq2UqR3AYmDf2azf8hQ== mmarius28@gmail.com
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQD3EdHswdNDuDC9kJdUli2zGGPVlEWJjmqtb4eABYWwjQnWqjGp8oAFbQ+r2TxR544WtEyhDL9BU6oO3EFH957DBGQJHJvfRgx2VnkNZEzN/XX/7HK6Cp5rlINGGp26PjHulKkZjARmjC3YK0aUFEkiyNyUBqhtQpmcYP4+wjUfiiO2xUkF9mzGplbWGK3ZmEdkWNd5BNddqxmxyLvd2KHAo8F7Vux9SyPWqZ8bwiDyidAMDU7kCXS/RabUMl2LGajzFbRnR87YA7cIaVFl/IWExO/fsYlgkwmmmIAMdjINp0IWDdydnmG1XNNhM8h/BKY/eK3uile8CvyEbmbuf0+ePm3Ex9vTjn4jYN2vFE148FgQGGTibibJ+sXFoQ87VFNGEuog/V0aajVk/xcOihszsEvzD2IV/tSKJFdI6klnYLuieuMZf7Dvs/6sC1l3dnsBtcpvjnU48altRRZvQzaJf3gIkG1lRGBgyW1n+WHe/7StIveYTVNFtx+fcnqB8gm9fZQxBp2WRbLNFpY/Qj+6BF66b1A2ZxH/3F9Z/6VT91EActOf+AMxjsI+09d7IRYIvzr8OxMPYOHU2bglp3o86xZEMUXfcjB8Sw/8KMsCjBp3ABEN9/bwv1496aw9IC67ZBQ2cDDfgdBej5DAkT4NS2XIx7wbM7sBtLYjcXMi7w== administrator@ROA-CARAPETRU2
"@
# Create the file
$bothKeys | Out-File -FilePath $authKeysFile -Encoding ASCII -NoNewline -Force
# Set permissions using icacls (more reliable than PowerShell ACL)
icacls $authKeysFile /inheritance:r
icacls $authKeysFile /grant "NT AUTHORITY\SYSTEM:(F)"
icacls $authKeysFile /grant "BUILTIN\Administrators:(R)"
Write-Host " New file created with correct permissions" -ForegroundColor Green
# Step 4: Start SSH service
Write-Host "[4/4] Starting SSH service..." -ForegroundColor Yellow
Start-Service sshd
Start-Sleep -Seconds 2
Write-Host " SSH service started" -ForegroundColor Green
# Verification
Write-Host ""
Write-Host "=== Verification ===" -ForegroundColor Cyan
Write-Host ""
Write-Host "File permissions:" -ForegroundColor Yellow
icacls $authKeysFile
Write-Host ""
Write-Host "File content (number of lines):" -ForegroundColor Yellow
$lines = Get-Content $authKeysFile
Write-Host " Total keys: $($lines.Count)" -ForegroundColor White
foreach ($line in $lines) {
if ($line -match "ssh-rsa .+ (.+)$") {
Write-Host "$($matches[1])" -ForegroundColor Green
}
}
Write-Host ""
Write-Host "SSH service status:" -ForegroundColor Yellow
Get-Service sshd | Format-Table Name, Status, StartType -AutoSize
Write-Host ""
Write-Host "=== Setup Complete ===" -ForegroundColor Green
Write-Host ""
Write-Host "Next: Test SSH connection from PRIMARY server" -ForegroundColor Cyan
Write-Host ""

View File

@@ -0,0 +1,34 @@
# Initialization Parameters for ROA Database - DR VM
# Generated: 2025-10-09 04:07:45
# Database Identification
db_name=ROA
db_unique_name=ROA
# Memory Configuration
memory_target=1024M
memory_max_target=1024M
# File Locations
control_files=('C:\Users\oracle\oradata\ROA\control01.ctl', 'C:\Users\oracle\recovery_area\ROA\control02.ctl')
db_recovery_file_dest='C:\Users\oracle\recovery_area'
db_recovery_file_dest_size=20G
audit_file_dest='C:\Users\oracle\admin\ROA\adump'
# Redo and Archive Log
log_archive_format=%t_%s_%r.dbf
# Compatibility
compatible=19.0.0
# Character Set
nls_language=AMERICAN
nls_territory=AMERICA
# Processes and Sessions
processes=300
sessions=472
# Miscellaneous
diagnostic_dest='C:\Users\oracle'
_allow_resetlogs_corruption=TRUE

View File

@@ -0,0 +1,512 @@
#!/bin/bash
#
# Oracle Backup Monitor for Proxmox with PVE::Notify
# Monitors Oracle backups and sends notifications via Proxmox notification system
#
# Location: /opt/scripts/oracle-backup-monitor-proxmox.sh (on Proxmox host)
# Schedule: Add to cron for daily execution
#
# This script is SELF-SUFFICIENT:
# - Automatically creates notification templates if they don't exist
# - Uses Proxmox native notification system (same as HA alerts)
# - No email configuration needed - uses existing Proxmox setup
#
# Installation:
# cp oracle-backup-monitor-proxmox.sh /opt/scripts/
# chmod +x /opt/scripts/oracle-backup-monitor-proxmox.sh
# /opt/scripts/oracle-backup-monitor-proxmox.sh --install # Creates templates
# crontab -e # Add: 0 9 * * * /opt/scripts/oracle-backup-monitor-proxmox.sh
#
# Author: Claude (based on ha-monitor.sh pattern)
# Version: 1.0
set -euo pipefail
# Configuration
PRIMARY_HOST="10.0.20.36"
PRIMARY_PORT="22122"
PRIMARY_USER="Administrator"
BACKUP_PATH="/mnt/pve/oracle-backups/ROA/autobackup"
MAX_FULL_AGE_HOURS=25
MAX_CUMULATIVE_AGE_HOURS=7
TEMPLATE_DIR="/usr/share/pve-manager/templates/default"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
# Function to create notification templates
create_templates() {
echo -e "${GREEN}Creating Oracle backup notification templates...${NC}"
# Create templates directory if needed
mkdir -p "$TEMPLATE_DIR"
# Subject template
cat > "$TEMPLATE_DIR/oracle-backup-subject.txt.hbs" <<'EOF'
Oracle Backup {{status}} | {{node}}
EOF
# Text body template
cat > "$TEMPLATE_DIR/oracle-backup-body.txt.hbs" <<'EOF'
Oracle Backup {{status}} | {{node}}
Date: {{date}}
SUMMARY
- Full backup: {{full_backup_age}}h (limit {{full_backup_limit}}h) -> {{#if full_backup_ok}}OK{{else}}CHECK{{/if}}
- Incremental: {{cumulative_backup_age}}h (limit {{cumulative_backup_limit}}h) -> {{#if cumulative_backup_ok}}OK{{else}}CHECK{{/if}}
- Backups: {{total_backups}} files ({{total_size_label}})
- Disk usage: {{disk_usage}}%
{{#if has_errors}}
ISSUES
{{#each errors}}
- {{this}}
{{/each}}
{{/if}}
{{#if has_warnings}}
WARNINGS
{{#each warnings}}
- {{this}}
{{/each}}
{{/if}}
FULL BACKUPS ({{full_backup_count}} files)
{{#if has_full_backups}}
{{#each full_backup_list}}
- {{this}}
{{/each}}
{{else}}
- none detected
{{/if}}
INCREMENTAL BACKUPS ({{incr_backup_count}} files)
{{#if has_incr_backups}}
{{#each incr_backup_list}}
- {{this}}
{{/each}}
{{else}}
- none detected
{{/if}}
Next check: +24h via Proxmox Monitor
EOF
# HTML body template (lightweight Gmail-friendly)
cat > "$TEMPLATE_DIR/oracle-backup-body.html.hbs" <<'EOF'
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Oracle Backup {{status}} | {{node}}</title>
</head>
<body style="margin:0;padding:16px;font-family:Arial,Helvetica,sans-serif;background:#ffffff;color:#2c3e50;">
<table style="width:100%;max-width:640px;margin:0 auto;border-collapse:collapse;">
<tr>
<td style="padding:0 0 12px 0;font-size:18px;font-weight:600;">
Oracle Backup {{status}} | {{node}}
</td>
</tr>
<tr>
<td style="padding:0 0 16px 0;font-size:13px;color:#6c757d;">
{{date}}
</td>
</tr>
<tr>
<td style="padding:12px;border:1px solid #e1e4e8;border-radius:4px;">
<table style="width:100%;border-collapse:collapse;font-size:14px;">
<tr>
<td style="padding:4px 0;">Full backup</td>
<td style="padding:4px 0;text-align:right;">
{{full_backup_age}}h / {{full_backup_limit}}h · {{#if full_backup_ok}}OK{{else}}CHECK{{/if}}
</td>
</tr>
<tr>
<td style="padding:4px 0;">Incremental</td>
<td style="padding:4px 0;text-align:right;">
{{cumulative_backup_age}}h / {{cumulative_backup_limit}}h · {{#if cumulative_backup_ok}}OK{{else}}CHECK{{/if}}
</td>
</tr>
<tr>
<td style="padding:4px 0;">Backups</td>
<td style="padding:4px 0;text-align:right;">{{total_backups}} files ({{total_size_label}})</td>
</tr>
<tr>
<td style="padding:4px 0;">Disk usage</td>
<td style="padding:4px 0;text-align:right;">{{disk_usage}}%</td>
</tr>
</table>
</td>
</tr>
{{#if has_errors}}
<tr>
<td style="padding:16px 0 0 0;">
<table style="width:100%;border-collapse:collapse;font-size:14px;background:#fff5f5;border:1px solid #f1b0b7;border-radius:4px;">
<tr><td style="padding:8px 12px;font-weight:600;color:#c82333;">Issues</td></tr>
{{#each errors}}
<tr><td style="padding:6px 12px;border-top:1px solid #f8d7da;">• {{this}}</td></tr>
{{/each}}
</table>
</td>
</tr>
{{/if}}
{{#if has_warnings}}
<tr>
<td style="padding:16px 0 0 0;">
<table style="width:100%;border-collapse:collapse;font-size:14px;background:#fff8e5;border:1px solid #ffe8a1;border-radius:4px;">
<tr><td style="padding:8px 12px;font-weight:600;color:#856404;">Warnings</td></tr>
{{#each warnings}}
<tr><td style="padding:6px 12px;border-top:1px solid #ffe8a1;">• {{this}}</td></tr>
{{/each}}
</table>
</td>
</tr>
{{/if}}
<tr>
<td style="padding:16px 0 0 0;">
<table style="width:100%;border-collapse:collapse;font-size:13px;border:1px solid #e1e4e8;border-radius:4px;background:#f9fafb;">
<tr><td style="padding:8px 12px;font-weight:600;">FULL Backups ({{full_backup_count}} files)</td></tr>
{{#if has_full_backups}}
{{#each full_backup_list}}
<tr><td style="padding:6px 12px;border-top:1px solid #e1e4e8;">• {{this}}</td></tr>
{{/each}}
{{else}}
<tr><td style="padding:6px 12px;border-top:1px solid #e1e4e8;">• none detected</td></tr>
{{/if}}
</table>
</td>
</tr>
<tr>
<td style="padding:16px 0 0 0;">
<table style="width:100%;border-collapse:collapse;font-size:13px;border:1px solid #e1e4e8;border-radius:4px;background:#f9fafb;">
<tr><td style="padding:8px 12px;font-weight:600;">INCREMENTAL Backups ({{incr_backup_count}} files)</td></tr>
{{#if has_incr_backups}}
{{#each incr_backup_list}}
<tr><td style="padding:6px 12px;border-top:1px solid #e1e4e8;">• {{this}}</td></tr>
{{/each}}
{{else}}
<tr><td style="padding:6px 12px;border-top:1px solid #e1e4e8;">• none detected</td></tr>
{{/if}}
</table>
</td>
</tr>
<tr>
<td style="padding:16px 0 0 0;font-size:12px;color:#6c757d;">
Next automated check: +24h via Proxmox Monitor
</td>
</tr>
</table>
</body>
</html>
EOF
echo -e "${GREEN}Templates created successfully in $TEMPLATE_DIR${NC}"
}
# Function to send notification via PVE::Notify
send_pve_notification() {
local severity="$1"
local status="$2"
local data="$3"
# Create Perl script to call PVE::Notify
cat > /tmp/oracle-notify.pl <<'PERL_SCRIPT'
#!/usr/bin/perl
use strict;
use warnings;
use PVE::Notify;
use JSON;
my $json_data = do { local $/; <STDIN> };
my $data = decode_json($json_data);
my $severity = $data->{severity} // 'info';
my $template_name = 'oracle-backup';
# Add fields for matching rules
my $fields = {
type => 'oracle-backup',
severity => $severity,
hostname => $data->{node} // 'unknown',
};
# Send notification
eval {
PVE::Notify::notify(
$severity,
$template_name,
$data,
$fields
);
};
if ($@) {
print "Error sending notification: $@\n";
exit 1;
}
print "Notification sent successfully\n";
PERL_SCRIPT
chmod +x /tmp/oracle-notify.pl
# Send notification
echo "$data" | perl /tmp/oracle-notify.pl
rm -f /tmp/oracle-notify.pl
}
# Function to check backups
check_backups() {
local status="OK"
local errors=()
local warnings=()
echo "Checking Oracle backups..."
local total_backups=0
local total_size_label="0G"
local full_age_hours="N/A"
local cumulative_age_hours="N/A"
local full_backup_ok=false
local cumulative_backup_ok=false
local disk_usage=0
local -a backup_entries=()
if [ ! -d "$BACKUP_PATH" ]; then
status="ERROR"
errors+=("Backup path $BACKUP_PATH not accessible")
else
if compgen -G "$BACKUP_PATH"/*.BKP > /dev/null; then
total_backups=$(find "$BACKUP_PATH" -maxdepth 1 -type f -name '*.BKP' | wc -l)
total_backups=${total_backups//[[:space:]]/}
[ -z "$total_backups" ] && total_backups=0
local total_size=$(du -shc "$BACKUP_PATH"/*.BKP 2>/dev/null | tail -1 | awk '{print $1}')
[ -z "$total_size" ] && total_size="0G"
total_size_label="$total_size"
# 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)
full_age_hours=$(( (current_timestamp - full_timestamp) / 3600 ))
if [ "$full_age_hours" -gt "$MAX_FULL_AGE_HOURS" ]; then
status="WARNING"
warnings+=("FULL backup is $full_age_hours hours old (threshold: $MAX_FULL_AGE_HOURS)")
else
full_backup_ok=true
fi
else
status="ERROR"
errors+=("No FULL backup found")
fi
# 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)
cumulative_age_hours=$(( (current_timestamp - cumulative_timestamp) / 3600 ))
if [ "$cumulative_age_hours" -gt "$MAX_CUMULATIVE_AGE_HOURS" ]; then
if [ "$status" != "ERROR" ]; then status="WARNING"; fi
warnings+=("CUMULATIVE backup is $cumulative_age_hours hours old (threshold: $MAX_CUMULATIVE_AGE_HOURS)")
else
cumulative_backup_ok=true
fi
fi
# 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' -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")
local backup_time=$(date -r "$backup_file" '+%Y-%m-%d %H:%M')
local backup_size=$(du -sh "$backup_file" 2>/dev/null | cut -f1)
[ -z "$backup_size" ] && backup_size="N/A"
full_backup_entries+=("$backup_time | $backup_name | $backup_size")
done
fi
# 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' -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")
local backup_time=$(date -r "$backup_file" '+%Y-%m-%d %H:%M')
local backup_size=$(du -sh "$backup_file" 2>/dev/null | cut -f1)
[ -z "$backup_size" ] && backup_size="N/A"
incr_backup_entries+=("$backup_time | $backup_name | $backup_size")
done
fi
else
status="ERROR"
errors+=("No backup files found in $BACKUP_PATH")
fi
local disk_usage_raw=$(df "$BACKUP_PATH" 2>/dev/null | tail -1 | awk '{print int($5)}')
if [ -n "$disk_usage_raw" ]; then
disk_usage="$disk_usage_raw"
else
if [ "$status" = "OK" ]; then status="WARNING"; fi
warnings+=("Unable to determine disk usage for $BACKUP_PATH")
fi
fi
if [ "$disk_usage" -gt 90 ]; then
status="ERROR"
errors+=("Disk usage critical: ${disk_usage}%")
elif [ "$disk_usage" -gt 80 ]; then
if [ "$status" != "ERROR" ]; then status="WARNING"; fi
warnings+=("Disk usage high: ${disk_usage}%")
fi
local severity="info"
[ "$status" = "WARNING" ] && severity="warning"
[ "$status" = "ERROR" ] && severity="error"
local errors_json
if [ ${#errors[@]} -eq 0 ]; then
errors_json='[]'
else
errors_json=$(printf '%s\n' "${errors[@]}" | jq -R . | jq -s .)
fi
local warnings_json
if [ ${#warnings[@]} -eq 0 ]; then
warnings_json='[]'
else
warnings_json=$(printf '%s\n' "${warnings[@]}" | jq -R . | jq -s .)
fi
local full_backup_list_json
if [ ${#full_backup_entries[@]} -eq 0 ]; then
full_backup_list_json='[]'
else
full_backup_list_json=$(printf '%s\n' "${full_backup_entries[@]}" | jq -R . | jq -s .)
fi
local incr_backup_list_json
if [ ${#incr_backup_entries[@]} -eq 0 ]; then
incr_backup_list_json='[]'
else
incr_backup_list_json=$(printf '%s\n' "${incr_backup_entries[@]}" | jq -R . | jq -s .)
fi
local has_errors=false
local has_warnings=false
local has_full_backups=false
local has_incr_backups=false
[ ${#errors[@]} -gt 0 ] && has_errors=true
[ ${#warnings[@]} -gt 0 ] && has_warnings=true
[ ${#full_backup_entries[@]} -gt 0 ] && has_full_backups=true
[ ${#incr_backup_entries[@]} -gt 0 ] && has_incr_backups=true
local json_data=$(cat <<JSON
{
"severity": "$severity",
"node": "$(hostname)",
"date": "$(date +'%Y-%m-%d %H:%M:%S')",
"status": "$status",
"errors": $errors_json,
"warnings": $warnings_json,
"has_errors": $has_errors,
"has_warnings": $has_warnings,
"total_backups": $total_backups,
"total_size_gb": "${total_size_label%G}",
"total_size_label": "$total_size_label",
"full_backup_age": "${full_age_hours}",
"cumulative_backup_age": "${cumulative_age_hours}",
"disk_usage": "${disk_usage}",
"full_backup_ok": $([ "$full_backup_ok" = "true" ] && echo "true" || echo "false"),
"cumulative_backup_ok": $([ "$cumulative_backup_ok" = "true" ] && echo "true" || echo "false"),
"is_error": $([ "$status" = "ERROR" ] && echo "true" || echo "false"),
"is_warning": $([ "$status" = "WARNING" ] && echo "true" || echo "false"),
"full_backup_list": $full_backup_list_json,
"incr_backup_list": $incr_backup_list_json,
"has_full_backups": $has_full_backups,
"has_incr_backups": $has_incr_backups,
"full_backup_count": ${#full_backup_entries[@]},
"incr_backup_count": ${#incr_backup_entries[@]},
"full_backup_limit": "$MAX_FULL_AGE_HOURS",
"cumulative_backup_limit": "$MAX_CUMULATIVE_AGE_HOURS"
}
JSON
)
if [ "$status" != "OK" ]; then
echo -e "${YELLOW}Issues detected, sending notification...${NC}"
send_pve_notification "$severity" "$status" "$json_data"
else
echo -e "${GREEN}All backups are healthy${NC}"
# Optionally send success notification (uncomment if desired)
# send_pve_notification "info" "$status" "$json_data"
fi
echo "Status: $status"
echo "Total backups: $total_backups"
echo "Total size: $total_size_label"
echo "FULL backup age: $full_age_hours hours"
echo "CUMULATIVE backup age: $cumulative_age_hours hours"
echo "Disk usage: ${disk_usage}%"
}
# Main execution
main() {
case "${1:-}" in
--install)
create_templates
echo ""
echo -e "${GREEN}Installation complete!${NC}"
echo "Next steps:"
echo "1. Test the monitor: /opt/scripts/oracle-backup-monitor-proxmox.sh"
echo "2. Add to cron: crontab -e"
echo " Add line: 0 9 * * * /opt/scripts/oracle-backup-monitor-proxmox.sh"
echo "3. Configure notifications in Proxmox GUI if needed:"
echo " Datacenter > Notifications > Add matching rules for 'oracle-backup'"
;;
--help)
echo "Oracle Backup Monitor for Proxmox"
echo "Usage:"
echo " $0 - Check backups and send alerts if issues found"
echo " $0 --install - Create notification templates"
echo " $0 --help - Show this help"
;;
*)
# Check if templates exist, create if missing
if [ ! -f "$TEMPLATE_DIR/oracle-backup-subject.txt.hbs" ]; then
echo -e "${YELLOW}Templates not found, creating...${NC}"
create_templates
echo ""
fi
# Run backup check
check_backups
;;
esac
}
# Check dependencies
if ! command -v jq &> /dev/null; then
echo -e "${RED}Error: jq is not installed${NC}"
echo "Install with: apt-get install jq"
exit 1
fi
main "$@"

View File

@@ -0,0 +1,43 @@
@echo off
setlocal enabledelayedexpansion
REM ===================================================================
REM Oracle RMAN FULL Backup with Logging
REM Runs at: 02:30 AM (scheduled task)
REM Duration: ~30 minutes
REM ===================================================================
REM Get script directory (where this .bat file is located)
set SCRIPTDIR=%~dp0
set LOGDIR=%SCRIPTDIR%logs
set TIMESTAMP=%date:~-4,4%%date:~-7,2%%date:~-10,2%_%time:~0,2%%time:~3,2%%time:~6,2%
set TIMESTAMP=%TIMESTAMP: =0%
set LOGFILE=%LOGDIR%\backup_full_%TIMESTAMP%.log
REM Create log directory if not exists
if not exist "%LOGDIR%" mkdir "%LOGDIR%"
echo ========================================= >> "%LOGFILE%" 2>&1
echo Oracle RMAN FULL Backup Started >> "%LOGFILE%" 2>&1
echo Date: %date% %time% >> "%LOGFILE%" 2>&1
echo Script Directory: %SCRIPTDIR% >> "%LOGFILE%" 2>&1
echo ========================================= >> "%LOGFILE%" 2>&1
echo. >> "%LOGFILE%" 2>&1
REM Run RMAN backup - show in console AND save to log (using PowerShell tee)
powershell -Command "& cmd.exe /c 'rman target sys/romfastsoft@roa cmdfile=\"%SCRIPTDIR%rman_backup.txt\"' 2>&1 | Tee-Object -FilePath '%LOGFILE%' -Append"
set EXITCODE=%ERRORLEVEL%
echo. >> "%LOGFILE%" 2>&1
echo ========================================= >> "%LOGFILE%" 2>&1
echo Oracle RMAN FULL Backup Completed >> "%LOGFILE%" 2>&1
echo Date: %date% %time% >> "%LOGFILE%" 2>&1
echo Exit Code: %EXITCODE% >> "%LOGFILE%" 2>&1
echo ========================================= >> "%LOGFILE%" 2>&1
REM Print summary to console
echo [%date% %time%] RMAN FULL backup completed with exit code: %EXITCODE%
echo [%date% %time%] Log file: %LOGFILE%
exit /b %EXITCODE%

View File

@@ -0,0 +1,30 @@
RUN {
CONFIGURE RETENTION POLICY TO REDUNDANCY 2;
CONFIGURE CONTROLFILE AUTOBACKUP ON;
CONFIGURE COMPRESSION ALGORITHM 'BASIC';
# Full backup COMPRESSED + Archive logs (șterge logs după backup)
# FORMAT: L0_<dbname>_<YYYYMMDD>_<set#>_<piece#>
# Files will be stored in recovery area for easier transfer to DR
BACKUP AS COMPRESSED BACKUPSET
INCREMENTAL LEVEL 0
TAG 'DAILY_FULL_COMPRESSED'
FORMAT 'C:\Users\oracle\recovery_area\ROA\autobackup\L0_%d_%T_%s_%p.BKP'
DATABASE
PLUS ARCHIVELOG DELETE INPUT
FORMAT 'C:\Users\oracle\recovery_area\ROA\autobackup\ARC_%d_%T_%s_%p.BKP';
# Backup SPFILE și Control File
BACKUP AS COMPRESSED BACKUPSET
TAG 'SPFILE_BACKUP'
FORMAT 'C:\Users\oracle\recovery_area\ROA\autobackup\SPFILE_%d_%T_%s_%p.BKP'
SPFILE;
BACKUP
TAG 'CONTROLFILE_BACKUP'
FORMAT 'C:\Users\oracle\recovery_area\ROA\autobackup\CF_%d_%T_%s_%p.BKP'
CURRENT CONTROLFILE;
# Cleanup old backups (păstrează ultimele 2 - REDUNDANCY 2)
DELETE NOPROMPT OBSOLETE;
}

View File

@@ -0,0 +1,43 @@
@echo off
setlocal enabledelayedexpansion
REM ===================================================================
REM Oracle RMAN INCREMENTAL/CUMULATIVE Backup with Logging
REM Runs at: 13:00 and 18:00 (scheduled tasks)
REM Duration: ~5-10 minutes
REM ===================================================================
REM Get script directory (where this .bat file is located)
set SCRIPTDIR=%~dp0
set LOGDIR=%SCRIPTDIR%logs
set TIMESTAMP=%date:~-4,4%%date:~-7,2%%date:~-10,2%_%time:~0,2%%time:~3,2%%time:~6,2%
set TIMESTAMP=%TIMESTAMP: =0%
set LOGFILE=%LOGDIR%\backup_incremental_%TIMESTAMP%.log
REM Create log directory if not exists
if not exist "%LOGDIR%" mkdir "%LOGDIR%"
echo ========================================= >> "%LOGFILE%" 2>&1
echo Oracle RMAN INCREMENTAL Backup Started >> "%LOGFILE%" 2>&1
echo Date: %date% %time% >> "%LOGFILE%" 2>&1
echo Script Directory: %SCRIPTDIR% >> "%LOGFILE%" 2>&1
echo ========================================= >> "%LOGFILE%" 2>&1
echo. >> "%LOGFILE%" 2>&1
REM Run RMAN backup - show in console AND save to log (using PowerShell tee)
powershell -Command "& cmd.exe /c 'rman target sys/romfastsoft@roa cmdfile=\"%SCRIPTDIR%rman_backup_incremental.txt\"' 2>&1 | Tee-Object -FilePath '%LOGFILE%' -Append"
set EXITCODE=%ERRORLEVEL%
echo. >> "%LOGFILE%" 2>&1
echo ========================================= >> "%LOGFILE%" 2>&1
echo Oracle RMAN INCREMENTAL Backup Completed >> "%LOGFILE%" 2>&1
echo Date: %date% %time% >> "%LOGFILE%" 2>&1
echo Exit Code: %EXITCODE% >> "%LOGFILE%" 2>&1
echo ========================================= >> "%LOGFILE%" 2>&1
REM Print summary to console
echo [%date% %time%] RMAN INCREMENTAL backup completed with exit code: %EXITCODE%
echo [%date% %time%] Log file: %LOGFILE%
exit /b %EXITCODE%

View File

@@ -0,0 +1,26 @@
RUN {
# Incremental Level 1 CUMULATIVE backup
# Backup doar modificările de la ultimul Level 0 (full backup de la 02:00 AM)
# FORMAT: L1_<dbname>_<YYYYMMDD>_<set#>_<piece#>
# Files will be stored in recovery area for easier transfer to DR
BACKUP AS COMPRESSED BACKUPSET
INCREMENTAL LEVEL 1 CUMULATIVE
TAG 'MIDDAY_INCREMENTAL'
FORMAT 'C:\Users\oracle\recovery_area\ROA\autobackup\L1_%d_%T_%s_%p.BKP'
DATABASE
PLUS ARCHIVELOG DELETE INPUT
FORMAT 'C:\Users\oracle\recovery_area\ROA\autobackup\ARC_%d_%T_%s_%p.BKP';
# Backup SPFILE și controlfile (pentru siguranță)
BACKUP AS COMPRESSED BACKUPSET
TAG 'SPFILE_BACKUP'
FORMAT 'C:\Users\oracle\recovery_area\ROA\autobackup\SPFILE_%d_%T_%s_%p.BKP'
SPFILE;
BACKUP
TAG 'CONTROLFILE_BACKUP'
FORMAT 'C:\Users\oracle\recovery_area\ROA\autobackup\CF_%d_%T_%s_%p.BKP'
CURRENT CONTROLFILE;
# NU ștergem obsolete aici - se face la full backup
}

View File

@@ -0,0 +1,550 @@
# RMAN Restore Database FROM ZERO - Clean State (PowerShell)
# Backups are on F:\ (NFS mount from Proxmox host)
# Run as: Administrator
# Location: D:\oracle\scripts\rman_restore_from_zero.ps1
#
# Parameters:
# -TestMode: Skip service reconfiguration and Listener startup (for weekly DR tests)
param(
[switch]$TestMode
)
$ErrorActionPreference = "Continue"
$env:ORACLE_HOME = "C:\Users\Administrator\Downloads\WINDOWS.X64_193000_db_home"
$env:ORACLE_SID = "ROA"
$env:PATH = "$env:ORACLE_HOME\bin;$env:PATH"
Write-Host "============================================"
Write-Host "RMAN Database Restore FROM ZERO"
Write-Host "============================================"
Write-Host ""
Write-Host "Database: ROA"
Write-Host "DBID: 1363569330"
Write-Host "Backups: F:\ROA\autobackup (NFS mount from Proxmox)"
Write-Host ""
Write-Host "This script will:"
Write-Host " 1. CLEANUP: Delete any existing database files"
Write-Host " 2. RESTORE: Restore from F:\ backups"
Write-Host " 3. VERIFY: Check database is working"
Write-Host ""
# Verify F:\ mount is accessible
if (-not (Test-Path "F:\ROA\autobackup")) {
Write-Host "ERROR: F:\ROA\autobackup not accessible!" -ForegroundColor Red
Write-Host ""
Write-Host "Please verify:"
Write-Host " 1. F:\ drive is mounted: dir F:\"
Write-Host " 2. NFS mount command: mount -o rw,nolock,mtype=hard,timeout=60 10.0.20.202:/mnt/pve/oracle-backups F:"
Write-Host " 3. Proxmox host is reachable: ping 10.0.20.202"
Write-Host ""
exit 1
}
Write-Host "[OK] F:\ROA\autobackup is accessible"
Write-Host ""
# Create directories with proper permissions
try {
New-Item -ItemType Directory -Path "D:\oracle\temp" -Force -ErrorAction Stop | Out-Null
New-Item -ItemType Directory -Path "D:\oracle\logs" -Force -ErrorAction Stop | Out-Null
Write-Host "[OK] Created required directories"
} catch {
Write-Host "ERROR: Failed to create directories: $_" -ForegroundColor Red
exit 1
}
Write-Host "============================================"
Write-Host "STEP 1: CLEANUP - Delete existing database"
Write-Host "============================================"
Write-Host ""
Write-Host "Calling cleanup_database.ps1..."
Write-Host ""
# Call cleanup script with /SILENT flag
& "D:\oracle\scripts\cleanup_database.ps1" /SILENT
if ($LASTEXITCODE -ne 0) {
Write-Host ""
Write-Host "ERROR: Cleanup failed!" -ForegroundColor Red
exit 1
}
Write-Host ""
Write-Host "[OK] Cleanup complete - VM is in clean state"
Write-Host ""
Write-Host "Starting restore in 2 seconds..."
Start-Sleep -Seconds 2
Write-Host "============================================"
Write-Host "STEP 2: RESTORE - Restore from F:\ backups"
Write-Host "============================================"
Write-Host ""
# Step 2.1: Check Oracle service (create only if missing)
Write-Host "[2.1] Checking Oracle service..."
$service = Get-Service -Name "OracleServiceROA" -ErrorAction SilentlyContinue
if ($service) {
Write-Host "[OK] Oracle service already exists, skipping creation (15s saved!)" -ForegroundColor Green
Write-Host " Service will be reused for this restore"
# Ensure service is running (required for SQL*Plus connection)
if ($service.Status -ne "Running") {
Write-Host " Service is stopped, starting it (may take 30-60 seconds)..."
# Start service in background job to avoid blocking
$startJob = Start-Job -ScriptBlock {
Start-Service -Name "OracleServiceROA" -ErrorAction SilentlyContinue
}
# Poll for service status with timeout
$maxWait = 60
$elapsed = 0
$pollInterval = 3
$serviceStarted = $false
while ($elapsed -lt $maxWait) {
Start-Sleep -Seconds $pollInterval
$elapsed += $pollInterval
# Refresh service status
$currentService = Get-Service -Name "OracleServiceROA" -ErrorAction SilentlyContinue
if ($currentService -and $currentService.Status -eq "Running") {
$serviceStarted = $true
Write-Host " [OK] Service started successfully after $elapsed seconds"
break
}
if ($elapsed % 15 -eq 0) {
Write-Host " Still waiting for service to start... ($elapsed/$maxWait seconds)"
}
}
# Cleanup background job
Stop-Job -Job $startJob -ErrorAction SilentlyContinue
Remove-Job -Job $startJob -ErrorAction SilentlyContinue
if (-not $serviceStarted) {
Write-Host " WARNING: Service did not start within $maxWait seconds" -ForegroundColor Yellow
Write-Host " This may cause SQL*Plus connection issues (ORA-12560)"
Write-Host " Attempting to continue anyway..."
}
} else {
Write-Host " Service is already running"
}
} else {
Write-Host " Oracle service not found, creating from PFILE..."
# Check if PFILE exists, create if missing
$pfilePath = "C:\Users\oracle\admin\ROA\pfile\initROA.ora"
if (-not (Test-Path $pfilePath)) {
Write-Host " PFILE not found, creating default initROA.ora..." -ForegroundColor Yellow
# Create directory if needed
$pfileDir = Split-Path $pfilePath -Parent
if (-not (Test-Path $pfileDir)) {
New-Item -ItemType Directory -Path $pfileDir -Force | Out-Null
}
# Create PFILE with tested configuration
$pfileContent = @"
# Initialization Parameters for ROA Database - DR VM
# Auto-generated by rman_restore_from_zero.ps1 - $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')
# Database Identification
db_name=ROA
db_unique_name=ROA
# Memory Configuration
memory_target=1024M
memory_max_target=1024M
# File Locations
control_files=('C:\Users\oracle\oradata\ROA\control01.ctl', 'C:\Users\oracle\recovery_area\ROA\control02.ctl')
db_recovery_file_dest='C:\Users\oracle\recovery_area'
db_recovery_file_dest_size=50G
audit_file_dest='C:\Users\oracle\admin\ROA\adump'
# Redo and Archive Log
log_archive_format=%t_%s_%r.dbf
# Compatibility
compatible=19.0.0
# Character Set
nls_language=AMERICAN
nls_territory=AMERICA
# Processes and Sessions
processes=300
sessions=472
# Miscellaneous
diagnostic_dest='C:\Users\oracle'
_allow_resetlogs_corruption=TRUE
"@
try {
$pfileContent | Out-File -FilePath $pfilePath -Encoding ASCII -ErrorAction Stop
Write-Host " [OK] Created PFILE: $pfilePath" -ForegroundColor Green
} catch {
Write-Host "ERROR: Failed to create PFILE: $_" -ForegroundColor Red
exit 1
}
}
& oradim -new -sid ROA -startmode auto -pfile $pfilePath 2>&1 | Out-Null
if ($LASTEXITCODE -ne 0) {
Write-Host "ERROR: Failed to create Oracle service" -ForegroundColor Red
exit 1
}
Write-Host "[OK] Oracle service created successfully (AUTOMATIC startup)"
Start-Sleep -Seconds 2
}
# Step 2.2: Startup NOMOUNT
Write-Host "[2.2] Starting database in NOMOUNT mode..."
# First, ensure any partially started instance is shut down
# (Service auto-start may have started instance in error state without control files)
Write-Host " Ensuring clean state - shutting down any existing instance..."
$cleanupSQL = @"
WHENEVER SQLERROR CONTINUE
SHUTDOWN ABORT;
EXIT;
"@
$cleanupSQL | & sqlplus -S / as sysdba 2>&1 | Out-Null
# Now start cleanly in NOMOUNT
Write-Host " Starting fresh instance in NOMOUNT mode..."
$nomountSQL = @"
STARTUP NOMOUNT PFILE='C:\Users\oracle\admin\ROA\pfile\initROA.ora';
EXIT;
"@
$nomountSQL | & sqlplus -S / as sysdba 2>&1 | Out-Null
if ($LASTEXITCODE -ne 0) {
Write-Host "ERROR: Failed to startup NOMOUNT" -ForegroundColor Red
exit 1
}
Write-Host "[OK] Database started in NOMOUNT mode"
Start-Sleep -Seconds 3
# Step 2.3: Copy backups and create RMAN script
Write-Host "[2.3] Preparing RMAN restore..."
$rmanScript = "D:\oracle\temp\restore_from_zero.rman"
$logFile = "D:\oracle\logs\restore_from_zero.log"
# Copy backups from F:\ to recovery area (mode-dependent)
New-Item -ItemType Directory -Path "C:\Users\oracle\recovery_area\ROA\autobackup" -Force | Out-Null
if ($TestMode) {
Write-Host "[INFO] Copying selected backups from F:\ROA\autobackup to recovery area..."
Write-Host " TEST MODE: Only latest backup set (faster for weekly tests)"
} else {
Write-Host "[INFO] Copying all backups from F:\ROA\autobackup to recovery area..."
Write-Host " STANDALONE MODE: All backups for maximum DR safety"
}
# Select backup files based on mode (TestMode vs Standalone)
try {
if ($TestMode) {
# TEST MODE: Copy latest L0 backup set + all incrementals/archives
Write-Host "[INFO] TEST MODE: Selecting latest backup set using naming convention..." -ForegroundColor Cyan
# Check if new naming convention is in use (L0_*, L1_*, etc.)
$l0Backups = Get-ChildItem "F:\ROA\autobackup\L0_*.BKP" -ErrorAction Continue |
Sort-Object LastWriteTime -Descending
if ($l0Backups.Count -gt 0) {
# New naming convention detected - use smart selection
Write-Host "[INFO] Using naming convention for optimized backup selection" -ForegroundColor Cyan
$latestL0 = $l0Backups[0]
# Extract date from filename: L0_ROA_20251011_123_1.BKP -> 20251011
if ($latestL0.Name -match 'L0_\w+_(\d{8})_') {
$backupDate = $Matches[1]
Write-Host "[INFO] Latest Level 0 backup date: $backupDate" -ForegroundColor Cyan
Write-Host " Base file: $($latestL0.Name)" -ForegroundColor Cyan
# Select all files from this backup set:
# - All L0_*_<date>_* (all pieces of Level 0)
# - All L1_*_<date>_* or later (incrementals from same day or after)
# - All ARC_*_<date>_* or later (archive logs)
# - All SPFILE_* and CF_* (needed for restore)
$backupFiles = Get-ChildItem "F:\ROA\autobackup\*.BKP" -ErrorAction Continue |
Where-Object {
$_.Name -match "^L0_\w+_${backupDate}_" -or # Level 0 pieces
$_.Name -match "^L1_\w+_\d{8}_" -or # All Level 1 incrementals
$_.Name -match "^ARC_\w+_\d{8}_" -or # All archive logs
$_.Name -match "^SPFILE_\w+_${backupDate}_" -or # SPFILE from same day
$_.Name -match "^CF_\w+_${backupDate}_" -or # Controlfile from same day
$_.Name -match "^O1_MF_S_" # Autobackup control files (always needed)
}
Write-Host "[INFO] Selected $($backupFiles.Count) files for restore:" -ForegroundColor Cyan
Write-Host " - Level 0 pieces for date $backupDate" -ForegroundColor Cyan
Write-Host " - All Level 1 incrementals" -ForegroundColor Cyan
Write-Host " - All archive logs" -ForegroundColor Cyan
Write-Host " - SPFILE and Control file backups" -ForegroundColor Cyan
} else {
Write-Host "WARNING: Cannot parse date from L0 filename, using ALL L0/L1/ARC files" -ForegroundColor Yellow
$backupFiles = Get-ChildItem "F:\ROA\autobackup\*.BKP" -ErrorAction Continue |
Where-Object { $_.Name -match "^(L0_|L1_|ARC_|SPFILE_|CF_)" }
}
} else {
# Old naming convention (autobackup format) - fallback to copying all
Write-Host "[INFO] Old naming convention detected - copying ALL backups for safety" -ForegroundColor Yellow
Write-Host " (New naming convention will be used after next backup runs)" -ForegroundColor Yellow
$backupFiles = Get-ChildItem "F:\ROA\autobackup\*.BKP" -ErrorAction Continue
}
Write-Host "[INFO] Total files selected: $($backupFiles.Count)" -ForegroundColor Cyan
} else {
# STANDALONE MODE: Copy ALL backups (disaster recovery - maximum safety with fallback)
Write-Host "[INFO] STANDALONE MODE: Copying ALL backups for maximum DR safety..." -ForegroundColor Yellow
$backupFiles = Get-ChildItem "F:\ROA\autobackup\*.BKP" -ErrorAction Continue
Write-Host "[INFO] Full DR restore - will copy all available backups (includes redundancy)" -ForegroundColor Yellow
}
} catch {
Write-Host "WARNING: Cannot enumerate backup files on F: drive - $_" -ForegroundColor Yellow
$backupFiles = @()
}
# Validate backup count
$minRequired = 2
if ($backupFiles.Count -lt $minRequired) {
Write-Host "ERROR: Insufficient backup files found on F: drive (found: $($backupFiles.Count))" -ForegroundColor Red
Write-Host " At least $minRequired backup files required for successful restore"
Write-Host " Checking F:\ROA\autobackup directory..."
try {
$dirCheck = Get-ChildItem "F:\ROA\autobackup" -ErrorAction Continue
Write-Host " Directory contents: $($dirCheck.Count) files"
foreach ($file in $dirCheck) {
Write-Host " $($file.Name) - $([math]::Round($file.Length / 1GB, 2)) GB" -ForegroundColor Gray
}
} catch {
Write-Host " Cannot access directory: $_" -ForegroundColor Red
}
exit 1
}
Write-Host "[INFO] Found $($backupFiles.Count) backup files, total size: $([math]::Round(($backupFiles | Measure-Object -Property Length -Sum).Sum / 1GB, 2)) GB"
# Copy backups with better error handling
Write-Host "[INFO] Starting backup copy operation..."
$copyErrors = @()
foreach ($backupFile in $backupFiles) {
try {
Write-Host "[INFO] Copying $($backupFile.Name)..."
Copy-Item $backupFile.FullName "C:\Users\oracle\recovery_area\ROA\autobackup\" -Force -ErrorAction Stop
Write-Host "[OK] Copied $($backupFile.Name)" -ForegroundColor Green
} catch {
Write-Host "ERROR: Failed to copy $($backupFile.Name) - $_" -ForegroundColor Red
$copyErrors += "$($backupFile.Name): $_"
}
}
if ($copyErrors.Count -gt 0) {
Write-Host "ERROR: Backup copy failed for $($copyErrors.Count) files" -ForegroundColor Red
foreach ($error in $copyErrors) {
Write-Host " $error" -ForegroundColor Red
}
exit 1
}
# Verify copied backups
try {
$copiedFiles = Get-ChildItem "C:\Users\oracle\recovery_area\ROA\autobackup\*.BKP" -ErrorAction Continue
} catch {
Write-Host "ERROR: Cannot verify copied backups - $_" -ForegroundColor Red
exit 1
}
if ($copiedFiles.Count -ne $backupFiles.Count) {
Write-Host "ERROR: Backup copy verification failed - file count mismatch" -ForegroundColor Red
Write-Host " Expected: $($backupFiles.Count), Copied: $($copiedFiles.Count)"
exit 1
}
Write-Host "[OK] All $($copiedFiles.Count) backups copied and verified to recovery area"
# Create RMAN script
$rmanContent = @"
SET DBID 1363569330;
RUN {
ALLOCATE CHANNEL ch1 DEVICE TYPE DISK;
RESTORE CONTROLFILE FROM AUTOBACKUP;
RELEASE CHANNEL ch1;
}
ALTER DATABASE MOUNT;
CATALOG START WITH 'C:/USERS/ORACLE/RECOVERY_AREA/ROA/AUTOBACKUP' NOPROMPT;
CROSSCHECK BACKUP;
DELETE NOPROMPT EXPIRED BACKUP;
RUN {
ALLOCATE CHANNEL ch1 DEVICE TYPE DISK;
ALLOCATE CHANNEL ch2 DEVICE TYPE DISK;
RESTORE DATABASE;
RELEASE CHANNEL ch1;
RELEASE CHANNEL ch2;
}
RUN {
ALLOCATE CHANNEL ch1 DEVICE TYPE DISK;
RECOVER DATABASE NOREDO;
RELEASE CHANNEL ch1;
}
ALTER DATABASE OPEN RESETLOGS;
DELETE NOPROMPT OBSOLETE;
EXIT;
"@
$rmanContent | Out-File -FilePath $rmanScript -Encoding ASCII
Write-Host "[OK] RMAN script created: $rmanScript"
# Step 2.4: Run RMAN restore
Write-Host "[2.4] Running RMAN restore (this will take 10-20 minutes)..."
Write-Host " Log file: $logFile"
Write-Host ""
& rman target / cmdfile=$rmanScript log=$logFile
if ($LASTEXITCODE -ne 0) {
Write-Host ""
Write-Host "ERROR: RMAN restore failed!" -ForegroundColor Red
Write-Host "Check log: $logFile"
exit 1
}
Write-Host ""
Write-Host "[OK] RMAN restore completed successfully!"
Write-Host ""
Write-Host "============================================"
Write-Host "STEP 3: VERIFY - Check database status"
Write-Host "============================================"
Write-Host ""
Write-Host "[3.1] Verifying database..."
$verifySQL = @"
SET PAGESIZE 100 LINESIZE 200
COLUMN info FORMAT A80
SELECT 'DB_NAME: ' || NAME || ', OPEN_MODE: ' || OPEN_MODE AS info FROM V`$DATABASE;
SELECT 'INSTANCE: ' || INSTANCE_NAME || ', STATUS: ' || STATUS AS info FROM V`$INSTANCE;
SELECT 'TABLESPACES: ' || COUNT(*) AS info FROM DBA_TABLESPACES;
SELECT 'DATAFILES: ' || COUNT(*) AS info FROM DBA_DATA_FILES;
SELECT 'TABLES: ' || COUNT(*) AS info FROM DBA_TABLES WHERE OWNER NOT IN ('SYS','SYSTEM');
EXIT;
"@
$verifySQL | & sqlplus -S / as sysdba
Write-Host ""
Write-Host "[3.2] Creating SPFILE for database persistence..."
$spfileSQL = @"
CREATE SPFILE FROM PFILE='C:\Users\oracle\admin\ROA\pfile\initROA.ora';
EXIT;
"@
$spfileSQL | & sqlplus -S / as sysdba 2>&1 | Out-Null
if ($LASTEXITCODE -ne 0) {
Write-Host "WARNING: Failed to create SPFILE - database may not persist after connections close" -ForegroundColor Yellow
} else {
Write-Host "[OK] SPFILE created successfully"
# Check if running in TestMode (weekly DR test)
if ($TestMode) {
Write-Host "[3.3] Running in TEST MODE - skipping service reconfiguration"
Write-Host " Database is OPEN and ready for verification"
Write-Host " Service will remain configured with PFILE (OK for testing)"
} else {
# Full configuration for standalone/production use
Write-Host "[3.3] Reconfiguring Oracle service to use SPFILE..."
# Shutdown database cleanly
Write-Host " Shutting down database temporarily..."
$shutdownSQL = @"
SHUTDOWN IMMEDIATE;
EXIT;
"@
$shutdownSQL | & sqlplus -S / as sysdba 2>&1 | Out-Null
Start-Sleep -Seconds 3
# Delete and recreate service with SPFILE
Write-Host " Recreating service with SPFILE..."
& oradim -delete -sid ROA 2>&1 | Out-Null
Start-Sleep -Seconds 2
& oradim -new -sid ROA -startmode auto -spfile 2>&1 | Out-Null
if ($LASTEXITCODE -ne 0) {
Write-Host " WARNING: Failed to recreate service with SPFILE" -ForegroundColor Yellow
} else {
Write-Host " [OK] Service now configured with SPFILE and AUTOMATIC startup"
}
# Restart database
Write-Host " Starting database with SPFILE..."
$startupSQL = @"
STARTUP;
EXIT;
"@
$startupSQL | & sqlplus -S / as sysdba 2>&1 | Out-Null
Start-Sleep -Seconds 3
Write-Host "[OK] Database restarted with SPFILE configuration"
# Start Oracle Listener
Write-Host "[3.4] Starting Oracle Listener..."
# Set Listener service to AUTOMATIC and start it
Set-Service -Name "OracleOraDB19Home1TNSListener" -StartupType Automatic -ErrorAction SilentlyContinue
Start-Service -Name "OracleOraDB19Home1TNSListener" -ErrorAction SilentlyContinue
if ((Get-Service -Name "OracleOraDB19Home1TNSListener" -ErrorAction SilentlyContinue).Status -eq "Running") {
Write-Host "[OK] Listener started successfully"
} else {
Write-Host "WARNING: Failed to start Listener automatically, trying lsnrctl..." -ForegroundColor Yellow
& lsnrctl start 2>&1 | Out-Null
}
Start-Sleep -Seconds 2
# Register database with listener
$registerSQL = @"
ALTER SYSTEM REGISTER;
EXIT;
"@
$registerSQL | & sqlplus -S / as sysdba 2>&1 | Out-Null
Write-Host "[OK] Database registered with Listener"
}
}
Write-Host ""
Write-Host "============================================"
Write-Host "Database Restore FROM ZERO Complete!"
Write-Host "============================================"
Write-Host ""
Write-Host "Restore log: $logFile"
Write-Host ""
Write-Host "Database is OPEN and ready for testing!" -ForegroundColor Green
Write-Host ""
Write-Host "Next steps:"
Write-Host " 1. Test application connectivity"
Write-Host " 2. Verify data integrity"
Write-Host " 3. Run cleanup_database.ps1 to remove database after test"
Write-Host " 4. Shutdown DR VM to conserve resources"
Write-Host ""
exit 0

View File

@@ -0,0 +1,231 @@
# Transfer Oracle Backups (Full + Incremental) towards DR Server
# Script UNIFICAT - poate fi rulat după orice tip de backup
# Transferă TOATE fișierele backup, skip-uiește duplicatele automat
#
# Poate fi apelat de:
# - Task Scheduler după full backup (03:00 AM)
# - Task Scheduler după incremental backup (14:30)
# - Manual oricând pentru recovery
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_HHmm').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-AllBackupFiles {
Write-Log "Searching for all backup files in FRA..."
$backupFiles = @()
$searchPaths = @(
"$SourceFRA\BACKUPSET",
"$SourceFRA\AUTOBACKUP"
)
foreach ($path in $searchPaths) {
if (Test-Path $path) {
# Get ALL backup files (duplicates will be skipped during transfer)
$files = Get-ChildItem -Path $path -Recurse -File -ErrorAction SilentlyContinue |
Where-Object {
$_.Name -match '\.(BKP|bkp)$' -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!" "WARNING"
return @()
}
$totalSizeGB = ($backupFiles | Measure-Object -Property Length -Sum).Sum / 1GB
Write-Log "Found $($backupFiles.Count) backup 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 {
# 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
# -mtime +N înseamnă "mai vechi de N zile", deci pentru a păstra RetentionDays zile, folosim +($RetentionDays - 1)
$mtimeDays = $RetentionDays - 1
$cleanupCmd = "find '$DRPath' -name '*.BKP' -type f -mtime +$mtimeDays -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"
}
}
# ==================== MAIN ====================
try {
Write-Log "========================================="
Write-Log "Oracle Backup Transfer Started (UNIFIED)"
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 TOATE backup-urile
$backupFiles = Get-AllBackupFiles
if ($backupFiles.Count -eq 0) {
Write-Log "No backup files to transfer (this might be normal for first run)" "WARNING"
exit 0
}
# Transfer fișiere
Write-Log "Starting file transfer..."
$successCount = 0
$failCount = 0
$skippedCount = 0
foreach ($file in $backupFiles) {
$result = Transfer-FileToDR -File $file -DestPath $DRPath
if ($result) {
# Check if it was skipped or transferred
$fileName = $file.Name
$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") {
$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 "Backup Transfer Completed Successfully"
Write-Log "========================================="
Write-Log "Files processed: $($backupFiles.Count)"
Write-Log "Successful: $successCount"
Write-Log "Failed: $failCount"
Write-Log "DR Server: ${DRHost}:${DRPath}"
exit 0
} catch {
Write-Log "CRITICAL ERROR: $($_.Exception.Message)" "ERROR"
Write-Log "Stack trace: $($_.ScriptStackTrace)" "ERROR"
exit 1
}

View File

@@ -0,0 +1,649 @@
#!/bin/bash
#
# Oracle DR Weekly Test with Proxmox PVE::Notify
# Automated DR test with notifications via Proxmox notification system
#
# Location: /opt/scripts/weekly-dr-test-proxmox.sh (on Proxmox host)
# Schedule: Add to cron for weekly execution (Saturdays)
#
# This script is SELF-SUFFICIENT:
# - Automatically creates notification templates if they don't exist
# - Uses Proxmox native notification system
# - No email configuration needed - uses existing Proxmox setup
#
# Installation:
# cp weekly-dr-test-proxmox.sh /opt/scripts/
# chmod +x /opt/scripts/weekly-dr-test-proxmox.sh
# /opt/scripts/weekly-dr-test-proxmox.sh --install # Creates templates
# crontab -e # Add: 0 6 * * 6 /opt/scripts/weekly-dr-test-proxmox.sh
#
# Author: Claude (based on ha-monitor.sh pattern)
# Version: 1.0
set -euo pipefail
# Set proper PATH for cron execution
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
# Configuration
DR_VM_ID="109"
DR_VM_IP="10.0.20.37"
DR_VM_PORT="22122"
DR_VM_USER="romfast"
BACKUP_PATH="/mnt/pve/oracle-backups/ROA/autobackup"
MAX_RESTORE_TIME_MIN=30
TEMPLATE_DIR="/usr/share/pve-manager/templates/default"
LOG_DIR="/var/log/oracle-dr"
LOG_FILE="$LOG_DIR/dr_test_$(date +%Y%m%d_%H%M%S).log"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
# Create log directory
mkdir -p "$LOG_DIR"
# Function to create notification templates
create_templates() {
echo -e "${GREEN}Creating Oracle DR test notification templates...${NC}"
# Create templates directory if needed
mkdir -p "$TEMPLATE_DIR"
# Subject template
cat > "$TEMPLATE_DIR/oracle-dr-test-subject.txt.hbs" <<'EOF'
Oracle DR Test {{test_result}} | {{date}}
EOF
# Text body template
cat > "$TEMPLATE_DIR/oracle-dr-test-body.txt.hbs" <<'EOF'
Oracle DR Test {{test_result}} | {{date}}
Severity: {{severity}}
SUMMARY
- Outcome: {{test_result}}
- Duration: {{total_duration}} min (restore {{restore_duration}} min)
- Backups used: {{backup_count}}
- Tables restored: {{tables_restored}}
COMPONENTS
- VM {{vm_id}} ({{vm_ip}}): {{vm_status}}
- NFS: {{nfs_status}}
- Database: {{database_status}}
- Cleanup: {{disk_freed}} GB freed
STEPS
{{#each test_steps}}
- {{#if this.passed}}✓{{else}}✗{{/if}} {{this.name}} ({{this.duration}}s){{#if this.status}} - {{this.status}}{{/if}}
{{/each}}
{{#if has_errors}}
ISSUES
{{#each errors}}
- {{this}}
{{/each}}
{{/if}}
{{#if has_warnings}}
WARNINGS
{{#each warnings}}
- {{this}}
{{/each}}
{{/if}}
RMAN RESTORE LOG (complete)
---
{{restore_log}}
---
BASH SCRIPT LOG (last 100 lines)
---
{{bash_log}}
---
Full log: {{log_file}}
Next test: Saturday 06:00
EOF
# HTML body template (compact Gmail-friendly layout)
cat > "$TEMPLATE_DIR/oracle-dr-test-body.html.hbs" <<'EOF'
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Oracle DR Test {{test_result}} | {{date}}</title>
</head>
<body style="margin:0;padding:16px;font-family:Arial,Helvetica,sans-serif;background:#ffffff;color:#2c3e50;">
<table style="width:100%;max-width:640px;margin:0 auto;border-collapse:collapse;">
<tr>
<td style="padding:0 0 12px 0;font-size:18px;font-weight:600;">
Oracle DR Test {{test_result}}
</td>
</tr>
<tr>
<td style="padding:0 0 8px 0;font-size:13px;color:#6c757d;">{{date}} · Severity: {{severity}}</td>
</tr>
<tr>
<td style="padding:12px;border:1px solid #e1e4e8;border-radius:4px;">
<table style="width:100%;border-collapse:collapse;font-size:14px;">
<tr><td style="padding:4px 0;">Outcome</td><td style="padding:4px 0;text-align:right;">{{test_result}}</td></tr>
<tr><td style="padding:4px 0;">Duration</td><td style="padding:4px 0;text-align:right;">{{total_duration}} min (restore {{restore_duration}} min)</td></tr>
<tr><td style="padding:4px 0;">Backups used</td><td style="padding:4px 0;text-align:right;">{{backup_count}}</td></tr>
<tr><td style="padding:4px 0;">Tables restored</td><td style="padding:4px 0;text-align:right;">{{tables_restored}}</td></tr>
</table>
</td>
</tr>
<tr>
<td style="padding:16px 0 0 0;">
<table style="width:100%;border-collapse:collapse;font-size:14px;border:1px solid #e1e4e8;border-radius:4px;background:#f9fafb;">
<tr><td style="padding:8px 12px;font-weight:600;">Components</td></tr>
<tr><td style="padding:6px 12px;border-top:1px solid #e1e4e8;">VM {{vm_id}} ({{vm_ip}}): {{vm_status}}</td></tr>
<tr><td style="padding:6px 12px;border-top:1px solid #e1e4e8;">NFS: {{nfs_status}}</td></tr>
<tr><td style="padding:6px 12px;border-top:1px solid #e1e4e8;">Database: {{database_status}}</td></tr>
<tr><td style="padding:6px 12px;border-top:1px solid #e1e4e8;">Cleanup: {{disk_freed}} GB freed</td></tr>
</table>
</td>
</tr>
<tr>
<td style="padding:16px 0 0 0;">
<table style="width:100%;border-collapse:collapse;font-size:14px;">
<tr><td style="padding:0 0 6px 0;font-weight:600;">Steps</td></tr>
{{#each test_steps}}
<tr>
<td style="padding:4px 0;border-bottom:1px solid #f1f1f1;">{{#if this.passed}}✓{{else}}✗{{/if}} {{this.name}} ({{this.duration}}s){{#if this.status}} {{this.status}}{{/if}}</td>
</tr>
{{/each}}
</table>
</td>
</tr>
{{#if has_errors}}
<tr>
<td style="padding:16px 0 0 0;">
<table style="width:100%;border-collapse:collapse;font-size:14px;background:#fff5f5;border:1px solid #f1b0b7;border-radius:4px;">
<tr><td style="padding:8px 12px;font-weight:600;color:#c82333;">Issues</td></tr>
{{#each errors}}
<tr><td style="padding:6px 12px;border-top:1px solid #f8d7da;">• {{this}}</td></tr>
{{/each}}
</table>
</td>
</tr>
{{/if}}
{{#if has_warnings}}
<tr>
<td style="padding:16px 0 0 0;">
<table style="width:100%;border-collapse:collapse;font-size:14px;background:#fff8e5;border:1px solid #ffe8a1;border-radius:4px;">
<tr><td style="padding:8px 12px;font-weight:600;color:#856404;">Warnings</td></tr>
{{#each warnings}}
<tr><td style="padding:6px 12px;border-top:1px solid #ffe8a1;">• {{this}}</td></tr>
{{/each}}
</table>
</td>
</tr>
{{/if}}
<tr>
<td style="padding:16px 0 0 0;">
<table style="width:100%;border-collapse:collapse;font-size:12px;border:1px solid #e1e4e8;border-radius:4px;background:#f9fafb;">
<tr><td style="padding:8px 12px;font-weight:600;font-size:13px;">RMAN Restore Log (complete)</td></tr>
<tr><td style="padding:8px 12px;font-family:monospace;white-space:pre-wrap;word-wrap:break-word;border-top:1px solid #e1e4e8;">{{restore_log}}</td></tr>
</table>
</td>
</tr>
<tr>
<td style="padding:16px 0 0 0;">
<table style="width:100%;border-collapse:collapse;font-size:12px;border:1px solid #e1e4e8;border-radius:4px;background:#f9fafb;">
<tr><td style="padding:8px 12px;font-weight:600;font-size:13px;">Bash Script Log (last 100 lines)</td></tr>
<tr><td style="padding:8px 12px;font-family:monospace;white-space:pre-wrap;word-wrap:break-word;border-top:1px solid #e1e4e8;">{{bash_log}}</td></tr>
</table>
</td>
</tr>
<tr>
<td style="padding:16px 0 0 0;font-size:12px;color:#6c757d;">
Full log: {{log_file}} · Next test: Saturday 06:00
</td>
</tr>
</table>
</body>
</html>
EOF
echo -e "${GREEN}Templates created successfully in $TEMPLATE_DIR${NC}"
}
# Function to send notification via PVE::Notify
send_pve_notification() {
local severity="$1"
local data="$2"
# Create Perl script to call PVE::Notify
cat > /tmp/oracle-dr-notify.pl <<'PERL_SCRIPT'
#!/usr/bin/perl
use strict;
use warnings;
use PVE::Notify;
use JSON;
my $json_data = do { local $/; <STDIN> };
my $data = decode_json($json_data);
my $severity = $data->{severity} // 'info';
my $template_name = 'oracle-dr-test';
# Add fields for matching rules
my $fields = {
type => 'oracle-dr-test',
severity => $severity,
test_result => $data->{test_result},
};
# Send notification
eval {
PVE::Notify::notify(
$severity,
$template_name,
$data,
$fields
);
};
if ($@) {
print "Error sending notification: $@\n";
exit 1;
}
print "Notification sent successfully\n";
PERL_SCRIPT
chmod +x /tmp/oracle-dr-notify.pl
# Send notification
echo "$data" | perl /tmp/oracle-dr-notify.pl
rm -f /tmp/oracle-dr-notify.pl
}
# Logging functions
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1" | tee -a "$LOG_FILE"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1" | tee -a "$LOG_FILE"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1" | tee -a "$LOG_FILE"
}
# Test tracking
TEST_STEPS=()
ERRORS=()
WARNINGS=()
TEST_START_TIME=$(date +%s)
# Function to track test steps
track_step() {
local name="$1"
local passed="$2"
local status="$3"
local start_time="$4"
local end_time=$(date +%s)
local duration=$((end_time - start_time))
local step_json
step_json=$(jq -n \
--arg name "$name" \
--arg status "$status" \
--arg duration "$duration" \
--arg passed "$passed" \
'{name:$name, status:$status, duration:($duration|tonumber), passed:($passed == "true")}'
)
TEST_STEPS+=("$step_json")
if [ "$passed" = "false" ]; then
ERRORS+=("$name: $status")
fi
}
# Main test workflow
run_dr_test() {
local test_result="FAILED"
local severity="error"
local is_success=false
local restore_duration=0
local tables_restored=0
local db_status="UNKNOWN"
local nfs_status="Not checked"
local vm_status_label="Not started"
local cleanup_freed=0
local backup_count=0
local restore_log="Not collected"
log "=========================================="
log "Oracle DR Weekly Test - Starting"
log "=========================================="
# Step 1: Pre-flight checks
local step_start=$(date +%s)
log "STEP 1: Pre-flight checks"
# Check backups exist
backup_count=$(find "$BACKUP_PATH" -maxdepth 1 -type f -name '*.BKP' 2>/dev/null | wc -l)
if [ "$backup_count" -lt 2 ]; then
track_step "Pre-flight checks" false "Insufficient backups (found: $backup_count)" "$step_start"
test_result="FAILED - No backups"
else
track_step "Pre-flight checks" true "Found $backup_count backups" "$step_start"
# Step 2: Start VM
step_start=$(date +%s)
log "STEP 2: Starting DR VM"
if qm start "$DR_VM_ID" 2>/dev/null; then
vm_status_label="Running"
# Intelligent VM boot wait with polling (max 180s)
local MAX_BOOT_WAIT=180
local POLL_INTERVAL=5
local boot_elapsed=0
local vm_ready=false
log "Waiting for VM to become ready (SSH + PowerShell, max ${MAX_BOOT_WAIT}s)..."
while [ $boot_elapsed -lt $MAX_BOOT_WAIT ]; do
# Check 1: VM running status in Proxmox
local vm_qm_status
vm_qm_status=$(qm status "$DR_VM_ID" 2>/dev/null | grep -o "running" || echo "")
if [ "$vm_qm_status" = "running" ]; then
# Check 2: SSH connectivity and PowerShell availability (what we actually need)
if ssh -p "$DR_VM_PORT" -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o BatchMode=yes "$DR_VM_USER@$DR_VM_IP" \
"powershell -Command 'Write-Output ready'" >/dev/null 2>&1; then
log "VM ready after ${boot_elapsed}s (SSH and PowerShell responding)"
vm_ready=true
break
fi
fi
sleep $POLL_INTERVAL
boot_elapsed=$((boot_elapsed + POLL_INTERVAL))
# Progress logging every 30 seconds
if [ $((boot_elapsed % 30)) -eq 0 ] && [ $boot_elapsed -lt $MAX_BOOT_WAIT ]; then
log "Still waiting for VM... (${boot_elapsed}s/${MAX_BOOT_WAIT}s elapsed)"
fi
done
if [ "$vm_ready" = false ]; then
log_warning "VM did not respond within ${MAX_BOOT_WAIT}s, continuing anyway (may cause subsequent failures)"
fi
track_step "VM Startup" true "VM $DR_VM_ID started and ready (${boot_elapsed}s)" "$step_start"
# Step 3: Verify NFS mount
step_start=$(date +%s)
log "STEP 3: Verifying NFS mount"
nfs_status="Not Mounted"
if ssh -p "$DR_VM_PORT" -o ConnectTimeout=10 "$DR_VM_USER@$DR_VM_IP" \
"powershell -Command 'Test-Path F:\\ROA\\autobackup'" 2>/dev/null; then
nfs_status="Mounted"
track_step "NFS Mount Check" true "F:\\ drive accessible" "$step_start"
else
track_step "NFS Mount Check" false "F:\\ drive not accessible" "$step_start"
WARNINGS+=("NFS mount may need manual intervention")
fi
# Step 4: Run restore
step_start=$(date +%s)
local restore_start=$step_start
log "STEP 4: Running database restore"
if ssh -p "$DR_VM_PORT" "$DR_VM_USER@$DR_VM_IP" \
"powershell -ExecutionPolicy Bypass -File D:\\oracle\\scripts\\rman_restore_from_zero.ps1 -TestMode" 2>&1 | tee -a "$LOG_FILE"; then
local restore_end=$(date +%s)
restore_duration=$(( (restore_end - restore_start) / 60 ))
track_step "Database Restore" true "Restored in $restore_duration minutes" "$step_start"
# Step 5: Verify database
step_start=$(date +%s)
log "STEP 5: Verifying database"
# Parse database status from LOG_FILE (rman_restore_from_zero.ps1 already verified it)
# Look for "OPEN_MODE: READ WRITE" in the captured output
if grep -q "OPEN_MODE: READ WRITE" "$LOG_FILE" 2>/dev/null; then
db_status="READ WRITE"
else
db_status=""
fi
# Parse table count from LOG_FILE (already captured in STEP 3 output)
# Look for "TABLES: <number>" in the output
tables_restored=$(grep -oP "TABLES:\s*\K\d+" "$LOG_FILE" 2>/dev/null | tail -1 || echo "0")
tables_restored=$(echo "$tables_restored" | tr -cd '0-9')
[ -z "$tables_restored" ] && tables_restored=0
if [[ "$db_status" == "READ WRITE" ]] && [ "$tables_restored" -gt 0 ]; then
track_step "Database Verification" true "Database OPEN, $tables_restored tables" "$step_start"
test_result="PASSED"
severity="info"
is_success=true
else
track_step "Database Verification" false "Database not OPEN" "$step_start"
fi
# Collect restore log from VM (always attempt collection - FULL log)
log "Collecting restore log from DR VM..."
restore_log=$(ssh -p "$DR_VM_PORT" "$DR_VM_USER@$DR_VM_IP" \
"powershell -Command \"Get-Content 'D:\\oracle\\logs\\restore_from_zero.log' -ErrorAction SilentlyContinue\"" 2>/dev/null || echo "")
# If not found, try alternate locations
if [ -z "$restore_log" ]; then
restore_log=$(ssh -p "$DR_VM_PORT" "$DR_VM_USER@$DR_VM_IP" \
"powershell -Command \"Get-Content 'D:\\oracle\\temp\\restore_from_zero.log' -ErrorAction SilentlyContinue\"" 2>/dev/null || echo "")
fi
# Still not found, use fallback message
if [ -z "$restore_log" ]; then
restore_log="Restore log not available (file may not exist or was not generated)"
fi
# Step 6: Cleanup (AFTER restore - stop service to release file locks)
step_start=$(date +%s)
log "STEP 6: Running cleanup"
ssh -p "$DR_VM_PORT" "$DR_VM_USER@$DR_VM_IP" \
"powershell -ExecutionPolicy Bypass -File D:\\oracle\\scripts\\cleanup_database.ps1 /SILENT /AFTER" 2>/dev/null
cleanup_freed=8
track_step "Cleanup" true "Database cleaned, ~${cleanup_freed}GB freed" "$step_start"
else
# Collect restore log even when restore fails (FULL log)
log "Collecting restore log after failure..."
restore_log=$(ssh -p "$DR_VM_PORT" "$DR_VM_USER@$DR_VM_IP" \
"powershell -Command \"Get-Content 'D:\\oracle\\logs\\restore_from_zero.log' -ErrorAction SilentlyContinue\"" 2>/dev/null || echo "")
if [ -z "$restore_log" ]; then
restore_log=$(ssh -p "$DR_VM_PORT" "$DR_VM_USER@$DR_VM_IP" \
"powershell -Command \"Get-Content 'D:\\oracle\\temp\\restore_from_zero.log' -ErrorAction SilentlyContinue\"" 2>/dev/null || echo "")
fi
# Always try to get some error output from RMAN script
if [ -z "$restore_log" ]; then
last_error=$(ssh -p "$DR_VM_PORT" "$DR_VM_USER@$DR_VM_IP" \
"powershell -Command \"Get-Content 'D:\\oracle\\temp\\*.rman' -Tail 20 -ErrorAction SilentlyContinue\"" 2>/dev/null || echo "")
if [ -n "$last_error" ]; then
restore_log="RMAN script content (last 20 lines):\n$last_error"
else
restore_log="No restore logs or RMAN scripts found"
fi
fi
track_step "Database Restore" false "Restore failed" "$step_start"
fi
# Step 7: Shutdown VM
step_start=$(date +%s)
log "STEP 7: Shutting down VM"
ssh -p "$DR_VM_PORT" "$DR_VM_USER@$DR_VM_IP" "shutdown /s /t 30" 2>/dev/null
sleep 60
qm stop "$DR_VM_ID" 2>/dev/null
track_step "VM Shutdown" true "VM stopped" "$step_start"
vm_status_label="Stopped"
else
track_step "VM Startup" false "Failed to start VM $DR_VM_ID" "$step_start"
vm_status_label="Failed to start"
fi
fi
# Calculate total duration
local test_end_time=$(date +%s)
local total_duration=$(( (test_end_time - TEST_START_TIME) / 60 ))
# Prepare notification data
local steps_json
if [ ${#TEST_STEPS[@]} -eq 0 ]; then
steps_json='[]'
else
steps_json=$(printf '%s\n' "${TEST_STEPS[@]}" | jq -s '.')
fi
local errors_json
if [ ${#ERRORS[@]} -eq 0 ]; then
errors_json='[]'
else
errors_json=$(printf '%s\n' "${ERRORS[@]}" | jq -R . | jq -s .)
fi
local warnings_json
if [ ${#WARNINGS[@]} -eq 0 ]; then
warnings_json='[]'
else
warnings_json=$(printf '%s\n' "${WARNINGS[@]}" | jq -R . | jq -s .)
fi
local has_errors=false
local has_warnings=false
[ ${#ERRORS[@]} -gt 0 ] && has_errors=true
[ ${#WARNINGS[@]} -gt 0 ] && has_warnings=true
if [ "$is_success" = true ] && [ "$has_warnings" = true ]; then
severity="warning"
fi
local db_status_clean=$(echo "$db_status" | tr -d '\r' | sed 's/^ *//;s/ *$//')
# Escape restore log for JSON
local restore_log_json
restore_log_json=$(echo "$restore_log" | jq -Rs .)
# Collect last 100 lines of bash script log
local bash_log
bash_log=$(tail -100 "$LOG_FILE" 2>/dev/null || echo "Bash log not available")
local bash_log_json
bash_log_json=$(echo "$bash_log" | jq -Rs .)
local json_data=$(cat <<JSON
{
"severity": "$severity",
"test_result": "$test_result",
"date": "$(date '+%Y-%m-%d %H:%M:%S')",
"total_duration": $total_duration,
"is_success": $is_success,
"has_errors": $has_errors,
"has_warnings": $has_warnings,
"test_steps": $steps_json,
"errors": $errors_json,
"warnings": $warnings_json,
"backup_count": $backup_count,
"restore_duration": $restore_duration,
"tables_restored": ${tables_restored:-0},
"database_status": "${db_status_clean:-UNKNOWN}",
"disk_freed": $cleanup_freed,
"vm_id": "$DR_VM_ID",
"vm_ip": "$DR_VM_IP",
"vm_status": "$vm_status_label",
"nfs_status": "${nfs_status:-Unknown}",
"log_file": "$LOG_FILE",
"restore_log": $restore_log_json,
"bash_log": $bash_log_json
}
JSON
)
# Send notification
log "Sending notification..."
send_pve_notification "$severity" "$json_data"
# Final summary
log "=========================================="
log "Oracle DR Test Complete: $test_result"
log "Duration: $total_duration minutes"
log "Log: $LOG_FILE"
log "=========================================="
}
# Main execution
main() {
case "${1:-}" in
--install)
create_templates
echo ""
echo -e "${GREEN}Installation complete!${NC}"
echo "Next steps:"
echo "1. Test the script: /opt/scripts/weekly-dr-test-proxmox.sh"
echo "2. Add to cron: crontab -e"
echo " Add line: 0 6 * * 6 /opt/scripts/weekly-dr-test-proxmox.sh"
echo "3. Configure notifications in Proxmox GUI if needed:"
echo " Datacenter > Notifications > Add matching rules for 'oracle-dr-test'"
;;
--help)
echo "Oracle DR Weekly Test for Proxmox"
echo "Usage:"
echo " $0 - Run DR test"
echo " $0 --install - Create notification templates"
echo " $0 --help - Show this help"
;;
*)
# Check if templates exist, create if missing
if [ ! -f "$TEMPLATE_DIR/oracle-dr-test-subject.txt.hbs" ]; then
echo -e "${YELLOW}Templates not found, creating...${NC}"
create_templates
echo ""
fi
# Run DR test
run_dr_test
;;
esac
}
# Check dependencies
if ! command -v jq &> /dev/null; then
echo -e "${RED}Error: jq is not installed${NC}"
echo "Install with: apt-get install jq"
exit 1
fi
main "$@"