Thursday, April 24, 2025

#!/bin/bash # Trigger Data Guard failover source /u01/app/oracle/env.prod dgmgrl sys/Password123@primary_db </tmp/backup.log 2>&1 #### #!/bin/bash # Load configuration source $(dirname "$0")/dg_config.cfg # Initialize logging init_logging() { local OPERATION=$1 LOG_FILE="${LOG_DIR}/dg_${OPERATION}_$(date +%Y%m%d%H%M%S).log" exec > >(tee -a "$LOG_FILE") 2>&1 } # Check DG configuration status check_dg_config() { dgmgrl -silent "sys/${SYS_PASSWORD}@${PRIMARY_DB}" <> "${LOG_FILE}" case $SEVERITY in "CRITICAL") echo "${MESSAGE}" | mail -s "[URGENT] ${SUBJECT}" "$ALERT_EMAIL" ;; "WARNING") echo "${MESSAGE}" | mail -s "${SUBJECT}" "$ALERT_EMAIL" ;; *) logger -t DG_OPERATION "${MESSAGE}" ;; esac } archive_logs() { local RETENTION_DAYS=30 find "${LOG_DIR}" -name "dg_*.log" -mtime +${RETENTION_DAYS} -delete } ################### #!/bin/bash # Main script with enhanced features source "$(dirname "$0")/dg_config.cfg" source "$(dirname "$0")/dg_functions.sh" source "$(dirname "$0")/prechecks.sh" source "$(dirname "$0")/alert_handler.sh" trap "rollback_switchover; send_alert CRITICAL 'Operation Aborted' 'Script terminated unexpectedly'; exit 1" INT TERM main() { local OPERATION=$1 local TARGET_DB=$2 # Initialize environment export TNS_ADMIN="${ORACLE_HOME}/network/admin" init_logging "${OPERATION}" archive_logs # Pre-flight checks if ! verify_connectivity "${PRIMARY_DB}"; then log "ERROR" "Cannot connect to primary database" send_alert CRITICAL "Connectivity Failure" "Primary DB unreachable" exit 1 fi local LAG=$(check_apply_lag | tr -d '\n') if [ "${LAG}" -gt 300 ]; then # 5 minutes lag threshold log "WARNING" "High apply lag detected: ${LAG} seconds" send_alert WARNING "High Lag" "Switchover with ${LAG} sec lag" fi case $OPERATION in "switchover") timeout ${DG_TIMEOUT} perform_switchover "${TARGET_DB}" || { log "ERROR" "Switchover timed out" send_alert CRITICAL "Timeout" "Switchover exceeded ${DG_TIMEOUT}s" exit 1 } ;; "switchback") timeout ${DG_TIMEOUT} perform_switchback || { log "ERROR" "Switchback timed out" send_alert CRITICAL "Timeout" "Switchback exceeded ${DG_TIMEOUT}s" exit 1 } ;; esac # Post-operation verification if ! check_dg_health "${PRIMARY_DB}"; then log "ERROR" "Post-operation health check failed" send_alert CRITICAL "Health Check Failed" "Configuration unstable" exit 1 fi } # Parse arguments with validation if [[ $# -lt 1 ]]; then echo "Usage: $0 [switchover|switchback] [target_db]" exit 1 fi main "$@" ############################### main() { # ... existing code ... case $OPERATION in "switchover") init_logging "switchover" local RESTORE_POINT=$(create_restore_point "${PRIMARY_DB}") if [ $? -ne 0 ]; then send_alert "CRITICAL" "Restore Point Creation Failed" "Could not create pre-switchover restore point" exit 1 fi # Perform switchover if perform_switchover "$TARGET_DB"; then purge_old_restore_points "${TARGET_DB}" create_restore_point "${TARGET_DB}" fi ;; "switchback") init_logging "switchback" drop_all_restore_points "${STANDBY_DB}" # ... rest of switchback logic ... ;; esac # ... existing code ... } ## # Add to prechecks.sh verify_mrp_status() { local DB=$1 local STATUS=$(sqlplus -s /nolog < /dev/null 2>&1; then { echo "To: $EMAIL_RECIPIENT" echo "Subject: $EMAIL_SUBJECT - $host_entry" echo "Content-Type: text/html" echo cat "$report_file" } | sendmail -t log_msg "Email sent as inline HTML using sendmail for $host_entry" elif command -v mailx > /dev/null 2>&1; then mailx -a "Content-Type: text/html" -s "$EMAIL_SUBJECT - $host_entry" "$EMAIL_RECIPIENT" < "$report_file" log_msg "Email sent as inline HTML using mailx for $host_entry" else log_msg "ERROR: Neither sendmail nor mailx available for sending inline HTML email." fi else # Compress report and attach local zip_file="${report_file%.html}.zip" zip -j "$zip_file" "$report_file" > /dev/null if command -v sendmail > /dev/null 2>&1; then { echo "To: $EMAIL_RECIPIENT" echo "Subject: $EMAIL_SUBJECT - $host_entry (Attached)" echo "MIME-Version: 1.0" echo "Content-Type: multipart/mixed; boundary="MIXED-BOUNDARY"" echo echo "--MIXED-BOUNDARY" echo "Content-Type: text/plain" echo echo "Health check report for $host_entry is attached (compressed)." echo echo "--MIXED-BOUNDARY" echo "Content-Type: application/zip; name="$(basename "$zip_file")"" echo "Content-Disposition: attachment; filename="$(basename "$zip_file")"" echo "Content-Transfer-Encoding: base64" echo base64 "$zip_file" echo "--MIXED-BOUNDARY--" } | sendmail -t log_msg "Email with ZIP attachment sent using sendmail for $host_entry" elif command -v mailx > /dev/null 2>&1; then echo "Health check report for $host_entry is attached (compressed)." | mailx -a "$zip_file" -s "$EMAIL_SUBJECT - $host_entry (Attached)" "$EMAIL_RECIPIENT" log_msg "Email with ZIP attachment sent using mailx for $host_entry" else log_msg "ERROR: Neither sendmail nor mailx available for sending ZIP attachment." fi fi } send_email() { local host_entry="$1" local report_file="$2" local max_inline_size=2097152 # 2MB in bytes local file_size file_size=$(stat -c%s "$report_file") if [[ "$file_size" -le "$max_inline_size" ]]; then # Send as HTML body { echo "To: $EMAIL_RECIPIENT" echo "Subject: $EMAIL_SUBJECT - $host_entry" echo "Content-Type: text/html" echo cat "$report_file" } | sendmail -t log_msg "Email sent as inline HTML for $host_entry" else # Send as attachment { echo "To: $EMAIL_RECIPIENT" echo "Subject: $EMAIL_SUBJECT - $host_entry (Attached)" echo "MIME-Version: 1.0" echo "Content-Type: multipart/mixed; boundary="MIXED-BOUNDARY"" echo echo "--MIXED-BOUNDARY" echo "Content-Type: text/plain" echo echo "Health check report is attached for $host_entry (size exceeds 2MB)." echo echo "--MIXED-BOUNDARY" echo "Content-Type: text/html; name="$(basename "$report_file")"" echo "Content-Disposition: attachment; filename="$(basename "$report_file")"" echo "Content-Transfer-Encoding: base64" echo base64 "$report_file" echo "--MIXED-BOUNDARY--" } | sendmail -t log_msg "Email sent with attachment for $host_entry (file too large)" fi } ## #!/bin/bash # Refreshable PDB Clone Precheck Script # Usage: ./pdb_clone_precheck.sh # Define SQL*Plus path (update if needed) SQLPLUS="/u01/app/oracle/product/19c/dbhome_1/bin/sqlplus -s" # Check input file if [ $# -ne 1 ] || [ ! -f "$1" ]; then echo "Usage: $0 " echo "Input file format:" echo "source_cdb|source_pdb|target_cdb|target_pdb" exit 1 fi INPUT_FILE="$1" LOG_FILE="pdb_clone_precheck_$(date +%Y%m%d_%H%M%S).log" # Precheck validation function validate_clone_prerequisites() { local src_cdb="$1" local src_pdb="$2" local tgt_cdb="$3" local tgt_pdb="$4" echo "=================================================================" echo " Starting precheck for:" echo " Source: $src_cdb/$src_pdb" echo " Target: $tgt_cdb/$tgt_pdb" echo "=================================================================" # 1. Source CDB Checks echo "Checking Source CDB ($src_cdb)..." $SQLPLUS "/ as sysdba" < # Configuration SQLPLUS="/u01/app/oracle/product/19c/dbhome_1/bin/sqlplus -s" HTML_FILE="pdb_precheck_$(date +%Y%m%d_%H%M%S).html" EMAIL_TO="$2" EMAIL_FROM="dba@company.com" EMAIL_SUBJECT="Oracle PDB Clone Precheck Report" # HTML Template Functions html_header() { echo " PDB Clone Precheck Report

Oracle PDB Clone Precheck Report

Generated at: $(date)

" > "$HTML_FILE" } html_add_row() { local check="$1" local source="$2" local target="$3" local status="$4" local details="$5" case $status in "PASS") class="pass" ;; "FAIL") class="fail" ;; *) class="warning" ;; esac echo "" >> "$HTML_FILE" } html_footer() { echo "
CheckSourceTargetStatusDetails
$check $source $target $status $details

Report generated by Oracle Precheck Script

" >> "$HTML_FILE" } # Database Check Functions run_sql() { local conn="$1" local query="$2" $SQLPLUS -S "/ as sysdba" < [email]" && exit 1 html_header while IFS="|" read -r src_cdb src_pdb tgt_cdb tgt_pdb; do [[ "$src_cdb" =~ ^# || -z "$src_cdb" ]] && continue validate_clone_prerequisites "$src_cdb" "$src_pdb" "$tgt_cdb" "$tgt_pdb" done < "$1" html_footer send_email echo "Precheck completed. HTML report: $HTML_FILE" exit 0 #### #!/bin/bash # Enhanced PDB Clone Precheck Script with Connectivity Checks # Usage: ./pdb_clone_precheck.sh # Configuration SQLPLUS="/u01/app/oracle/product/19c/dbhome_1/bin/sqlplus -s" HTML_FILE="pdb_precheck_$(date +%Y%m%d_%H%M%S).html" EMAIL_TO="$2" EMAIL_FROM="dba@company.com" EMAIL_SUBJECT="Oracle PDB Clone Precheck Report" OVERALL_STATUS=0 # HTML Template Functions (keep existing implementation) html_header() { ... } html_add_row() { ... } html_footer() { ... } # Database Connection Validation check_db_connectivity() { local cdb="$1" local cdb_type="$2" local query="SELECT 1 FROM DUAL;" echo "Checking $cdb_type connectivity ($cdb)..." local output=$(run_sql "/@$cdb as sysdba" "$query" 2>&1) if [ $? -ne 0 ]; then html_add_row "$cdb_type Connectivity" "$cdb" "N/A" "FAIL" "Connection failed: $output" return 1 else html_add_row "$cdb_type Connectivity" "$cdb" "N/A" "PASS" "Successfully connected" return 0 fi } # Enhanced Validation Function with Connectivity Checks validate_clone_prerequisites() { local src_cdb="$1" local src_pdb="$2" local tgt_cdb="$3" local tgt_pdb="$4" local status=0 # Check source DB connectivity if ! check_db_connectivity "$src_cdb" "Source CDB"; then OVERALL_STATUS=1 status=1 fi # Check target DB connectivity if ! check_db_connectivity "$tgt_cdb" "Target CDB"; then OVERALL_STATUS=1 status=1 fi # Skip further checks if connectivity failed [ $status -ne 0 ] && return 1 # Proceed with other checks (keep existing implementation) # TDE, Charset, Patching checks... } # Main Execution with Exit Code Handling [ $# -lt 1 ] && echo "Usage: $0 [email]" && exit 1 html_header while IFS="|" read -r src_cdb src_pdb tgt_cdb tgt_pdb; do [[ "$src_cdb" =~ ^# || -z "$src_cdb" ]] && continue echo "Processing: $src_cdb/$src_pdb -> $tgt_cdb/$tgt_pdb" validate_clone_prerequisites "$src_cdb" "$src_pdb" "$tgt_cdb" "$tgt_pdb" done < "$1" html_footer # Send email and exit with proper status [ -n "$EMAIL_TO" ] && send_email echo "Precheck completed. Exit code: $OVERALL_STATUS" exit $OVERALL_STATUS ### check_db_connectivity() { local cdb="$1" local cdb_type="$2" local query="SELECT 1 FROM DUAL;" echo "Checking $cdb_type connectivity ($cdb)..." local output=$(run_sql "/@$cdb as sysdba" "$query" 2>&1) if [ $? -ne 0 ]; then html_add_row "$cdb_type Connectivity" "$cdb" "N/A" "FAIL" "Connection failed: $output" return 1 else html_add_row "$cdb_type Connectivity" "$cdb" "N/A" "PASS" "Successfully connected" return 0 fi } ### validate_clone_prerequisites() { # Check source DB connectivity if ! check_db_connectivity "$src_cdb" "Source CDB"; then OVERALL_STATUS=1 status=1 fi # Check target DB connectivity if ! check_db_connectivity "$tgt_cdb" "Target CDB"; then OVERALL_STATUS=1 status=1 fi # Skip further checks if connectivity failed [ $status -ne 0 ] && return 1 # ... existing checks ... } #### #!/bin/bash # Refreshable PDB Clone Precheck Script with Parameter Comparison # Usage: ./pdb_clone_precheck.sh # Configuration SQLPLUS="/u01/app/oracle/product/19c/dbhome_1/bin/sqlplus -s" HTML_FILE="pdb_precheck_$(date +%Y%m%d_%H%M%S).html" EMAIL_TO="$2" EMAIL_FROM="dba@company.com" EMAIL_SUBJECT="Oracle PDB Clone Precheck Report" OVERALL_STATUS=0 EXCLUDED_PARAMS="db_name|db_unique_name|instance_name|control_files|local_listener|remote_login_passwordfile|log_archive_dest_1|dg_broker_config_file1|dg_broker_config_file2" # HTML Template Functions (keep existing implementation) html_header() { ... } html_add_row() { ... } html_footer() { ... } # Parameter Comparison Function compare_parameters() { local src_cdb="$1" local tgt_cdb="$2" local differences=0 # Get parameters from both databases src_params=$(mktemp) tgt_params=$(mktemp) run_sql "/@$src_cdb as sysdba" "SELECT name, value FROM v\$system_parameter WHERE name NOT IN (${EXCLUDED_PARAMS//|/,}) ORDER BY name;" | awk -F' ' '{print $1 "|" $2}' > "$src_params" run_sql "/@$tgt_cdb as sysdba" "SELECT name, value FROM v\$system_parameter WHERE name NOT IN (${EXCLUDED_PARAMS//|/,}) ORDER BY name;" | awk -F' ' '{print $1 "|" $2}' > "$tgt_params" # Compare parameters and format output diff --suppress-common-lines -y "$src_params" "$tgt_params" | while read -r line; do param=$(echo "$line" | awk -F'|' '{print $1}' | tr -d ' ') src_val=$(echo "$line" | awk -F'|' '{print $2}' | awk '{$1=$1;print}') tgt_val=$(echo "$line" | awk -F'|' '{print $4}' | awk '{$1=$1;print}') html_add_row "Parameter: $param" "$src_val" "$tgt_val" "FAIL" "Parameter mismatch" ((differences++)) done [ $differences -gt 0 ] && OVERALL_STATUS=1 rm "$src_params" "$tgt_params" } # Enhanced Validation Function validate_clone_prerequisites() { local src_cdb="$1" local src_pdb="$2" local tgt_cdb="$3" local tgt_pdb="$4" local status=0 # Check connectivity (existing implementation) # ... # Perform parameter comparison compare_parameters "$src_cdb" "$tgt_cdb" # Existing checks (TDE, charset, etc.) # ... } # Main Execution (keep existing flow) # ... ### #!/bin/bash # Refreshable PDB Clone Precheck Script with Storage Checks # Usage: ./pdb_clone_precheck.sh # Configuration SQLPLUS="/u01/app/oracle/product/19c/dbhome_1/bin/sqlplus -s" HTML_FILE="pdb_precheck_$(date +%Y%m%d_%H%M%S).html" EMAIL_TO="$2" EMAIL_FROM="dba@company.com" EMAIL_SUBJECT="Oracle PDB Clone Precheck Report" OVERALL_STATUS=0 # Existing functions (html_header, html_add_row, html_footer, etc.) check_max_pdb_storage() { local src_cdb="$1" local src_pdb="$2" local tgt_cdb="$3" local tgt_pdb="$4" # Get source PDB storage limit src_storage=$(run_sql "/@$src_cdb as sysdba" " ALTER SESSION SET CONTAINER = $src_pdb; SELECT value FROM v\$parameter WHERE name = 'max_pdb_storage';") # Get target CDB storage capabilities (if target PDB exists) tgt_storage=$(run_sql "/@$tgt_cdb as sysdba" " SELECT NVL2(p.name, (SELECT value FROM v\$parameter WHERE name = 'max_pdb_storage' AND con_id = p.con_id), 'NOT_CREATED') FROM v\$pdbs p WHERE p.name = '$tgt_pdb';") # Format comparison results if [ "$tgt_storage" == "NOT_CREATED" ]; then html_add_row "MAX_PDB_STORAGE" "$src_storage" "N/A" "INFO" "Target PDB not created" elif [ "$src_storage" != "$tgt_storage" ]; then html_add_row "MAX_PDB_STORAGE" "$src_storage" "$tgt_storage" "WARN" "Storage limit mismatch" OVERALL_STATUS=1 else html_add_row "MAX_PDB_STORAGE" "$src_storage" "$tgt_storage" "PASS" "Storage limits match" fi } validate_clone_prerequisites() { local src_cdb="$1" local src_pdb="$2" local tgt_cdb="$3" local tgt_pdb="$4" local status=0 # Existing connectivity checks... # MAX_PDB_STORAGE Check check_max_pdb_storage "$src_cdb" "$src_pdb" "$tgt_cdb" "$tgt_pdb" # Existing TDE, charset, patching checks... } # Main execution remains the same pdb_clone_precheck/ ├── precheck.conf # Configuration variables ├── precheck_main.sh # Main execution script ├── precheck_helpers.sh # Core helper functions └── precheck_db_checks.sh # Database validation functions 1. precheck.conf (Configuration File): bash #!/bin/bash # Configuration File - Edit these values for your environment # Database Configuration SQLPLUS="/u01/app/oracle/product/19c/dbhome_1/bin/sqlplus -s" EXCLUDED_PARAMS="db_name|db_unique_name|instance_name|control_files|local_listener|remote_login_passwordfile" WHITELISTED_PARAMS="compatible|db_block_size|memory_target" # Email Configuration EMAIL_FROM="dba@company.com" EMAIL_SUBJECT="Oracle PDB Clone Precheck Report" # File Paths LOG_DIR="./logs" REPORT_DIR="./reports" 2. precheck_helpers.sh (Helper Functions): bash #!/bin/bash # Helper Functions html_header() { echo "

Oracle PDB Clone Precheck Report

Generated at: $(date)

" } html_add_row() { # Existing implementation } html_footer() { echo "
" } validate_input_file() { [ $# -ne 1 ] || [ ! -f "$1" ] && return 1 return 0 } initialize_directories() { mkdir -p "$LOG_DIR" "$REPORT_DIR" } 3. precheck_db_checks.sh (Database Validation Functions): bash #!/bin/bash # Database Validation Functions check_db_connectivity() { # Existing implementation } check_tde_settings() { # Existing implementation } compare_parameters() { # Existing implementation } check_max_pdb_storage() { # Existing implementation } check_patch_level() { # Existing implementation } 4. precheck_main.sh (Main Script): bash #!/bin/bash # Main Execution Script # Source configuration and functions source ./precheck.conf source ./precheck_helpers.sh source ./precheck_db_checks.sh # Initialize environment initialize_directories HTML_FILE="${REPORT_DIR}/pdb_precheck_$(date +%Y%m%d_%H%M%S).html" OVERALL_STATUS=0 # Main logic html_header > "$HTML_FILE" while IFS="|" read -r src_cdb src_pdb tgt_cdb tgt_pdb; do [[ "$src_cdb" =~ ^# || -z "$src_cdb" ]] && continue validate_clone_prerequisites "$src_cdb" "$src_pdb" "$tgt_cdb" "$tgt_pdb" done < "$1" html_footer >> "$HTML_FILE" send_email exit $OVERALL_STATUS Directory Structure Explanation: pdb_clone_precheck/ ├── config/ │ └── precheck.conf # Environment-specific settings ├── lib/ │ ├── precheck_helpers.sh # Generic helper functions │ └── precheck_db_checks.sh # Database-specific checks ├── bin/ │ └── precheck_main.sh # Main executable script ├── logs/ # Auto-generated logs └── reports/ # HTML reports Usage Instructions: Set Up Directory Structure: bash mkdir -p pdb_clone_precheck/{config,lib,bin,logs,reports} Make Scripts Executable: bash chmod +x bin/precheck_main.sh Run the Script: bash cd pdb_clone_precheck bin/precheck_main.sh input_file.txt dba@company.com ### #!/bin/bash # Variables (update as needed) SYS_USER="sys" SYS_PASSWORD="your_password" EMAIL_RECIPIENT="you@example.com" INPUT_FILE="db_targets.txt" SQL_SCRIPT="db_healthcheck_clean.sql" LOG_DIR="./healthcheck_logs" EMAIL_SUBJECT="Oracle DB Health Check Report" LOG_FILE="$LOG_DIR/healthcheck_run.log" # Ensure log directory exists mkdir -p "$LOG_DIR" # Function to log messages with timestamp log_msg() { local msg="$1" echo "$(date '+%Y-%m-%d %H:%M:%S') : $msg" | tee -a "$LOG_FILE" } # Function to extract DB info get_db_info() { log_msg "Reading DB targets from $INPUT_FILE" if [[ ! -f "$INPUT_FILE" ]]; then log_msg "ERROR: Input file $INPUT_FILE not found." exit 1 fi mapfile -t DB_LIST < <(grep -v '^#' "$INPUT_FILE" | tr -d '[:space:]' | grep -v '^$') if [[ ${#DB_LIST[@]} -eq 0 ]]; then log_msg "ERROR: No valid DB entries found in $INPUT_FILE" exit 1 fi } # Function to run health check for a DB db_health_check() { local host_entry="$1" local timestamp timestamp=$(date +"%Y%m%d_%H%M%S") local report_file="$LOG_DIR/healthcheck_$(echo "$host_entry" | tr '/:' '_')_${timestamp}.html" log_msg "Running health check for $host_entry" sqlplus -s "$SYS_USER/$SYS_PASSWORD@$host_entry as sysdba" @"$SQL_SCRIPT" | awk 'BEGIN { print "
" } { print } END { print "
" }' > "$report_file" if [[ ! -s "$report_file" ]]; then log_msg "WARNING: Empty or failed report for $host_entry" else log_msg "Health check report saved to $report_file" fi echo "$report_file" } # Function to send HTML email, fallback to mailx if sendmail not present send_email() { local host_entry="$1" local report_file="$2" if command -v sendmail > /dev/null 2>&1; then { echo "To: $EMAIL_RECIPIENT" echo "Subject: $EMAIL_SUBJECT - $host_entry" echo "Content-Type: text/html" echo cat "$report_file" } | sendmail -t log_msg "Email sent via sendmail for $host_entry" elif command -v mailx > /dev/null 2>&1; then mailx -a "Content-Type: text/html" -s "$EMAIL_SUBJECT - $host_entry" "$EMAIL_RECIPIENT" < "$report_file" log_msg "Email sent via mailx for $host_entry" else log_msg "ERROR: No supported mail client (sendmail or mailx) found." fi } # Main driver function main() { get_db_info for host_entry in "${DB_LIST[@]}"; do log_msg "Processing target: $host_entry" report_file=$(db_health_check "$host_entry") send_email "$host_entry" "$report_file" done log_msg "Health check run complete for all targets." } # Execute main main