Our legacy site: www.webwob.com   

The All new BASH approach to LoadRunner CI/CD

This page will outline the top level of my all-new Jenkins job for LoadRunner CI, using pure BASH scripts (without plugins needed). This approach is of course then portable to other runtime sytems, even pure cygwin scripting. Cygwin because LoadRunner does run on Windows in this setup.

     LoadRunner CI/CD      *_LR_CI_*      LR_CI_+      Email_out      Email_in      Cookies 1      TUTORIAL      sprintf      debug      VUgen      Kill -9      disk space      AWS S3      dig (DNS++)      substitute      curl with timings      LR_Jenkins_graphs     
    

Configure a new Jenkins V2 job to make use of our BASH scripts

I will intersperse screenshots in this page to make things clearer and print code where required


Jenkins Job high level input screens

Screen shots for: Job description, keeping results, setting parameters and git integration

Job name and log rotation:
This is actually my template job, we use it when creating new jobs, copy it over and stop and start Jenkins to pick up the change.
        
Screenshot         
        
Scenario:
Set all the variables below as shown here. WATCH OUT for the types of parameters used. This first one is used in the BASH scripts to check, edit and update quite a few things before running the load test (e.g. injectors used, logging settings, results location)
        
Screenshot         
        
Injectors:
Define this IF you need to change the injectors to use previously instigated ones. This is used in the BASH scripts to define the injectors in the scenario if you have to use a fixed list of machines.
        
Screenshot         
        
Injectors Method:
This defines how injectors are setup for the load test. Create new ones in various ways, use the fixed list above or make no changes to the current scenario.
        
Screenshot         
        
Where to run this script:
This setting is important mainly because of the new way in which we now run Jenkins: (LoadRunner CI/CD). We deliberatly run Jenkins interactively (not as a service), so that we can interact with any programs that are triggered during a job, such as the LR controller for example. But this requires us to run directly on this box and not across slaves (as used in our other methods).

Jenkins is started as a shortcut (by the admin user):
        java -jar "C:\Program Files (x86)\Jenkins\jenkins.war" --httpPort=80
and worth noting that we have a few system variables set up as well:
        JENKINS_HOME -- C:\loadrunner\Jenkins
        LG_PATH -- C:\loadrunner\program\HP\loadrunner\
        ANALYSIS_PATH -- C:\loadrunner\program\HP\loadrunner\
        LR_PATH -- C:\loadrunner\program\HP\loadrunner\
        VUGEN_PATH -- C:\loadrunner\program\HP\loadrunner\
        
So we need to set this restriction to running on the current box: Screenshot         
        
Get our scripts and scenarios out of source code management
We're using github ( desktop.github.com ) for source code management. In fact we use it for the LoadRunner scripts and scenarios AND for the BASH scripts used here. This is brilliant. It makes all our projects portable 'cos github is easily accessible from the cloud etc. We use the Jenkins git plugins that do work well, another plus of Jenkins for us.
        
Screenshot         
        
MAIN SCRIPT:
We use one main shell for the calling routine:
Screenshot         
which contains the following code:

[ The top of this code is used to counteract a problem with git and windows: git automatically sets the end of line characters to '\r\n', assuming we want that 'cos we're on Windows! But we don't, cos we're using BASH n cygwin. So the first step is to resolve this issue with our own BASH code at the top of the Jenkins job, before calling the shell scripts in turn. ]

================================================================
================================================================

#printenv

if [ ! "${new_injector_method}" ]; then
echo "\"new_injector_method\" empty. CANNOT carry on. Please set a value."
    #set a flag for the postbuild script to mark a failure
    echo "last jtl file sets value to fail"
    exit 1
fi

echo hide next bit from output for convenience
set +x

export PATH="/cygdrive/c/cygwin/bin/:$PATH"
echo path now is - two -
echo "${PATH}"

#/cygdrive/c/Temp/all4web_version.txt
#cd /cygdrive/c/Temp/
echo which find
which find

###. /cygdrive/Z/temp/end_of_line.sh

find /cygdrive/z/github/PerformanceTesting/jenkins/ -name "*.sh" | wc -l
find /cygdrive/z/github/PerformanceTesting/jenkins/ -name "*.sh" > sh1.txt
#cat sh1.txt

sh_now=$(cat sh1.txt)

sh_now_count=$( echo ${sh_now} | awk 'BEGIN {OFS="\n"} {FS=" "} { print NF }')


for ((i=1;i<=sh_now_count;i++)); do

    val=$(echo ${sh_now} | awk -v i="$i" 'BEGIN {OFS="\n"} {FS=" "} { print $i }')

    echo shell $i currently is $val
    
    old_sh[$i]="${val}"

echo old_sh[$i] is ${old_sh[$i]}
###sed --in-place s#/\r/\n#/\n#g ${old_sh[$i]}
cat ${old_sh[$i]} | tr -d '\r' > temp_tr; rm -f ${old_sh[$i]}; cp temp_tr ${old_sh[$i]}

    echo ____

done

set -x
echo END OF - hide next bit from output for convenience


# Tidy up machine and copy over analysis templates from git etc.
. /cygdrive/Z/GitHub/PerformanceTesting/jenkins/LR_CI_tidy_up.sh

#Set analysis default timings (see notes just below)
. /cygdrive/Z/GitHub/PerformanceTesting/jenkins/analysis_reset.sh

#to change the default timings for the standard analysis, use this instead:
#start_time=1 end_time=3 . /cygdrive/Z/GitHub/PerformanceTesting/jenkins/analysis.sh


. /cygdrive/Z/GitHub/PerformanceTesting/jenkins/LR_CI_MAIN_jenkins_caller.sh

################################################
#
# TEMPLATE NAMES BELOW CAN BE:
# Perc90SLA, Perc90SLA1, Perc90SLA2, Perc90SLA4, Perc90SLA5 OR
# Perc95SLA1, Perc95SLA2, Perc95SLA4, Perc95SLA5, Perc95SLA6
#
# DO NOT re-use templates in the same test run i.e below, in this script
#
# start_time and end_time are in minutes FROM the START of the scenario
#
# use_times=0 means DO NOT use these times, i.e. include the whole scenario
#
# example:
#
# use_times=1 start_time=1 end_time=3 template="Perc90SLA3" . /cygdrive/Z/GitHub/PerformanceTesting/jenkins/analyse.sh
#
################################################

use_times=1 start_time=1 end_time=3 template="Perc90SLA3" . /cygdrive/Z/GitHub/PerformanceTesting/jenkins/analyse.sh

use_times=0 start_time=1 end_time=3 template="Perc95SLA3" . /cygdrive/Z/GitHub/PerformanceTesting/jenkins/analyse.sh

use_times=1 start_time=2 end_time=4 template="Perc95SLA4" . /cygdrive/Z/GitHub/PerformanceTesting/jenkins/analyse.sh

================================================================
================================================================

Post Job control Pass/Fail:
There are a few post build steps to follow on. This one was added to control the CI pass/Fail criteria, so it only considers real Pass/Fails, based on SLAs, set in the LoadRunner scenario. The source code is given below the image for ease of use:
        
Screenshot         
        
Use this code:
  evaluate(new File("Z:\\github\\PerformanceTesting\\jenkins\\postbuild.groovy"));

OR

  evaluate(new File("Z:\\github\\PerformanceTesting\\jenkins\\postbuild_no_slas.groovy"));

And then inside postbuild.groovy:

//NEW approach now:
manager.listener.logger.println("starting Groovy post build")
if(manager.logContains(".*last jtl file sets value to fail.*"))
{
    manager.listener.logger.println("Groovy setting build to FAILED")
    manager.build.@result = hudson.model.Result.FAILURE
}
else
{
    manager.listener.logger.println("Groovy setting build to PASSED")
    manager.build.@result = hudson.model.Result.SUCCESS
}

if(manager.logContains(".*Controller IS IN USE.*"))
{
    manager.listener.logger.println("Groovy setting build to FAILED")
    manager.listener.logger.println("Controller IS IN USE")
    manager.build.@result = hudson.model.Result.FAILURE
}

if(manager.logContains(".*Fiddler IS IN USE.*"))
{
    manager.listener.logger.println("Groovy setting build to FAILED")
    manager.listener.logger.println("Fiddler IS IN USE")
    manager.build.@result = hudson.model.Result.FAILURE
}

if(manager.logContains(".*Analysis IS IN USE.*"))
{
    manager.listener.logger.println("Groovy setting build to FAILED")
    manager.listener.logger.println("Analysis IS IN USE")
    manager.build.@result = hudson.model.Result.FAILURE
}

if(manager.logContains(".*Overall SLA status: FAIL.*"))
{
    manager.listener.logger.println("Groovy setting build to FAILED")
    manager.listener.logger.println("Overall SLA status: FAIL")
    manager.build.@result = hudson.model.Result.FAILURE
}
  
manager.listener.logger.println("finished Groovy post build")



Post Job - Archive everything:
This post build section archives everything so reports etc are available per build to the user, with simple click throughs from the Jenkins front end (code snippet given below):
        
Screenshot         
code (all on one line without spaces):
*.*,htmlReport/**/*,Report85/**/*,Report90/**/*,Report95/**/*,
Session85/**/*,Session90/**/*,Session95/**/*,res*/**/*
        

Post Job - Create Jenkins type trend graphs:
Here we use the Jmeter plugin (called 'Performance plugin') to create JMeter style trend graphs ( See JMeter Jenkins for a graphing example):
        
Screenshot