02/11/2018 - Automating the Automator: Using LoadRunner to Script Against CA Workload


CA Workload, previously known as Autosys, is an enterprise tool used for scheduling tasks based on timings and/or conditions. Sometimes as performance engineers we need to utilize external systems to drive load or be a part of our performance tests. I decided to investigate writing a simple LoadRunner script to automate the process of starting a couple of jobs for me. The version of CA Workload I am using, is using unsecure HTTP with LDAP authentication. However, the password is not being passed as clear text. If you look at the login POST request we can see that there is a "SECUREPASSWORD" feature being used. What wasn't possible in a browser 10 years ago is now possible with all of the improvements to Javascript engines; CA Workload is using RSA to encrypt the user's password with 1024bit public key.

Knowing this information we are left with a couple choices of how to implement this encryption correlation in the script. We could implement RSA in an external library and use the HTTP/Web protocol with the C language, but I wanted to try and make use of LoadRunner's new Javascript engine that is available in HTTP/Web protocol. I was hoping the RSA.js file provided by the web site would be plug-in-play, but LoadRunner wasn't able to access the functions through an anonymous function and because the functions were currently being exported through the global Window object. After a few tweaks I was able to get my LoadRunner script to call the RSA.Encrypt() function.

I wanted to verify I had a working implementation after implementing RSA in my script, but because this implementation of RSA is using padding, you can't compare a recorded value against a executed value. CA Workload only provides the modulus and exponent of the public key, so without the private key we can't validate our implementation. To get around this limitation, I generated my own private/public key pair and validated the script was working as expected. Once this was complete, I was able to correlate the rest of my script as per any other JFaces server side application. In conclusion, utilizing LoadRunner's Javascript engine made easy work of scripting against CA Workload.


11/30/2017 - Integrating LoadRunner's JS Engine with RabbitMQ (POC)


alt text

3/10/2016 - Brotli DFE for LoadRunner


    DFE is the acronym for Data Format Exchange. DFE's in LoadRunner allow you to modify responses and requests which transforms them in to another format. LoadRunner comes with some built-in DFE's, such as JSON to XML, this DFE will take JSON data in the request or response and transform it to XML, which with helper functions easily allows parsing of the data.

    Brotli, created by Google, is a generic-purpose lossless compression algorithm that compresses data using a combination of a modern variant of the LZ77 algorithm, Huffman coding and 2nd order context modeling, with a compression ratio comparable to the best currently available general-purpose compression methods. It is similar in speed with deflate but offers more dense compression.

    The Brotli DFE compresses/decompresses HTTP requests and responses allowing you to generate scripts when the HTTP server uses this data encoding. Please contact us if you are interested in purchasing this DFE.



03/03/2016 - Deploy LoadRunner VTS on Linux


Prerequisites
clang 3.7
[email protected]
node v0.10.17
LoadRunner v12.50 Community Edition
lessmsi

01.  Download node-leveldb (https://github.com/my8bird/node-leveldb) from the master branch.  
02.  Download Node source code for build v0.10.17 (https://nodejs.org/download/release/v0.10.17/node-v0.10.17.tar.gz)
03.  Download the Node binaries for Linux x64 (https://nodejs.org/download/release/v0.10.17/node-v0.10.17-linux-x64.tar.gz)
04.  Extract the 3 packages above, the directories will be referred to as $node-leveldb_src, $node_v0.10.17_src, $node_v0.10.17
05.  Export NODE_PATH:  export NODE_PATH=$node_v0.10.17/lib/node_modules
06.  Export the linker and compiler to clang:  export CXX=clang++; export LINK=clang++
05.  Change to the $node-leveldb_src directory
06.  Execute $node_v0.10.17/lib/node_modules/npm/bin/node-gyp-bin/node-gyp rebuild --nodedir=$node-leveldb_src
07.  Goto: <LRINSTALL_FILES>\DVD\Additional Components\Virtual Table Server\ and extract SetupVTS.exe with WinZip.
08.  After extracting SetupVTS, use lessmsi (http://lessmsi.activescott.com/) to extract .\SetupVTS\HP_VTS_x64.msi
09.  After extracting HP_VTS_x64.msi to a folder, copy .\SourceDir\HP\VTS\web\* to your linux server, referred to as $vts below
10.  Copy $node-leveldb_src/build/Release/leveldb.node to $vts/node_modules/leveldb/lib
11.  Replace $vts/main.js and $vts/log.js with these modified versions: main.js log.js
12.  Create tmp and data directories in $vts:  mkdir $vts/data; mkdir $vts/tmp
13.  Modify $vts/configure.json dbPath and logger.transports.file.path values to $vts/data and $vts/tmp
14.  Start the server:  cd $vts; $node_v0.10.17/bin/node main.js



05/28/2015 - Atlassian Bamboo ALM Plugin


After seeing some interest from a recent client, and LinkedIn, for using Bamboo as their CI tool we decided to put together an ALM plugin that allows users to run tests from within Bamboo. This plugin will allow users to execute automated test scripts residing in ALM. We are releasing the binary for the plugin today and we'll be posting a link to the git repoitory in the next few weeks.

Downloads

Dependencies

Limitations

01/23/2015 - Using a Rust DLL in your VuGen Scripts


On occasion the need arises for a performance engineer to expand their scripts with an external function. This function usually resides in an external DLL which we need to reference. For the most part this external function is likely to be written in C/C++. But what if we decide we want to use a different language, like Rust. In this post, I will demonstrate how to construt a small Rust function that will intern access one of LoadRunner's functions. Once we compile our new function we will be able to access it from a VuGen script.

First, let's look at the Rust function. This code was written to compile against a recent nightly build [rustc 1.0.0-nightly (29bd9a06e 2015-01-20 23:03:09 +0000)]. The code isn't documented and I won't be stepping through line by line to explain it. The purpose of this post is just to demonstrate the feasibility. To ease our use of the Win32 API, I am making use of a 3rd party Rust library.

#![crate_type = "dylib"]

#[allow(unstable)]
extern crate libc;
extern crate "kernel32-sys" as kernel32;
extern crate winapi;

use winapi::*;
use kernel32::*;
use std::ffi::CString;

type LROUTPUTMESSAGE = extern "C" fn(LPCSTR, ...) -> i32;

#[allow(unused_variables)]
#[allow(non_snake_case)]
#[no_mangle] 
pub extern "stdcall" fn DllMain(module: u32, reason_for_call: u32, reserved: *mut libc::c_void) -> BOOL 
{ 
    return 1;
}

#[no_mangle] 
#[allow(non_snake_case,unstable)]
pub extern "stdcall" fn rustTest() -> u32
{
    let funcstr = CString::from_slice(b"lr_output_message");
    let dll = CString::from_slice(b"lrun50.dll");

    let handle = unsafe { GetModuleHandleA(dll.as_ptr())};

    if handle == 0 as *mut winapi::HINSTANCE__ 
    {
        return 0;
    }

    let lr_output_message: LROUTPUTMESSAGE = unsafe {  std::mem::transmute(GetProcAddress(handle, funcstr.as_ptr())) };
    if unsafe {::std::mem::transmute::<_,*mut DWORD >(lr_output_message).is_null()} 
    {   
        return 0;
    }

    lr_output_message("0x%.08x - %s\0".as_ptr() as LPCSTR, 0xDEADBEEFu32, "An output message from Rust\0".as_ptr() as LPCSTR);

    return 1;
}


Now that we have our Rust code, let's compile it to a DLL. Because we specifid a 'crate type' above, we don't have to specify any special command line arguments for the compiler to know it's a DLL; as would be the case with C.

C:\Temp\>rustc test.rs -L . -o test.dll


Once we've compiled our code, we can now use it in our script. Below is a small example of loading our DLL and calling our external function.

vuser_init()
{

    lr_load_dll("c:\\temp\\test.dll");

    lr_output_message("retval = %d",rustTest());

    return 0;
}


And the corresponding output to script execution...

Virtual User Script started at : 1/22/2015 3:36:00 PM
Starting action vuser_init.
Web Turbo Replay of LoadRunner 12.0.0 for Windows 8.1; build 2739 (Nov 30 2014 23:13:05)    [MsgId: MMSG-27143]
Run mode: HTML      [MsgId: MMSG-26993]
Run-Time Settings file: "C:\Users\Developer\Documents\VuGen\Scripts\WebHttpHtml10\\default.cfg"     [MsgId: MMSG-27141]
vuser_init.c(6): 0xdeadbeef - An output message from Rust
vuser_init.c(6): retval = 1
Ending action vuser_init.
Running Vuser...


In conclustion, this is a simple proof of concept. No more, no less. Keep in mind that Rust v1.0 has not been finalized yet, which means this code could break in future nightly builds/final build. Another topic to note is the size of the Rust DLL, which compiled as is, is 2.6MB. I have not investigated if it is possible to slim some bloat off of the file. As is, this likely wouldn't be feasible to run this in a 'Per Process' test. Which means that in a 'Per Thread' VUser test, we would need to be aware of any thread-safety issues.