halil.cakal | 25ff8e8 | 2024-05-31 11:18:54 +0100 | [diff] [blame] | 1 | #!/bin/bash |
| 2 | # |
| 3 | # Copyright 2024 Nordix Foundation. |
| 4 | # |
| 5 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 | # you may not use this file except in compliance with the License. |
| 7 | # You may obtain a copy of the License at |
| 8 | # |
| 9 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | # |
| 11 | # Unless required by applicable law or agreed to in writing, software |
| 12 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | # See the License for the specific language governing permissions and |
| 15 | # limitations under the License. |
| 16 | # |
| 17 | |
| 18 | set -o errexit # Exit on most errors |
| 19 | set -o nounset # Disallow expansion of unset variables |
| 20 | set -o pipefail # Use last non-zero exit code in a pipeline |
danielhanrahan | 913c6ea | 2024-06-28 18:14:14 +0100 | [diff] [blame] | 21 | set -o xtrace # Trace logging |
halil.cakal | 25ff8e8 | 2024-05-31 11:18:54 +0100 | [diff] [blame] | 22 | |
| 23 | ############################################################################################################################# |
| 24 | ################################################ F U N C T I O N S ########################################################## |
| 25 | ############################################################################################################################# |
| 26 | |
halil.cakal | 25ff8e8 | 2024-05-31 11:18:54 +0100 | [diff] [blame] | 27 | JENKINS_JOB_URL="https://jenkins.nordix.org/job/onap-cps-performance-test-k6" |
| 28 | |
halil.cakal | 25ff8e8 | 2024-05-31 11:18:54 +0100 | [diff] [blame] | 29 | latestRecordedBuild="" |
danielhanrahan | 913c6ea | 2024-06-28 18:14:14 +0100 | [diff] [blame] | 30 | formattedTimestampOfLatestRecordedBuild="" |
halil.cakal | 25ff8e8 | 2024-05-31 11:18:54 +0100 | [diff] [blame] | 31 | |
| 32 | # Get last completed build number |
| 33 | # The number has not been plotted on the graphs yet |
| 34 | getLastCompletedBuildNumber() { |
| 35 | curl -s "${JENKINS_JOB_URL}/lastCompletedBuild/buildNumber" |
| 36 | } |
| 37 | |
| 38 | # Get the last recorded build number from local workspace of the jenkins |
| 39 | # The number has already been plotted on the graphs |
| 40 | getLastRecordedBuildNumber() { |
danielhanrahan | 913c6ea | 2024-06-28 18:14:14 +0100 | [diff] [blame] | 41 | local file_name="data/1.csv" |
halil.cakal | 25ff8e8 | 2024-05-31 11:18:54 +0100 | [diff] [blame] | 42 | if [ -f "$file_name" ]; then |
danielhanrahan | 913c6ea | 2024-06-28 18:14:14 +0100 | [diff] [blame] | 43 | tail -n 1 "$file_name" | cut -d, -f1 |
halil.cakal | 25ff8e8 | 2024-05-31 11:18:54 +0100 | [diff] [blame] | 44 | else |
danielhanrahan | 913c6ea | 2024-06-28 18:14:14 +0100 | [diff] [blame] | 45 | echo 0 |
halil.cakal | 25ff8e8 | 2024-05-31 11:18:54 +0100 | [diff] [blame] | 46 | fi |
| 47 | } |
| 48 | |
| 49 | # Get all builds numbers |
| 50 | getAllBuildNumbers() { |
| 51 | curl -s "${JENKINS_JOB_URL}/api/json?tree=allBuilds\[id\]" | jq -r '.allBuilds[].id' | sort -n |
| 52 | } |
| 53 | |
danielhanrahan | 913c6ea | 2024-06-28 18:14:14 +0100 | [diff] [blame] | 54 | # Extract CSV results from the console text of k6 performance job |
| 55 | getSummaryCsv() { |
halil.cakal | 25ff8e8 | 2024-05-31 11:18:54 +0100 | [diff] [blame] | 56 | buildToRead=$1 |
| 57 | consoleURL="${JENKINS_JOB_URL}/${buildToRead}/consoleText" |
danielhanrahan | 913c6ea | 2024-06-28 18:14:14 +0100 | [diff] [blame] | 58 | # Extract summary CSV from Jenkins console output |
halil.cakal | 838722c | 2024-09-06 15:30:10 +0100 | [diff] [blame] | 59 | curl -s "$consoleURL" | sed -n '/-- BEGIN CSV REPORT/, /-- END CSV REPORT/p' | grep "^[0-9].*,.*,.*,.*,.*$" || true |
halil.cakal | 25ff8e8 | 2024-05-31 11:18:54 +0100 | [diff] [blame] | 60 | } |
| 61 | |
| 62 | # Get and record the latest k6-job-results with the build number for all tests |
| 63 | getAndRecordPerformanceJobResultForBuild() { |
| 64 | buildNumber="$1" |
danielhanrahan | 913c6ea | 2024-06-28 18:14:14 +0100 | [diff] [blame] | 65 | getSummaryCsv "$buildNumber" > summary.csv |
| 66 | if [ -s summary.csv ]; then |
| 67 | # Store the test cases, names, and units |
| 68 | cut -d, -f1,2,3 summary.csv > test-cases.csv |
halil.cakal | b02a315 | 2024-09-17 15:53:09 +0100 | [diff] [blame] | 69 | # Process each row in the CSV, storing the limit cps_expectation and actual value for this build |
| 70 | while IFS=, read -r test_case test_name units limit cps_expectation actual; do |
| 71 | echo "$buildNumber,$limit,$cps_expectation,$actual" >> "data/$test_case.csv" |
danielhanrahan | 913c6ea | 2024-06-28 18:14:14 +0100 | [diff] [blame] | 72 | done < summary.csv |
halil.cakal | 25ff8e8 | 2024-05-31 11:18:54 +0100 | [diff] [blame] | 73 | else |
danielhanrahan | 913c6ea | 2024-06-28 18:14:14 +0100 | [diff] [blame] | 74 | # No CSV data for this build, so record zeroes for this build in each existing data file |
| 75 | for dataFile in data/*.csv; do |
halil.cakal | b02a315 | 2024-09-17 15:53:09 +0100 | [diff] [blame] | 76 | echo "$buildNumber,0,0,0" >> "$dataFile" |
danielhanrahan | 913c6ea | 2024-06-28 18:14:14 +0100 | [diff] [blame] | 77 | done |
halil.cakal | 25ff8e8 | 2024-05-31 11:18:54 +0100 | [diff] [blame] | 78 | fi |
danielhanrahan | 913c6ea | 2024-06-28 18:14:14 +0100 | [diff] [blame] | 79 | recordLatestRecordedBuild "$buildNumber" |
halil.cakal | 25ff8e8 | 2024-05-31 11:18:54 +0100 | [diff] [blame] | 80 | } |
| 81 | |
| 82 | # Format the date and time of the latest build |
| 83 | recordLatestRecordedBuild() { |
danielhanrahan | 913c6ea | 2024-06-28 18:14:14 +0100 | [diff] [blame] | 84 | local latestBuildToRecord="$1" |
| 85 | local timestampOfLatestRecordedBuild |
halil.cakal | 25ff8e8 | 2024-05-31 11:18:54 +0100 | [diff] [blame] | 86 | timestampOfLatestRecordedBuild=$(curl -s "${JENKINS_JOB_URL}/${latestBuildToRecord}/api/json?tree=timestamp" | jq -r '.timestamp') |
| 87 | formattedTimestampOfLatestRecordedBuild=$(date -d "@$((timestampOfLatestRecordedBuild / 1000))" "+%B %e, %Y at %H:%M") |
| 88 | latestRecordedBuild=$latestBuildToRecord |
| 89 | } |
| 90 | |
| 91 | # Plot the image (graph) in png format |
| 92 | buildPlotImage() { |
| 93 | dataFile="$1" |
| 94 | chartFileName="$2" |
danielhanrahan | 913c6ea | 2024-06-28 18:14:14 +0100 | [diff] [blame] | 95 | units="$3" |
halil.cakal | b02a315 | 2024-09-17 15:53:09 +0100 | [diff] [blame] | 96 | numberOfBuilds="$4" |
halil.cakal | 25ff8e8 | 2024-05-31 11:18:54 +0100 | [diff] [blame] | 97 | |
danielhanrahan | ea51278 | 2024-08-01 11:14:13 +0100 | [diff] [blame] | 98 | y_max=$(findMaxPlotRange "$dataFile") |
halil.cakal | 25ff8e8 | 2024-05-31 11:18:54 +0100 | [diff] [blame] | 99 | |
| 100 | # Create a temporary Gnuplot script |
| 101 | cat <<EOT >gnuplot_script.gp |
| 102 | set datafile separator "," |
| 103 | set terminal pngcairo size 1500,600 |
| 104 | set output "${chartFileName}" |
| 105 | set xlabel "Build" |
danielhanrahan | 913c6ea | 2024-06-28 18:14:14 +0100 | [diff] [blame] | 106 | set ylabel "${units}" |
halil.cakal | 56b61ea | 2024-10-03 10:26:56 +0100 | [diff] [blame] | 107 | EOT |
halil.cakal | 0d4a9c1 | 2024-09-10 16:05:36 +0100 | [diff] [blame] | 108 | |
| 109 | # Set y-axis range based on y_max |
halil.cakal | 56b61ea | 2024-10-03 10:26:56 +0100 | [diff] [blame] | 110 | if [[ "$units" == */second ]]; then |
| 111 | echo "set yrange [${y_max} : 0] reverse" >> gnuplot_script.gp |
| 112 | else |
| 113 | echo "set yrange [0 : ${y_max}]" >> gnuplot_script.gp |
| 114 | fi |
halil.cakal | 0d4a9c1 | 2024-09-10 16:05:36 +0100 | [diff] [blame] | 115 | |
halil.cakal | 56b61ea | 2024-10-03 10:26:56 +0100 | [diff] [blame] | 116 | cat <<EOT >>gnuplot_script.gp |
halil.cakal | 0d4a9c1 | 2024-09-10 16:05:36 +0100 | [diff] [blame] | 117 | set style line 1 lc rgb '#d3d3d3' lt 1 lw 1 dashtype 2 # Grid style |
| 118 | set style line 2 lc rgb '#0000FF' lt 1 lw 1 # threshold - Blue |
| 119 | set style line 3 lc rgb '#00FF00' lt 1 lw 1 # cps-limit - Green |
| 120 | set style line 4 lc rgb '#800080' lt 1 lw 1 # measured - Purple |
| 121 | |
danielhanrahan | ac1fea8 | 2024-08-29 12:50:48 +0100 | [diff] [blame] | 122 | set grid ytics ls 1 |
halil.cakal | 7ae8934 | 2024-06-14 17:28:29 +0100 | [diff] [blame] | 123 | set xtics rotate |
halil.cakal | 0d4a9c1 | 2024-09-10 16:05:36 +0100 | [diff] [blame] | 124 | |
| 125 | # Use plot command with explicit column assignments and lines for all series |
halil.cakal | 25ff8e8 | 2024-05-31 11:18:54 +0100 | [diff] [blame] | 126 | EOT |
| 127 | |
halil.cakal | b02a315 | 2024-09-17 15:53:09 +0100 | [diff] [blame] | 128 | if [ "$numberOfBuilds" -eq 100 ]; then |
| 129 | cat <<EOT >>gnuplot_script.gp |
halil.cakal | 0cca7e2 | 2024-11-08 14:44:15 +0000 | [diff] [blame] | 130 | plot '$dataFile' using 1:2 with lines linestyle 2 title "threshold", \ |
| 131 | '$dataFile' using 1:3 with lines linestyle 3 title "cps-limit", \ |
| 132 | '$dataFile' using 1:4:xtic(1) with linespoints linestyle 4 title "measured" |
halil.cakal | b02a315 | 2024-09-17 15:53:09 +0100 | [diff] [blame] | 133 | EOT |
| 134 | elif [ "$numberOfBuilds" -eq 720 ]; then |
| 135 | cat <<EOT >>gnuplot_script.gp |
halil.cakal | 0cca7e2 | 2024-11-08 14:44:15 +0000 | [diff] [blame] | 136 | unset xtics |
| 137 | plot '$dataFile' using 1:2 with lines linestyle 2 title "threshold", \ |
| 138 | '$dataFile' using 1:3 with lines linestyle 3 title "cps-limit", \ |
| 139 | '$dataFile' using 1:4 with lines linestyle 4 title "measured" |
halil.cakal | b02a315 | 2024-09-17 15:53:09 +0100 | [diff] [blame] | 140 | EOT |
| 141 | fi |
| 142 | |
halil.cakal | 25ff8e8 | 2024-05-31 11:18:54 +0100 | [diff] [blame] | 143 | # Run the temporary Gnuplot script |
| 144 | gnuplot gnuplot_script.gp |
| 145 | |
| 146 | # Remove the temporary Gnuplot script |
| 147 | rm gnuplot_script.gp |
| 148 | } |
| 149 | |
halil.cakal | 14089ff | 2024-09-24 15:35:29 +0100 | [diff] [blame] | 150 | # To set y-axis based on max of threshold or cps_expectation |
danielhanrahan | ea51278 | 2024-08-01 11:14:13 +0100 | [diff] [blame] | 151 | findMaxPlotRange() { |
| 152 | dataFile="$1" |
| 153 | awk -F, '{ |
halil.cakal | 14089ff | 2024-09-24 15:35:29 +0100 | [diff] [blame] | 154 | if (NF == 4 && $4 != "") { |
| 155 | if ($2 > max) max = $2; |
| 156 | if ($3 > max) max = $3; |
| 157 | } |
danielhanrahan | ea51278 | 2024-08-01 11:14:13 +0100 | [diff] [blame] | 158 | } END { |
halil.cakal | 0d4a9c1 | 2024-09-10 16:05:36 +0100 | [diff] [blame] | 159 | if (max == 0) { |
halil.cakal | 14089ff | 2024-09-24 15:35:29 +0100 | [diff] [blame] | 160 | max = 1; |
halil.cakal | 0d4a9c1 | 2024-09-10 16:05:36 +0100 | [diff] [blame] | 161 | } |
halil.cakal | 0cca7e2 | 2024-11-08 14:44:15 +0000 | [diff] [blame] | 162 | print max * 1.2; |
danielhanrahan | ea51278 | 2024-08-01 11:14:13 +0100 | [diff] [blame] | 163 | }' "$dataFile" |
| 164 | } |
halil.cakal | b02a315 | 2024-09-17 15:53:09 +0100 | [diff] [blame] | 165 | |
| 166 | generateHtmlReport() { |
| 167 | local reportTitle="$1" |
| 168 | local outputFile="$2" |
| 169 | local numberOfBuilds="$3" |
| 170 | |
| 171 | cat <<EOT >"$outputFile" |
| 172 | <!DOCTYPE html> |
| 173 | <html> |
| 174 | <head> |
| 175 | <title>$reportTitle</title> |
| 176 | </head> |
| 177 | <body> |
| 178 | <h1 style="text-align: center;">$reportTitle</h1> |
| 179 | <h4>Last updated for performance job build no. $latestRecordedBuild on $formattedTimestampOfLatestRecordedBuild</h4> |
| 180 | <table align="center"> |
| 181 | EOT |
| 182 | |
| 183 | # Loop through the tests to generate the HTML rows which consists of the plot-image |
| 184 | while IFS=, read -r test_case test_name units; do |
| 185 | dataFile="data/$test_case.csv" |
| 186 | # Plot the image (graph) in png format |
halil.cakal | 0cca7e2 | 2024-11-08 14:44:15 +0000 | [diff] [blame] | 187 | csvFile="data/${test_case}_${numberOfBuilds}.csv" |
| 188 | pngFile="data/${test_case}_${numberOfBuilds}.png" |
| 189 | tail -n $numberOfBuilds $dataFile > "$csvFile" |
| 190 | buildPlotImage "$csvFile" "$pngFile" "$units" "$numberOfBuilds" |
halil.cakal | b02a315 | 2024-09-17 15:53:09 +0100 | [diff] [blame] | 191 | # Output to HTML |
| 192 | cat <<EOF >>"$outputFile" |
| 193 | <tr> |
| 194 | <td align="center" style="padding: 10px;"> |
| 195 | <figcaption>$test_case. $test_name ($units)</figcaption> |
| 196 | <img src="data/${test_case}_${numberOfBuilds}.png" width="750" height="300"> |
| 197 | </td> |
| 198 | </tr> |
| 199 | EOF |
| 200 | done < test-cases.csv |
| 201 | |
| 202 | # Close the HTML file |
| 203 | cat <<EOT >>"$outputFile" |
| 204 | </table> |
| 205 | <p>The plots are generated from the output of the Nordix Jenkins job: |
| 206 | <a href="https://jenkins.nordix.org/job/onap-cps-performance-test-k6/">onap-cps-performance-test-k6</a>.</p> |
| 207 | <p>The k6 performance tests run are described in the |
| 208 | <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> |
| 209 | </body> |
| 210 | </html> |
| 211 | EOT |
| 212 | } |
| 213 | |
| 214 | |
halil.cakal | 25ff8e8 | 2024-05-31 11:18:54 +0100 | [diff] [blame] | 215 | ############################################################################################################################# |
| 216 | ################################################ M A I N #################################################################### |
| 217 | ############################################################################################################################# |
| 218 | |
| 219 | # Install dependencies |
| 220 | sudo apt-get install -y bc gnuplot jq |
| 221 | |
| 222 | # Download data from CPS performance Jenkins job |
| 223 | cd "$WORKSPACE" |
danielhanrahan | 913c6ea | 2024-06-28 18:14:14 +0100 | [diff] [blame] | 224 | if [ ! -d data ]; then |
halil.cakal | 25ff8e8 | 2024-05-31 11:18:54 +0100 | [diff] [blame] | 225 | # If workspace is empty, pull data from all previous performance job runs |
danielhanrahan | 913c6ea | 2024-06-28 18:14:14 +0100 | [diff] [blame] | 226 | mkdir -p data |
halil.cakal | 25ff8e8 | 2024-05-31 11:18:54 +0100 | [diff] [blame] | 227 | for buildNumber in $(getAllBuildNumbers); do |
| 228 | getAndRecordPerformanceJobResultForBuild "$buildNumber" |
| 229 | done |
| 230 | else |
| 231 | # Append new data from latest jobs run |
| 232 | lastCompletedBuildNumber=$(getLastCompletedBuildNumber) |
| 233 | lastRecordedBuildNumber=$(getLastRecordedBuildNumber) |
| 234 | # Check if last completed build number is greater than last recorded build number |
| 235 | if [ "$lastCompletedBuildNumber" -gt "$lastRecordedBuildNumber" ]; then |
| 236 | for ((i = lastRecordedBuildNumber + 1; i <= lastCompletedBuildNumber; i++)); do |
| 237 | # Process the new builds which hasn't been recorded yet |
| 238 | getAndRecordPerformanceJobResultForBuild "$i" |
| 239 | done |
| 240 | else |
| 241 | echo "No new builds to process." |
| 242 | fi |
| 243 | fi |
| 244 | |
halil.cakal | b02a315 | 2024-09-17 15:53:09 +0100 | [diff] [blame] | 245 | reportTitleDaily="daily k6 tests performance review" |
| 246 | outputFileDaily="k6TestsPerformanceReview.html" |
| 247 | generateHtmlReport "$reportTitleDaily" "$outputFileDaily" "100" |
halil.cakal | 25ff8e8 | 2024-05-31 11:18:54 +0100 | [diff] [blame] | 248 | |
halil.cakal | b02a315 | 2024-09-17 15:53:09 +0100 | [diff] [blame] | 249 | reportTitleMonthly="monthly k6 tests performance review" |
| 250 | outputFileMonthly="k6TestsPerformanceReviewMonthly.html" |
| 251 | generateHtmlReport "$reportTitleMonthly" "$outputFileMonthly" "720" |