Skip to content

Commit

Permalink
More WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewnicols committed Dec 20, 2024
1 parent 0ad7302 commit c0d752e
Show file tree
Hide file tree
Showing 6 changed files with 311 additions and 39 deletions.
88 changes: 64 additions & 24 deletions runner/main/jobtypes/performance/performance.sh
Original file line number Diff line number Diff line change
Expand Up @@ -151,28 +151,35 @@ function performance_generate_test_data() {

# Generate the test plan files and capture the output
local testplancmd
perfomance_testplan_generator_command testplancmd # By nameref.
performance_testplan_generator_command testplancmd # By nameref.
echo "Running: ${testplancmd[*]}"
testplanfiles=$(docker exec -t -u www-data "${WEBSERVER}" "${testplancmd[@]}")
testplanfiles=$(docker exec -i -t -u www-data "${WEBSERVER}" "${testplancmd[@]}")

# Display the captured output
echo "Captured Output:"
echo "${testplanfiles}"
echo "${SHAREDDIR}"

# Ensure the directory exists and is writable
mkdir -p "${SHAREDDIR}/planfiles"
mkdir -p "${SHAREDDIR}/logs"
mkdir -p "${SHAREDDIR}/results"
mkdir -p "${SHAREDDIR}/runs"

chmod -R 777 "${SHAREDDIR}"

# Extract URLs and download files to ${SHAREDDIR}
urls=$(echo "${testplanfiles}" | grep -oP 'http://[^ ]+')
for url in ${urls}; do
# Trim any whitespace or newline characters from the URL
url=$(echo "${url}" | tr -d '\r\n')
# Extract the filename from the URL
filename=$(basename "${url}")
echo "Downloading: ${url} to /shared/${filename}"
docker exec -it -u www-data "${WEBSERVER}" curl -o "/shared/${filename}" "${url}"
echo "Downloading: ${url} to ${SHAREDDIR}/${filename}"
docker exec -i -t -u www-data "${WEBSERVER}" curl -o "/shared/planfiles/${filename}" "${url}"
done
}

#function performance_datacmd() {
#
#}

# Performance job type run.
function performance_run() {
echo
Expand All @@ -183,14 +190,43 @@ function performance_run() {
fi
echo "============================================================================"

datestring=`date '+%Y%m%d%H%M'`
# Get the plan file name.
testplanfile=`ls "${SHAREDDIR}"/planfiles/*.jmx | head -1 | sed "s@${SHAREDDIR}@/shared@"`
testusersfile=`ls "${SHAREDDIR}"/planfiles/*.csv | head -1 | sed "s@${SHAREDDIR}@/shared@"`
group="${MOODLE_BRANCH}"
description="${MOODLE_BRANCH}"
siteversion=""
sitebranch="${MOODLE_BRANCH}"
sitecommit="${MOODLE_BRANCH}"
runoutput="${SHAREDDIR}/results/$datestring.output"

# Calculate the command to run. The function will return the command in the passed array.
local cmd=
performance_main_command cmd # By nameref.
local jmeterruncmd=
performance_main_command jmeterruncmd # By nameref.

echo "Running: ${cmd[*]}"
echo "Running: ${jmeterruncmd[*]}"
echo ">>> Performance run at $(date) <<<"
docker exec -t "${JMETER}" "${cmd[@]}"
local dockerrunargs=
docker-jmeter_run_args dockerrunargs # By nameref

echo "${dockerrunargs[@]}"
echo docker run ${dockerrunargs[@]} -- ${jmeterruncmd[@]}
docker run "${dockerrunargs[@]}" ${jmeterruncmd[@]} > "${runoutput}"
EXITCODE=$?
echo "============================================================================"
echo "============================================================================"
echo "============================================================================"
read

# Grep the logs looking for errors and warnings.
for errorkey in ERROR WARN; do
# Also checking that the errorkey is the log entry type.
if grep $errorkey "${SHAREDDIR}/logs/jmeter.log" | awk '{print $3}' | grep -q $errorkey ; then
echo "Error: \"$errorkey\" found in jmeter logs, read $logfile to see the full trace."
EXITCODE=1
fi
done

echo "============================================================================"
echo "== Date: $(date)"
Expand All @@ -213,10 +249,19 @@ function performance_teardown() {
function performance_main_command() {
local -n _cmd=$1 # Return by nameref.

# Uses the test plan specified in the CLI call.
logfile="logs/jmeter.$datestring.log"

includelogs=1

# Include logs string.
includelogsstr="-Jincludelogs=$includelogs"
samplerinitstr="-Jbeanshell.listener.init=recorderfunctions.bsf"


# TODO: Get all of these values from somewhere?
# Build the complete perf command for the run.
_cmd=(
jmeter \
_cmd=(
-n \
-j "/shared/logs/jmeter.log" \
-t "$testplanfile" \
Expand All @@ -226,15 +271,10 @@ function performance_main_command() {
-Jsiteversion="$siteversion" \
-Jsitebranch="$sitebranch" \
-Jsitecommit="$sitecommit" \
$samplerinitstr \
$includelogsstr \
$users \
$loops \
$rampup \
$throughput \
> $runoutput || \
throw_error $jmetererrormsg
)
-Jusers=50 -Jloops=1 -Jrampup=10 -Jthroughput=1 \
$samplerinitstr $includelogsstr
)
#$includelogsstr $users $loops $rampup $throughput
}

function perfomance_testsite_generator_command() {
Expand All @@ -257,7 +297,7 @@ function performance_testplan_generator_command() {
_cmd=(
php admin/tool/generator/cli/maketestplan.php \
--size="${SITESIZE}" \
--shortname="${COURSENAME}" \
--shortname="testcourse_3" \
--bypasscheck
)
}
21 changes: 14 additions & 7 deletions runner/main/modules/docker-jmeter/docker-jmeter.sh
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,22 @@ function docker-jmeter_setup() {
echo "============================================================================"

# Start the jmeter server
docker run \
--detach \
--name "${JMETER}" \
justb4/jmeter
docker pull alpine/jmeter:2.11

echo "JMETER: URL: ${JMETERTESTURL}"
echo "JMETER logs:"
docker logs "${JMETER}"
cp -rf "${BASEDIR}"/modules/docker-jmeter/libraries/* "${SHAREDDIR}"

echo "============================================================================"
echo ">>> stopsection <<<"
}

function docker-jmeter_run_args() {
local -n _cmd=$1 # Return by nameref.
# Start the jmeter server
_cmd=(
--detach \
--name "${JMETER}" \
-v "${SHAREDDIR}:/shared" \
-w /shared \
alpine/jmeter:2.11
)
}
193 changes: 193 additions & 0 deletions runner/main/modules/docker-jmeter/libraries/recorder.bsf
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
import java.io.*;
import java.util.regex.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.util.JMeterUtils; // http://jakarta.apache.org/jmeter/api/org/apache/jmeter/util/JMeterUtils.html
import org.apache.jmeter.threads.JMeterContext; // http://jakarta.apache.org/jmeter/api/org/apache/jmeter/threads/JMeterContext.html
import org.apache.jmeter.samplers.SampleResult; // http://jakarta.apache.org/jmeter/api/org/apache/jmeter/samplers/SampleResult.html

MoodleResult(JMeterContext ctx) {

Integer thread = ctx.getThreadNum();
SampleResult result = ctx.getPreviousResult();

String html = result.getResponseDataAsString();

String dbreads = "0";
Pattern pdbreads = Pattern.compile(".*?DB reads/writes: (\\d+)/\\d+.*", Pattern.UNIX_LINES | Pattern.DOTALL);
Matcher mdbreads = pdbreads.matcher(html);
if (mdbreads.matches()) {
dbreads = mdbreads.group(1);
}

String dbwritesstr = "0";
Pattern pdbwrites = Pattern.compile(".*?DB reads/writes: \\d+/(\\d+).*", Pattern.UNIX_LINES | Pattern.DOTALL);
Matcher mdbwrites = pdbwrites.matcher(html);
if (mdbwrites.matches()) {
dbwritesstr = mdbwrites.group(1);
}
Integer dbwrites = Integer.parseInt(dbwritesstr);

// Adding logs if required.
if (props.get("includelogs") != null) {
Pattern plogwrites = Pattern.compile(".*?Log DB writes (\\d+).*", Pattern.UNIX_LINES | Pattern.DOTALL);
Matcher mlogwrites = plogwrites.matcher(html);
if (mlogwrites.matches()) {
dbwrites = dbwrites + Integer.parseInt(mlogwrites.group(1));
}
}

String dbquerytime = "0";
Pattern pdbquerytime = Pattern.compile(".*?DB queries time: (\\d+(\\.\\d+)?) secs.*", Pattern.UNIX_LINES | Pattern.DOTALL);
Matcher mdbquerytime = pdbquerytime.matcher(html);
if (mdbquerytime.matches()) {
dbquerytime = mdbquerytime.group(1);
}

String memoryused = "0";
Pattern pmemoryused = Pattern.compile(".*?RAM: (\\d+(\\.\\d+)?)[^M]*MB.*", Pattern.UNIX_LINES | Pattern.DOTALL);
Matcher mmemoryused = pmemoryused.matcher(html);
if (mmemoryused.matches()) {
memoryused = mmemoryused.group(1);
}

String filesincluded = "0";
Pattern pfilesincluded = Pattern.compile(".*?Included (\\d+) files.*", Pattern.UNIX_LINES | Pattern.DOTALL);
Matcher mfilesincluded = pfilesincluded.matcher(html);
if (mfilesincluded.matches()) {
filesincluded = mfilesincluded.group(1);
}

String serverload = "0";
Pattern pserverload = Pattern.compile(".*?Load average: (\\d+(\\.\\d+)?).*", Pattern.UNIX_LINES | Pattern.DOTALL);
Matcher mserverload = pserverload.matcher(html);
if (mserverload.matches()) {
serverload = mserverload.group(1);
}

String sessionsize = "0";
Pattern psessionsize = Pattern.compile(".*?Session[^:]*: (\\d+(\\.\\d+)? ?[a-zA-Z]*).*", Pattern.UNIX_LINES | Pattern.DOTALL);
Matcher msessionsize = psessionsize.matcher(html);
if (msessionsize.matches()) {
sessionsize = msessionsize.group(1);
}

String timeused = "0";
Pattern ptimeused = Pattern.compile(".*?\"timeused[^\"]*\">(\\d+(\\.\\d+)?) secs.*", Pattern.UNIX_LINES | Pattern.DOTALL);
Matcher mtimeused = ptimeused.matcher(html);
if (mtimeused.matches()) {
timeused = mtimeused.group(1);
}

// Actual information collected about the sample by jmeter
String username = vars.get("username");
String name = StringUtils.rightPad(result.getSampleLabel(), 30);
String url = result.getUrlAsString();
Integer bytes = result.getBytes();
Long time = result.getTime();
Long latency = result.getLatency();
Long starttime = result.getStartTime();
String status = result.getResponseCode();

headerToString() {
String str = "status | thread | ";
str += StringUtils.rightPad("user", 10) + " | ";
str += StringUtils.rightPad("name", 30) + " | db-r | db-w | ";
str += StringUtils.rightPad("dbquerytime", 8) + " | ";
str += StringUtils.rightPad("memory", 8) + " | ";
str += StringUtils.rightPad("files", 6) + " | ";
str += StringUtils.rightPad("load", 6) + " |";
return str;
}

toString() {
String str = StringUtils.rightPad(status, 6) + " | ";
str += StringUtils.rightPad(Integer.toString(thread), 6) + " | ";
str += StringUtils.rightPad(username, 10) + " | ";
str += StringUtils.rightPad(name, 30) + " | ";
str += StringUtils.rightPad(dbreads, 4) + " | ";
str += StringUtils.rightPad(Integer.toString(dbwrites), 4) + " | ";
str += StringUtils.rightPad(dbquerytime, 8) + " | ";
str += StringUtils.rightPad(memoryused, 8) + " | ";
str += StringUtils.rightPad(filesincluded, 6) + " | ";
str += StringUtils.rightPad(serverload, 6) + " | ";
str += url;
return str;
}

toPHP() {

int bytesPos = sessionsize.indexOf(" bytes");
int kbsPos = sessionsize.indexOf("KB");
// Convert the size to KB and strip out the measure.
if (bytesPos != -1) {
sessionsize = "0." + sessionsize.substring(0, bytesPos);
} else if (kbsPos != -1) {
sessionsize = sessionsize.substring(0, kbsPos);
}

String php = "$results["+thread+"][] = array(\n";
php += " 'thread'=>"+thread+",\n"; // Int
php += " 'starttime'=>"+starttime+",\n"; // Long
php += " 'dbreads'=>"+Integer.parseInt(dbreads)+",\n"; // String => Int
php += " 'dbwrites'=>"+dbwrites+",\n";
php += " 'dbquerytime'=>"+dbquerytime+",\n";
php += " 'memoryused'=>'"+memoryused+"',\n";
php += " 'filesincluded'=>'"+filesincluded+"',\n";
php += " 'serverload'=>'"+serverload+"',\n";
php += " 'sessionsize'=>'"+sessionsize+"',\n";
php += " 'timeused'=>'"+timeused+"',\n";
php += " 'name'=>'"+name+"',\n";
php += " 'url'=>'"+url+"',\n";
php += " 'bytes'=>'"+bytes+"',\n";
php += " 'time'=>'"+time+"',\n";
php += " 'latency'=>'"+latency+"',\n";
php += ");\n";
return php;
}

return this;
}

EscapeQuotes(String text) {
return text.replace("'", "\\'");
}

Runnable mr = MoodleResult(ctx);

// Get the file (it is created in testStarted).
String filenamepath = "runs/tmpfilename.php";

// We add the run info when starting the first thread
if (JMeterUtils.getProperty("headerprinted") == null) {

// Output headers.
JMeterUtils.setProperty("headerprinted", "1");
print(mr.headerToString());

FileWriter fstream = new FileWriter(filenamepath, true);
BufferedWriter out = new BufferedWriter(fstream);
out.write("$host = '"+vars.get("host")+"';\n");
out.write("$sitepath = '"+vars.get("sitepath")+"';\n");
out.write("$group = '"+EscapeQuotes(props.get("group"))+"';\n");
out.write("$rundesc = '"+EscapeQuotes(props.get("desc"))+"';\n");
out.write("$users = '"+vars.get("users")+"';\n");
out.write("$loopcount = '"+vars.get("loops")+"';\n");
out.write("$rampup = '"+vars.get("rampup")+"';\n");
out.write("$throughput = '"+vars.get("throughput")+"';\n");
out.write("$size = '"+vars.get("size")+"';\n");
out.write("$baseversion = '"+vars.get("moodleversion")+"';\n");
out.write("$siteversion = '"+EscapeQuotes(props.get("siteversion"))+"';\n");
out.write("$sitebranch = '"+EscapeQuotes(props.get("sitebranch"))+"';\n");
out.write("$sitecommit = '"+EscapeQuotes(props.get("sitecommit"))+"';\n");
out.close();

// Send the run timestamp to set it as run filename.
props.put("filepath", "runs/" + vars.get("runtimestamp") + ".php");
}

FileWriter fstream = new FileWriter(filenamepath, true);
BufferedWriter out = new BufferedWriter(fstream);
out.write(mr.toPHP());
out.close();

print(mr.toString());
20 changes: 20 additions & 0 deletions runner/main/modules/docker-jmeter/libraries/recorderfunctions.bsf
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import java.io.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.util.JMeterUtils; // http://jakarta.apache.org/jmeter/api/org/apache/jmeter/util/JMeterUtils.html


testStarted(){
String filenamepath = "runs/tmpfilename.php";
FileWriter fstream = new FileWriter(filenamepath, true);
BufferedWriter out = new BufferedWriter(fstream);
out.write("<?php\n");
out.close();
}

testEnded(){
File from = new File("runs/tmpfilename.php");
File to = new File(JMeterUtils.getProperty("filepath"));
from.renameTo(to);
to.setReadable(true, false);
to.setWritable(true, false);
}
Loading

0 comments on commit c0d752e

Please sign in to comment.