Friday, December 5, 2025

timings

#!/bin/ksh

# =============================================================================

# Script      : adop_patch_timing_report.ksh

# Author      : Abdul Muqeet (inspired by your previous successful scripts)

# Purpose     : Calculate exact duration of each individual patch applied via ADOP

# Environment : Oracle EBS R12.2 (works on both Run and Patch edition)

# Usage       : nohup ./adop_patch_timing_report.ksh &

# Output      : /tmp/adop_patch_timing_report_$(date +%Y%m%d_%H%M).html + .csv

# Tested on   : Solaris SPARC & Linux (your environments)

# =============================================================================


REPORT_DATE=$(date +%Y%m%d_%H%M%S)

HTML_FILE="/tmp/adop_patch_timing_report_${REPORT_DATE}.html"

CSV_FILE="/tmp/adop_patch_timing_report_${REPORT_DATE}.csv"

TEMP_LOG="/tmp/adop_timing_temp_${REPORT_DATE}.log"


# === Change these if your structure is different =============================

ADOP_LOG_BASE="${ADOP_LOG_HOME:-$APPLRGF/adop}"   # most environments have symlink

# fallback if ADOP_LOG_HOME not set

[ ! -d "$ADOP_LOG_BASE" ] && ADOP_LOG_BASE="$RUN_BASE/EBSapps/log/adop"


echo "Scanning ADOP logs in $ADOP_LOG_BASE ..."


> "$CSV_FILE"

echo "Instance,Session_ID,Phase,Patch_Number,Start_Time,End_Time,Duration_Minutes" >> "$CSV_FILE"


cat << 'EOF' > "$HTML_FILE"

<!DOCTYPE html>

<html><head>

<meta charset="UTF-8">

<title>ADOP Individual Patch Timing Report - $(date '+%d-%b-%Y %H:%M')</title>

<style>

  body { font-family: Arial, sans-serif; margin: 20px; }

  h1 { color: #2c3e50; }

  table { border-collapse: collapse; width: 100%; margin-top: 20px; }

  th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }

  th { background-color: #3498db; color: white; }

  tr:nth-child(even) { background-color: #f2f2f2; }

  .slow { background-color: #fadbd8 !important; }   /* > 120 mins */

  .medium { background-color: #fef9e7 !important; } /* 60-120 mins */

</style>

</head><body>

<h1>Oracle EBS R12.2 – Individual Patch Application Duration Report</h1>

<p>Generated on: $(date '+%d-%b-%Y %H:%M:%S') | Instance: $TWO_TASK | Host: $(hostname)</p>

<table>

<tr><th>Instance</th><th>Session ID</th><th>Phase</th><th>Patch</th><th>Start</th><th>End</th><th>Duration (min)</th></tr>

EOF


find "$ADOP_LOG_BASE" -type d -name "[0-9]*_[0-9]*" | sort | while read SESSION_DIR; do

    SESSION_ID=$(basename "$SESSION_DIR")

    INSTANCE="${TWO_TASK:-$(echo $SESSION_DIR | awk -F'/' '{print $(NF-3)}')}"


    # Look for apply logs in both run and patch edition

    for PHASE in apply finalize cutover cleanup; do

        PHASE_DIR="$SESSION_DIR/${PHASE}"

        [ ! -d "$PHASE_DIR" ] && continue


        # Most accurate timestamps come from the driver log

        DRIVER_LOG=$(find "$PHASE_DIR" -name "*_apply_*.log" | head -1)

        [ -z "$DRIVER_LOG" ] && continue


        echo "Processing $SESSION_ID - $PHASE ..." >> "$TEMP_LOG"


        awk -v inst="$INSTANCE" -v sid="$SESSION_ID" -v phase="$PHASE" '

        BEGIN {

            in_patch = 0

            start_time = ""

        }

        /Applying patch/ || /Start time:/ {

            if ($0 ~ /Applying.*patch [0-9]+/) {

                if (in_patch && start_time != "") {

                    end_time = $1 " " $2

                    split(start_time, s, /[:.]/)

                    split(end_time,   e, /[:.]/)

                    duration = (e[1]*3600 + e[2]*60 + e[3]) - (s[1]*3600 + s[2]*60 + s[3])

                    mins = int(duration/60)

                    printf "%s,%s,%s,%s,%s,%s,%.1f\n", inst, sid, prev_phase, prev_patch, start_time, prev_end_time, mins >> "'"$CSV_FILE"'"

                }

                # New patch starts

                match($0, /patch [0-9]+/, a)

                prev_patch = a[0]

                gsub(/patch /,"",prev_patch)

                start_time = $1 " " $2

                prev_phase = phase

                prev_end_time = ""

                in_patch = 1

            }

        }

        /End time:/ {

            if (in_patch) {

                prev_end_time = $3 " " $4

            }

        }

        END {

            if (in_patch && start_time != "" && prev_end_time != "") {

                split(start_time, s, /[:.]/)

                split(prev_end_time, e, /[:.]/)

                duration = (e[1]*3600 + e[2]*60 + e[3]) - (s[1]*3600 + s[2]*60 + s[3])

                mins = int(duration/60)

                printf "%s,%s,%s,%s,%s,%s,%.1f\n", inst, sid, phase, prev_patch, start_time, prev_end_time, mins >> "'"$CSV_FILE"'"

            }

        }

        ' "$DRIVER_LOG"

    done

done


# Generate HTML rows from CSV

tail -n +2 "$CSV_FILE" | sort -t',' -k7nr | awk -F',' '

{

    duration = $7

    rowclass = ""

    if (duration > 120)  ? rowclass="slow" : 

    (duration > 60)   ? rowclass="medium" : rowclass=""

    print "<tr class=\"" rowclass "\">",

          "<td>" $1 "</td><td>" $2 "</td><td>" $3 "</td>",

          "<td>" $4 "</td><td>" $5 "</td><td>" $6 "</td>",

          "<td><b>" $7 " min</b></td></tr>"

}' >> "$HTML_FILE"


cat << 'EOF' >> "$HTML_FILE"

</table>

<br>

<p><b>Color legend:</b> <span style="background:#fadbd8;padding:3px">> 120 min</span> 

<span style="background:#fef9e7;padding:3px">60–120 min</span></p>

<p>Report & CSV also saved in /tmp with timestamp <b>adop_patch_timing_report_'$REPORT_DATE'</b></p>

</body></html>

EOF


# === Send email (your usual format) ===

SUBJECT="ADOP Patch Timing Report – $(date '+%d-%b-%Y')"

RECIPIENTS="your.team@company.com"   # change as needed


(

echo "Subject: $SUBJECT"

echo "MIME-Version: 1.0"

echo "Content-Type: text/html"

echo

cat "$HTML_FILE"

) | /usr/sbin/sendmail "$RECIPIENTS"


echo "Report completed → $HTML_FILE and $CSV_FILE"

echo "Email sent to $RECIPIENTS"


# Optional: auto-clean reports older than 30 days

find /tmp -name "adop_patch_timing_report_*" -mtime +30 -exec rm {} \;


exit 0

No comments:

Post a Comment