3 # Copyright 2024 Nordix Foundation.
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
9 # http://www.apache.org/licenses/LICENSE-2.0
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.
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
21 #set -o xtrace # Trace logging - disabled to avoid producing gigabytes of logs
23 #############################################################################################################################
24 ################################################ F U N C T I O N S ##########################################################
25 #############################################################################################################################
27 # Define the list of "Test Case + Condition" k6 test names to search for in the logs.
28 # The Test Case and Condition concatenated with \s* to represent zero or more whitespace characters.
29 # Later in this script can use Perl-compatible regular expressions (PCRE) can match to pattern with grep -P flag.
30 # The tests which measure http_req_failed excluded since nothing to plot if the rate is always zero.
32 "1-create-cmhandles.js\s*http_req_duration"
33 "2-wait-for-cmhandles-to-be-ready.js\s*iteration_duration"
34 "10-mixed-load-test.js\s*http_req_duration{scenario:id_search_module}"
35 "10-mixed-load-test.js\s*http_req_duration{scenario:passthrough_read}"
36 "10-mixed-load-test.js\s*http_req_duration{scenario:cm_search_module}"
37 "11-delete-cmhandles.js\s*http_req_duration"
40 JENKINS_JOB_URL="https://jenkins.nordix.org/job/onap-cps-performance-test-k6"
42 latestBuildToRecord=""
44 latestRecordedBuild=""
45 timestampOfLatestRecordedBuild=""
47 # Get last completed build number
48 # The number has not been plotted on the graphs yet
49 getLastCompletedBuildNumber() {
50 curl -s "${JENKINS_JOB_URL}/lastCompletedBuild/buildNumber"
53 # Get the last recorded build number from local workspace of the jenkins
54 # The number has already been plotted on the graphs
55 getLastRecordedBuildNumber() {
57 local file_name="1-create-cmhandles_http_req_duration.txt"
59 # Check if the file exists
60 if [ -f "$file_name" ]; then
61 # Get the last line from the file
62 local last_line=$(tail -n 1 "$file_name")
63 local left_side=$(echo "$last_line" | cut -d ',' -f 2)
70 # Get all builds numbers
71 getAllBuildNumbers() {
72 curl -s "${JENKINS_JOB_URL}/api/json?tree=allBuilds\[id\]" | jq -r '.allBuilds[].id' | sort -n
75 # Get the console text of k6 performance job
78 consoleURL="${JENKINS_JOB_URL}/${buildToRead}/consoleText"
79 consoleText=$(curl -s "$consoleURL")
82 # Replace special characters with underscore
83 # The return value will be used to create a file for each test combination
84 replace_special_characters() {
85 local pattern=".js\s*"
88 # Escape special characters in the pattern
89 local escaped_pattern=$(echo "$pattern" | sed 's/[\.*^$]/\\&/g')
91 # Replace the pattern with underscores using sed
92 local result=$(echo "$text" | sed "s/$escaped_pattern/_/g")
97 # Get and record the latest k6-job-results with the build number for all tests
98 getAndRecordPerformanceJobResultForBuild() {
100 getConsoleText "$buildNumber"
101 # Loop through each test names
102 for k6_test_name in "${k6_test_names[@]}"; do
103 getAndRecordDataResults "$consoleText" "$k6_test_name" "$(replace_special_characters "$k6_test_name").txt" "$buildNumber"
107 # Get and record the latest k6-job-results for a single test
108 getAndRecordDataResults() {
118 # Find the text combination (Test Case + Condition) in the console log
119 if matched_line=$(echo "$consoleText" | grep -P "${patternToMatch}"); then
120 # Find the first-occurred number in the line matched (limit)
121 limit_value=$(echo "$matched_line" | awk '{for(i=1;i<=NF;i++) if($i ~ /^[0-9]+$/) {print $i; exit}}')
122 # Find the second-occurred number in the line matched (actual)
123 actual=$(echo "$matched_line" | awk '{count=0; for(i=1; i<=NF; i++) if($i ~ /^[0-9]+$/) {count++; if(count==2) {print $i; exit}}}')
126 # Add a new entry which is a combination of limit, build-number, and actual into related file
128 lastLine=$(tail -n 1 "$dataFile")
129 newLine="$limit_value,$buildNumber,$actual"
130 if [ -z "$actual" ]; then
131 # No entry found for this build, the default added as zero
132 echo "0,$buildNumber,0" >>"$dataFile"
133 recordLatestRecordedBuild "$buildNumber"
134 elif [ "$newLine" == "$lastLine" ]; then
135 # Entry already exists
136 recordLatestRecordedBuild "$buildNumber"
138 # New entry added to the file
139 echo "$limit_value,$buildNumber,$actual" >>"$dataFile"
140 recordLatestRecordedBuild "$buildNumber"
144 # Format the date and time of the latest build
145 recordLatestRecordedBuild() {
146 latestBuildToRecord="$1"
147 timestampOfLatestRecordedBuild=$(curl -s "${JENKINS_JOB_URL}/${latestBuildToRecord}/api/json?tree=timestamp" | jq -r '.timestamp')
148 formattedTimestampOfLatestRecordedBuild=$(date -d "@$((timestampOfLatestRecordedBuild / 1000))" "+%B %e, %Y at %H:%M")
149 latestRecordedBuild=$latestBuildToRecord
152 # Plot the image (graph) in png format
157 # Read the first line of the file
158 first_line=$(head -n 1 "$dataFile")
159 # Set upper limit of the graphs to %20 to have more space above the plot
161 limit=$(echo "$first_line" | cut -d ',' -f 1)
162 limit=$(expr "$limit * $ten_percent" | bc)
164 # Create a temporary Gnuplot script
165 cat <<EOT >gnuplot_script.gp
166 set datafile separator ","
167 set terminal pngcairo size 1500,600
168 set output "${chartFileName}"
170 set ylabel "Limit (ms)"
171 set yrange [0 : ${limit} < *]
173 plot '$dataFile' using 2:3:xtic(sprintf("%d", column(2))) with linespoints title "measured", \
174 '$dataFile' using 2:1 with lines linestyle 2 title "limit"
177 # Run the temporary Gnuplot script
178 gnuplot gnuplot_script.gp
180 # Remove the temporary Gnuplot script
186 # use indirect expansion to get all elements of the array
187 categoryName=("${!1}")
190 cat <<EOT >"$outputFile"
194 <title>$reportTitle</title>
197 <h1 style="text-align: center;">$reportTitle</h1>
198 <h4>Last updated for performance job build no. $latestRecordedBuild on $formattedTimestampOfLatestRecordedBuild</h4>
199 <table align="center">
201 # Loop through the tests to generate the HTML rows which consists of the plot-image
202 for test_name_in_category in "${categoryName[@]}"; do
203 test_name_in_category="$(replace_special_characters "$test_name_in_category")"
204 cat <<EOF >>"$outputFile"
205 <tr> <!-- Row for $test_name_in_category -->
206 <td align="center" style="padding: 10px;">
207 <figcaption>"$test_name_in_category"</figcaption>
208 <img src="$test_name_in_category.png" width="750" height="300">
213 # Close the HTML file
214 cat <<EOT >>"$outputFile"
216 <p>The k6 performance tests runs every hour, providing performance metrics to the plots.</p>
217 <p>The k6 plots is being updated 31 minutes past every hour.</p>
218 <p>Successful performance tests job build adds new data, but even if a build fails, existing data is retained.</p>
224 #############################################################################################################################
225 ################################################ M A I N ####################################################################
226 #############################################################################################################################
228 # Install dependencies
229 sudo apt-get install -y bc gnuplot jq
231 # Download data from CPS performance Jenkins job
233 if [ -z "$(ls -A)" ]; then
234 # If workspace is empty, pull data from all previous performance job runs
235 for buildNumber in $(getAllBuildNumbers); do
236 getAndRecordPerformanceJobResultForBuild "$buildNumber"
239 # Append new data from latest jobs run
240 lastCompletedBuildNumber=$(getLastCompletedBuildNumber)
241 lastRecordedBuildNumber=$(getLastRecordedBuildNumber)
242 # Check if last completed build number is greater than last recorded build number
243 if [ "$lastCompletedBuildNumber" -gt "$lastRecordedBuildNumber" ]; then
244 for ((i = lastRecordedBuildNumber + 1; i <= lastCompletedBuildNumber; i++)); do
245 # Process the new builds which hasn't been recorded yet
246 getAndRecordPerformanceJobResultForBuild "$i"
249 echo "No new builds to process."
253 # Limit the plots to last 100 builds for each test
254 for k6_test_name in "${k6_test_names[@]}"; do
255 k6_file_name="$(replace_special_characters "$k6_test_name")"
256 tail -n 100 "$k6_file_name.txt" > file.tmp && mv file.tmp "$k6_file_name.txt"
259 # Plot image files in png format
260 for k6_test_name in "${k6_test_names[@]}"; do
261 k6_file_name="$(replace_special_characters "$k6_test_name")"
262 buildPlotImage "$k6_file_name.txt" "$k6_file_name.png"
266 buildHtmlReport k6_test_names[@] "k6 tests performance review" "k6TestsPerformanceReview.html"