Showing posts with label Python. Show all posts
Showing posts with label Python. Show all posts

2011/05/26

Maintainable JMeter Scenarios

One of the problems I have with JMeter is that its scenarios are hard to maintain.

I have already found that I can introduce variables to parametrize a scenario but it is kind of cumbersome - when application changes a bit I had to re-run the scenario in Firefox with Firebug, hunt for parameters' values and set their new values.

This approach is slow and prone to errors. Definitely no way for my colleagues that do not know JMeter at all or only a little.

Recently I have had to solve it and I have come with following solution - let's transform the original scenario to a template and fill it with data gathered by some transparent proxy during manual re-play of the scenario.


Recording The Session

I found Paros proxy (or its fork Andiparos) to be a good match for my needs, although the proxy is made for vulnerability testing of web applications. To record a session follow these steps:
  1. Run Paros on your workstation.
    1. Open Tools - Options and set:
      1. Local Proxy - domain to localhost, port to 9009  
      2. Trap - check "Use exclusive filter" and insert pattern to exclude unwanted requests : .*\.(png|js|css)
    2. Open Tools - Filter and check "Log request and reponse into file (filter/messages)"

  2. Run Firefox :
    firefox -P -no-remote
    This will open Firefox Profile manager. Create a new profile, named e.g. test. Run Firefox with this profile and set manual HTTP proxy to the values set previously in Paros.

  3. In the browser go through your scenario. You can close the browser after that.
  4. Get recorded session from Paros directory - messages.txt file.

 The Template

When you have the recorded session, open you JMeter scenario - a file with jmx suffix - and replace all parameter values with custom place holders. The file contains XML, here is a part of it with %HOST% and %PORT% place holders :

<ConfigTestElement guiclass="HttpDefaultsGui" testclass="ConfigTestElement" 
                   testname="HTTP Request Defaults" enabled="true">
  <elementProp name="HTTPsampler.Arguments" elementType="Arguments" 
               guiclass="HTTPArgumentsPanel" testclass="Arguments" 
               testname="User Defined Variables" enabled="true">
     <collectionProp name="Arguments.arguments"/>
   </elementProp>
   <stringProp name="HTTPSampler.domain">%HOST%</stringProp>
   <stringProp name="HTTPSampler.port">%PORT%</stringProp>
</ConfigTestElement>

The last thing you need is a Python script to extract the parameters' values and fill the template with them:
#!/usr/bin/python

import sys
import argparse
import string
import re

if __name__ == '__main__':

    host_pattern = re.compile("^Host: (.*)")
    host_value = None

    # -------------- get cmdline args -----------------
    parser = argparse.ArgumentParser(prog='jmfiller')
    parser.add_argument('session_record_file', help='session record file')
    parser.add_argument('template_file', help='template file')

    args = parser.parse_args()
 
    session_record_file_name = args.session_record_file
    template_file_name = args.template_file

    # -------------- collect data ----------------------
    for line in open(session_record_file_name,'r'):
       # get HOST    
       if host_value is None:
          match = host_pattern.search(line)
          if match is not None: host_value = match.group(1)

    print  >> sys.stderr, "HOST:", host_value

    # -------------- insert in the template ------------

    host_placeholder                    = "%HOST%"

    for line in open(template_file_name,'r'):       
        output_line = line
        output_line = string.replace(output_line, host_placeholder, host_value)

        print output_line

2011/02/09

Monitoring Tomcat with Jmxterm and Python

I needed to collect statistics related to connections in Tomcat. The goal was to use some command line tool to make dumping and post-processing of acquired data as simple as possible. It seems that jmxterm fitts well into these requirements. Following text represents short HOWTO for monitoring some of Tomcat's JMX attributes using jmxterm.
  1. Enable JMX in your Tomcat, e.g. by adding following parameters to its java command line.
            -Dsun.management.jmxremote -Dcom.sun.management.jmxremote.port=3993
            -Dcom.sun.management.jmxremote.ssl=false
            -Dcom.sun.management.jmxremote.authenticate=false
    As you see this settings turns off  both SSL and authentication, to make this simplem, although not secure.
    Jmxterm does not support SSL yet, password-based authentication in command open is supported (use help open in jmxterm shell for more info).
  2. Download jmxterm from http://sourceforge.net/projects/cyclops-group/files/jmxterm/
  3. Start in interactive mode to test commands:
    java -jar jmxterm-${version}-uber.jar
    (for ${version} substitute current version of jxmterm, e.g. "1.0-alpha-4")
  4. Run commands to get desired data
    1. open localhost:3993
    2. Restricts output of following commands to given domain - select domain: domain Catalina.
    3. Now you can list beans:
      beans
    4. now the domain is set and we get values of some attributes:
      • get -b Catalina:port=80,type=Connector acceptCount
        get -b Catalina:port=80,type=Connector acceptCount
        #mbean = Catalina:port=80,type=Connector:
        acceptCount = 100;
      • get -b name=http-80,type=ThreadPool maxThreads
        get -b name=http-80,type=ThreadPool maxThreads
        #mbean = Catalina:name=http-80,type=ThreadPool:
        maxThreads = 40;
      • get -b name=http-80,type=ThreadPool currentThreadCount
        get -b name=http-80,type=ThreadPool currentThreadCount
        #mbean = Catalina:name=http-80,type=ThreadPool:
        currentThreadCount = 15;
      • get -b name=http-80,type=ThreadPool currentThreadsBusy
        get -b name=http-80,type=ThreadPool currentThreadsBusy
        #mbean = Catalina:name=http-80,type=ThreadPool:
        currentThreadsBusy = 2;
      • get -b Catalina:host=localhost,path=${webapp_context},type=Manager activeSessions
        get -b Catalina:host=localhost,path=${webapp_context},type=Manager activeSessions
        #mbean = Catalina:host=localhost,path=${webapp_context},type=Manager:
        activeSessions = 3;
    5. Put commands in file and run this scripts in non-interactive way
      java -jar jmxterm-${version}-uber.jar -n
      or
      java -jar jmxterm-${version}-uber.jar -n -i jmx_script

      content of the file jmx_script:
      open localhost:3993
      get -d Catalina -b Catalina:port=80,type=Connector acceptCount
      get -d Catalina -b name=http-80,type=ThreadPool maxThreads
      get -d Catalina -b name=http-80,type=ThreadPool currentThreadCount 
      get -d Catalina -b name=http-80,type=ThreadPool currentThreadsBusy
      quit

      Output:


      Welcome to JMX terminal. Type "help" for available commands.
      #Connection to localhost:3993 is opened
      #mbean = Catalina:port=80,type=Connector:
      acceptCount = 100;

      #mbean = Catalina:name=http-80,type=ThreadPool:
      maxThreads = 40;

      #mbean = Catalina:name=http-80,type=ThreadPool:
      currentThreadCount = 18;

      #mbean = Catalina:name=http-80,type=ThreadPool:
      currentThreadsBusy = 5;

      #bye

note #1 : if domain si not set, you must give it  to get command as parameter:
get -d Catalina -b ${bean_name} ${attribute_name}

Wrapping in Python

The problem with jmxterm si that its startup time is really long - even several seconds. Also connecting to process can take second or two. The reponsivness is good so if you need it for periodic monitoring, you would like to start it and connect and then fire requests when you need the data. After trying some approaches I settled on Python pexpect for this (script reduced to minimum):
import time
import pexpect

connection = "localhost:3993"
connection_timeout = 2

jmxterm = pexpect.spawn("java -jar jmxterm-1.0-alpha-4-uber.jar")
jmxterm.expect_exact("$>") # got prompt, we can continue
jmxterm.sendline("open " + connection)
jmxterm.expect_exact("#Connection to "+connection+" is opened", connection_timeout)

jmxterm.sendline("get -d Catalina -b name=http-80,type=ThreadPool currentThreadCount")

response_lines = []
response_lines.append(jmxterm.readline())
response_lines.append(jmxterm.readline())
response_lines.append(jmxterm.readline())
response_lines.append(jmxterm.readline())

result = response_lines[3].replace(";"," ").strip().split(" ")
del result[1]
name,value = result

print "["+time.ctime()+"]", name, "=", value
jmxterm.sendline("quit")