| #!/bin/bash |
| # |
| # Copyright 2024 Nordix Foundation. |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| # |
| |
| set -o errexit # Exit on most errors |
| set -o nounset # Disallow expansion of unset variables |
| set -o pipefail # Use last non-zero exit code in a pipeline |
| set -o xtrace # Trace logging |
| |
| ############################################################################################################################# |
| ################################################ F U N C T I O N S ########################################################## |
| ############################################################################################################################# |
| |
| JENKINS_JOB_URL="https://jenkins.nordix.org/job/onap-cps-performance-test-k6" |
| |
| latestRecordedBuild="" |
| formattedTimestampOfLatestRecordedBuild="" |
| |
| # Get last completed build number |
| # The number has not been plotted on the graphs yet |
| getLastCompletedBuildNumber() { |
| curl -s "${JENKINS_JOB_URL}/lastCompletedBuild/buildNumber" |
| } |
| |
| # Get the last recorded build number from local workspace of the jenkins |
| # The number has already been plotted on the graphs |
| getLastRecordedBuildNumber() { |
| local file_name="data/1.csv" |
| if [ -f "$file_name" ]; then |
| tail -n 1 "$file_name" | cut -d, -f1 |
| else |
| echo 0 |
| fi |
| } |
| |
| # Get all builds numbers |
| getAllBuildNumbers() { |
| curl -s "${JENKINS_JOB_URL}/api/json?tree=allBuilds\[id\]" | jq -r '.allBuilds[].id' | sort -n |
| } |
| |
| # Extract CSV results from the console text of k6 performance job |
| getSummaryCsv() { |
| buildToRead=$1 |
| consoleURL="${JENKINS_JOB_URL}/${buildToRead}/consoleText" |
| # Extract summary CSV from Jenkins console output |
| curl -s "$consoleURL" | sed -n '/-- BEGIN CSV REPORT/, /-- END CSV REPORT/p' | grep "^[0-9].*,.*,.*,.*,.*$" || true |
| } |
| |
| # Get and record the latest k6-job-results with the build number for all tests |
| getAndRecordPerformanceJobResultForBuild() { |
| buildNumber="$1" |
| getSummaryCsv "$buildNumber" > summary.csv |
| if [ -s summary.csv ]; then |
| # Store the test cases, names, and units |
| cut -d, -f1,2,3 summary.csv > test-cases.csv |
| # Process each row in the CSV, storing the limit cps_expectation and actual value for this build |
| while IFS=, read -r test_case test_name units limit cps_expectation actual; do |
| echo "$buildNumber,$limit,$cps_expectation,$actual" >> "data/$test_case.csv" |
| done < summary.csv |
| else |
| # No CSV data for this build, so record zeroes for this build in each existing data file |
| for dataFile in data/*.csv; do |
| echo "$buildNumber,0,0,0" >> "$dataFile" |
| done |
| fi |
| recordLatestRecordedBuild "$buildNumber" |
| } |
| |
| # Format the date and time of the latest build |
| recordLatestRecordedBuild() { |
| local latestBuildToRecord="$1" |
| local timestampOfLatestRecordedBuild |
| timestampOfLatestRecordedBuild=$(curl -s "${JENKINS_JOB_URL}/${latestBuildToRecord}/api/json?tree=timestamp" | jq -r '.timestamp') |
| formattedTimestampOfLatestRecordedBuild=$(date -d "@$((timestampOfLatestRecordedBuild / 1000))" "+%B %e, %Y at %H:%M") |
| latestRecordedBuild=$latestBuildToRecord |
| } |
| |
| # Plot the image (graph) in png format |
| buildPlotImage() { |
| dataFile="$1" |
| chartFileName="$2" |
| units="$3" |
| numberOfBuilds="$4" |
| |
| y_max=$(findMaxPlotRange "$dataFile") |
| |
| # Create a temporary Gnuplot script |
| cat <<EOT >gnuplot_script.gp |
| set datafile separator "," |
| set terminal pngcairo size 1500,600 |
| set output "${chartFileName}" |
| set xlabel "Build" |
| set ylabel "${units}" |
| |
| # Set y-axis range based on y_max |
| set yrange [0 : ${y_max}] |
| |
| set style line 1 lc rgb '#d3d3d3' lt 1 lw 1 dashtype 2 # Grid style |
| set style line 2 lc rgb '#0000FF' lt 1 lw 1 # threshold - Blue |
| set style line 3 lc rgb '#00FF00' lt 1 lw 1 # cps-limit - Green |
| set style line 4 lc rgb '#800080' lt 1 lw 1 # measured - Purple |
| |
| set grid ytics ls 1 |
| set xtics rotate |
| |
| # Use plot command with explicit column assignments and lines for all series |
| EOT |
| |
| if [ "$numberOfBuilds" -eq 100 ]; then |
| cat <<EOT >>gnuplot_script.gp |
| plot '<(tail -n $numberOfBuilds $dataFile)' using 1:2 with lines linestyle 2 title "threshold", \ |
| '<(tail -n $numberOfBuilds $dataFile)' using 1:3 with lines linestyle 3 title "cps-limit", \ |
| '<(tail -n $numberOfBuilds $dataFile)' using 1:4:xtic(1) with linespoints linestyle 4 title "measured" |
| EOT |
| elif [ "$numberOfBuilds" -eq 168 ]; then |
| cat <<EOT >>gnuplot_script.gp |
| plot '<(tail -n $numberOfBuilds $dataFile | awk "NR % 2 == 0")' using 1:2 with lines linestyle 2 title "threshold", \ |
| '<(tail -n $numberOfBuilds $dataFile | awk "NR % 2 == 0")' using 1:3 with lines linestyle 3 title "cps-limit", \ |
| '<(tail -n $numberOfBuilds $dataFile | awk "NR % 2 == 0")' using 1:4:xtic(1) with linespoints linestyle 4 title "measured" |
| EOT |
| elif [ "$numberOfBuilds" -eq 720 ]; then |
| cat <<EOT >>gnuplot_script.gp |
| plot '<(tail -n $numberOfBuilds $dataFile | awk "NR % 7 == 0")' using 1:2 with lines linestyle 2 title "threshold", \ |
| '<(tail -n $numberOfBuilds $dataFile | awk "NR % 7 == 0")' using 1:3 with lines linestyle 3 title "cps-limit", \ |
| '<(tail -n $numberOfBuilds $dataFile | awk "NR % 7 == 0")' using 1:4:xtic(1) with linespoints linestyle 4 title "measured" |
| EOT |
| fi |
| |
| # Run the temporary Gnuplot script |
| gnuplot gnuplot_script.gp |
| |
| # Remove the temporary Gnuplot script |
| rm gnuplot_script.gp |
| } |
| |
| # Set upper limit of the graphs to 20% bigger than largest value to have more space above the plot |
| findMaxPlotRange() { |
| dataFile="$1" |
| awk -F, '{ |
| if ($2 > max) max = $2; |
| if ($3 > max) max = $3; |
| if ($4 > max) max = $4; |
| } END { |
| if (max == 0) { |
| max = 100; |
| } |
| print max * 1.2; |
| }' "$dataFile" |
| } |
| |
| generateHtmlReport() { |
| local reportTitle="$1" |
| local outputFile="$2" |
| local numberOfBuilds="$3" |
| |
| cat <<EOT >"$outputFile" |
| <!DOCTYPE html> |
| <html> |
| <head> |
| <title>$reportTitle</title> |
| </head> |
| <body> |
| <h1 style="text-align: center;">$reportTitle</h1> |
| <h4>Last updated for performance job build no. $latestRecordedBuild on $formattedTimestampOfLatestRecordedBuild</h4> |
| <table align="center"> |
| EOT |
| |
| # Loop through the tests to generate the HTML rows which consists of the plot-image |
| while IFS=, read -r test_case test_name units; do |
| dataFile="data/$test_case.csv" |
| # Plot the image (graph) in png format |
| buildPlotImage "$dataFile" "data/${test_case}_${numberOfBuilds}.png" "$units" "$numberOfBuilds" |
| # Output to HTML |
| cat <<EOF >>"$outputFile" |
| <tr> |
| <td align="center" style="padding: 10px;"> |
| <figcaption>$test_case. $test_name ($units)</figcaption> |
| <img src="data/${test_case}_${numberOfBuilds}.png" width="750" height="300"> |
| </td> |
| </tr> |
| EOF |
| done < test-cases.csv |
| |
| # Close the HTML file |
| cat <<EOT >>"$outputFile" |
| </table> |
| <p>The plots are generated from the output of the Nordix Jenkins job: |
| <a href="https://jenkins.nordix.org/job/onap-cps-performance-test-k6/">onap-cps-performance-test-k6</a>.</p> |
| <p>The k6 performance tests run are described in the |
| <a href="https://wiki.onap.org/display/DW/CPS-391Spike%3A+Define+and+Agree+NCMP+REST+Interface#CPS391Spike:DefineandAgreeNCMPRESTInterface-Characteristics">NCMP REST Interface Characteristics</a> documentation.</p> |
| </body> |
| </html> |
| EOT |
| } |
| |
| |
| ############################################################################################################################# |
| ################################################ M A I N #################################################################### |
| ############################################################################################################################# |
| |
| # Install dependencies |
| sudo apt-get install -y bc gnuplot jq |
| |
| # Download data from CPS performance Jenkins job |
| cd "$WORKSPACE" |
| if [ ! -d data ]; then |
| # If workspace is empty, pull data from all previous performance job runs |
| mkdir -p data |
| for buildNumber in $(getAllBuildNumbers); do |
| getAndRecordPerformanceJobResultForBuild "$buildNumber" |
| done |
| else |
| # Append new data from latest jobs run |
| lastCompletedBuildNumber=$(getLastCompletedBuildNumber) |
| lastRecordedBuildNumber=$(getLastRecordedBuildNumber) |
| # Check if last completed build number is greater than last recorded build number |
| if [ "$lastCompletedBuildNumber" -gt "$lastRecordedBuildNumber" ]; then |
| for ((i = lastRecordedBuildNumber + 1; i <= lastCompletedBuildNumber; i++)); do |
| # Process the new builds which hasn't been recorded yet |
| getAndRecordPerformanceJobResultForBuild "$i" |
| done |
| else |
| echo "No new builds to process." |
| exit |
| fi |
| fi |
| |
| reportTitleDaily="daily k6 tests performance review" |
| outputFileDaily="k6TestsPerformanceReview.html" |
| generateHtmlReport "$reportTitleDaily" "$outputFileDaily" "100" |
| |
| reportTitleWeekly="weekly k6 tests performance review" |
| outputFileWeekly="k6TestsPerformanceReviewWeekly.html" |
| generateHtmlReport "$reportTitleWeekly" "$outputFileWeekly" "168" |
| |
| reportTitleMonthly="monthly k6 tests performance review" |
| outputFileMonthly="k6TestsPerformanceReviewMonthly.html" |
| generateHtmlReport "$reportTitleMonthly" "$outputFileMonthly" "720" |