If you need to run a Unix command on a remote server with a LoadRunner script (running on Windows), then there is a neat program called plink that you will find very useful.
If you use any variety of Unix, you will probably already by using PuTTY; well plink is is distributed with PuTTY, and allows you to run remote Unix commands from your DOS prompt.
In this example, I have used it to collect disk I/O metrics from a remote Solaris server.
You may need to refer to some background material for the different components that are used for this example.
- Solaris Tips
- AWK
- Running command-line programs from LoadRunner
To capture the %busy metric for each disk, I can run iostat -xc. This gives the following output:
1 2 3 4 5 6 7 8 9 10 | extended device statistics cpu device r/s w/s kr/s kw/s wait actv svc_t %w %b us sy wt id md0 0.5 0.6 8.5 0.8 0.0 0.0 19.9 0 0 1 1 0 98 md1 6.3 6.8 50.3 54.5 0.0 0.3 24.0 0 0 md3 0.2 0.7 12.7 12.6 0.0 0.0 11.0 0 1 md5 0.0 0.0 2.7 0.3 0.0 0.0 8.0 0 0 md6 0.0 0.0 0.0 0.0 0.0 0.0 3.6 0 0 md10 0.3 0.6 4.2 0.8 0.0 0.0 21.3 0 0 md11 3.1 6.8 25.1 54.5 0.0 0.3 25.6 0 0 md13 0.1 0.7 6.4 12.6 0.0 0.0 8.7 0 0 |
Using AWK to just grab the device name and the %b column can be done by running iostat -xc | awk ‘NR >2 {print $1, $9}’, which gives:
1 2 3 4 5 6 7 8 | md0 0 md1 0 md3 1 md5 0 md6 0 md10 0 md11 0 md13 0 |
Putting this in a LoadRunner script that will save the %b value for each device using lr_user_data_point() looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | /*
* This script retrieves Disk IO stats from a Solaris server and plots them in the Controller using lr_user_data_point().
*/
#define BUFFER_SIZE 10240 // 10 KB
extern char* strtok(char *token, const char *delimiter); // Explicit declaration required for functions that do not return an int.
Action()
{
long fp; // file/stream pointer
int count; // number of characters that have been read from the stream.
char buffer[BUFFER_SIZE]; // allocate memory for the output of the command.
char * row_token; // for each row in the output of the command.
char field_name[100]; // for the first column in the row.
int field_value; // for the second column in the row.
char lrudp_name[100]; // buffer to hold the LR user data point name.
int rc; // return code for sscanf.
lr_start_transaction("Monitor Disk IO");
// The command we want to run is: plink -ssh -l username -pw password hostname command
lr_save_string("stuart", "UserName");
lr_save_string("asdf7890", "Password");
lr_save_string("sdb526.sysdomain.local", "Server");
lr_save_string("iostat -xc | awk 'NR >2 {print $1, $10}'", "Command"); // run iostat, and retrieve the "device" and "%b" column from the output.
lr_save_string(lr_eval_string("C:\\Progra~1\\PuTTY\\plink -ssh -l {UserName} -pw {Password} {Server} \"{Command}\" 2>&1 0< nul:"), "CompleteCommand"); // Note: plink refuses to work without stdin redirected
fp = popen(lr_eval_string("{CompleteCommand}"), "r");
if (fp == NULL) {
lr_error_message("Error opening stream.");
return -1;
}
count = fread(buffer, sizeof(char), BUFFER_SIZE, fp); // read up to 10KB
if (feof(fp) == 0) {
lr_error_message("Did not reach the end of the input stream when reading. Try increasing BUFFER_SIZE.");
return -1;
}
if (ferror(fp)) {
lr_error_message ("I/O error during read.");
return -1;
}
if (count == 0) {
lr_error_message("Something went wrong when running command. No output returned.");
return -1;
}
buffer[count] = NULL;
/*
The command should return data formatted like this:
md0 0
md1 0
md3 1
md5 0
md6 0
md10 0
md11 0
md13 0
Iterate through each row, and save the name and value.
*/
row_token = (char*) strtok(buffer, "\n"); // Split the buffer at each newline character
if (row_token == NULL) {
lr_error_message ("Something went wrong. No token found");
return -1;
}
while (row_token != NULL) { // While there are still rows in the buffer...
rc = sscanf(row_token, "%s %d", field_name, &field_value); // Grab the field name and value.
if (rc != 2) {
lr_error_message("Incorrect number of items read from the row.");
return -1;
}
// Create a user data point, so this value can be graphed in the LoadRunner Controller (and in LoadRunner Analysis).
sprintf(lrudp_name, "disk_busy_%s", field_name);
lr_user_data_point(lrudp_name, field_value);
row_token = (char*) strtok(NULL, "\n");
}
/*
Output will be
Notify: Data Point "disk_busy_md0" value = 0.0000.
Notify: Data Point "disk_busy_md1" value = 0.0000.
Notify: Data Point "disk_busy_md3" value = 1.0000.
Notify: Data Point "disk_busy_md5" value = 0.0000.
Notify: Data Point "disk_busy_md6" value = 0.0000.
Notify: Data Point "disk_busy_md10" value = 0.0000.
Notify: Data Point "disk_busy_md11" value = 0.0000.
Notify: Data Point "disk_busy_md13" value = 0.0000.
*/
pclose(fp);
lr_end_transaction("Monitor Disk IO", LR_AUTO);
return 0;
} |
I hope you find this useful – both for the general concept of using LoadRunner to invoke commands (or shell scripts) on a remote Unix host and retrieve the output, and for the ability to monitor disk IO on Solaris without using SiteScope.
Note that the first time you connect to a server using plink, you are likely to get a message like this:
…and you will have to add the key to the cache before you can run plink from LoadRunner.
Another hot tip:
If you use this script for monitoring, run it from a separate load generator, rather than from your Controller, because every time the DOS command is invoked, it briefly pops up an annoying cmd.exe window.