29 August 2013

Project: Making a SIP GUI test harness with Grails and SIPP - Part I

Making a SIP GUI Test Harness - Part I

Introduction

Part I will cover:

  • The Introduction
  • Setting up SIPP scenarios
  • Creating a Grails project
  • Creating Grails Domain Classes and Controllers

In VOIP, the term SIP refers to Session Initiation Protocol.  It's a protocol that allows voice communication via online connections.  Many phone calls are themselves SIP at some point.

If you currently test SIP, you probably use tools like SIPP.  SIPP is the most common tool I know of. It's open source, very diverse in functionality, and has a large following of contributors.  SIPP is typically used to generate load tests - like driving 50 calls per second, to a switch.  But SIPP can also be used for functional tests as well.


Audience for this Tutorial


  • SIPP is installed and running
  • Familiar with some scripting/programming (I'll be very detailed on basics of groovy/grails)
  • A basic understanding of automation
  • A basic understanding of VOIP 
  • You have an environment with a soft switch (FreeSwitch, Asterix... or a sip server like Berkeke, OpenSIPS, etc.) that we can route calls through.

End Results

By the end of this tutorial, you should have a working UI that will take a phone number and proxy (sip server) - dial the number via the proxy on submit and validate if the call passed or failed.  You will also have the foundation on which to build other test criteria, such as voice analysis and load testing.

Basically by the end of this demonstration you should be able to do this with Grails:

  • Be able to create a Grails Project
  • Know how to create controllers
  • Know how to create services and why they're cool
  • Set up Domain Classes
...and you'll be able to do this with VOIP / SIP:
  • Create SIPP XML scenarios for Outbound carrier tests
  • Run SIPP as a Functional Test Tool

SIPP Basics

While SIPP automates these phone calls, it's use is manual.   That is, you create a XML file that builds the scenario. You then type a command line, like:

sipp [phone number] [switch] -sf [path to xml scenario file] -l [limit the amount of calls to this value] -r [limit the rate of calls] -trace_err



SIP Automation

That's great for one off tests, what if you want something run often by a chron job or by other people who may not know SIPP's depth of command line properties?

What if you wanted to integrate functionality with SIPP to do things like Audio Analysis, or to iterate through a collection of tests?

That's why I created a test harness. 

My first SIP Test Harness was written with shell scripts.  It worked, but it put my work all over the place. I had one shell script to do a phone call, another to do complex audio analysis, another to do packet captures, etc.

I've improved on this, and in the process got a UI thrown in for free.  


What is Grails and Why Use It

Grails is a MVC architecture for Groovy. MVC stands for Model View and Controller.  It's a design system that offers a quick way to generate code.  It makes use of domain models, hibernate, plugins and much more - to give a rich tool set.

You'll get the UI for free.  Grails will generate UI's for you and we can modify the static scaffolding to suite our needs.

Groovy and Grails (much like Ruby and Rails) is very simplistic and will allow you to write complex applications with very little code.  You can parse JSON with a line of code, or generate CRUD (create, read, update, delete) actions instantly with a command line.  It's fast and rapid development that truly suits tools development.

Why Grails? Why not Rails?  
No particular reason.  The benefit of Grails is that it compiles down to Java Byte Code so it runs where there is Java. Also Java developers can easily read it ... and there is the benefit of having more people who can update the code as needed.  


Technology Used


  • SIPP (make sure you have that installed - you'll need linux or MacOSX
  • Tomcat (I'll use Tomcat, but you can feel free to use Jetty or something else)
  • Grails (For grails you'll need Java installed, as well as groovy, and you'll want to have an IDE.  A free IDE is the Groovy Grails Tools Suite from SpringSource: http://www.springsource.org/groovy-grails-tool-suite-download A better choice IMO is the commercial version of Intellij.  But either will work.  This tutorial will use Intellij.
  • SoftSwitch that we can route calls through (Asterix, FreeSwitch, etc.)

Test Harness Requirements

First, what do we want the test harness to do?  

For the purpose of this demonstration, I'm going to build out a test harness that will 
  1. place calls 
  2. validate if the call is a pass or failure.  
  3. Then we'll build from there to iterate through a list of different call scenarios (like calling different carriers.)
We need to make sure the technology used is working... so:
  • Make a test call with SIPP (dial a DNIS/phone number with an installed soft switch)
  • Verify you have Tomcat or Jetty up and running
  • Verify you have Grails avail., in a command prompt do a grails -version. 
  • Verify your IDE is integrated with Grails (I.e. you can make a grails project)
If you can check off the four items in the above list, then we're good to go.

So let's start....

Create a Grails Project

To create a Grails project, just go to a directory on your local file system and do a 
grails create-app [name]  
Example:
 grails create-app siptesting

In your IDE load the project.
For example, in Intellij you would do a:
File | Import Project
Point it to the project you just created and accept the prompts it gives.

Once the project is loaded in the IDE, we'll create the domain class first.  The domain class will set the data elements we care about, any validation rules on them and these values will be stored in the db.

Speaking of Db, we could use any db we want, but for this demo, we'll just the in memory db (h2) that Grails defaults to.

To create a domain class in Intellij, you right click the project name, then click the sub menu item: New | Grails Domain Class

Give it a name like: OutboundCall

In the project tree, you should see a structure like:
grails-app
   > conf
   > controllers
   > domain

In domain there should be a class now, called OutboundCall, open that up.

It will probably look like this:
class OutboundCall {
   static constraints = {

   }
}

Under the class reference, we're going to define some values.  These values will tell Grails what to store.  The info in a domain class is also used to create scaffolding elements like controllers and views.  

By that I mean, if you define something in the Domain, Grails will generate all the actions to capture this data for you.  Which saves from writing a lot of structure from scratch.

So lets define some things we want to capture...

Under class OutboundCall {
Date dateCreated
String carrier
String results
String ipProxy
String phone
String pass
dateCreated is an internal function. It will add a timestamp to each save of data
carrier will be captured from user input
results will be a computed value
ipProxy will be captured from user input
phone will be captured from user input
pass will be a computed value

Below this, there is that closure called "static constraints"  - we want to add some constraints to the above variables.  For example, we are ok with date being nullable, and only be able to pick carriers from a defined list (dropdown...) 

So in the static constraints closure, add the following:
  
dateCreated nullable: true
carrier inList: ['Sprint','Verizon','Tcast']
results maxSize: 50000, nullable: true, display: false
ipProxy blank: false
phone blank: false
pass nullable: true, display: false
What we're saying here is this: 
dateCreated is nullable, but why? Because it's not a value on the user creation form. It's going to be calculated by Grails.  I had to make this nullable because otherwise Grails expects a value.  
carrier uses a inList. Everything in the square brackets is going to be a option in a dropdown in a form.
results - we are saying results will have a size limitation and it's also nullable, and it won't display.  The reason is that this field is going to be the SIPP output.  It can be large output and it's not user input.  We don't want it to display, except when it's avail, so not until the results are shown.
ipProxy and phone - are user input fields and they can't be blank.
pass - will be a value based on some logic to determine if the call went through fine or not.
Basically the Domain Class should look like this:
 
class OutboundCall{

    Date dateCreated
    String carrier
    String results
    String ipProxy
    String phone
    String pass


    static constraints = {
        dateCreated nullable: true
        carrier inList: ['Sprint','TCast','Verizon']
        results maxSize: 5000000, nullable: true, display: false
        ipProxy blank: false
        phone blank: false
        pass nullable: true, display: false
    }
    static mapping = {
        results type: 'text'
    }
}

Generating the Static Scaffolding

At this point, we've created the domain.  Lets do our first generation of code.  We want Grails to generate the Controllers (the part of the code that will route logic) and Views (the display portion of the code.)

In Intellij, you can do a control+alt+G (windows) or Mac equivalent to pull up a command line for grails. In the box you will send:
grails generate-all [packag name].[class name]

For example:
grails generate-all siptests.OutboundCall

This will generate a controller for you called OutboundCallController (under the controllers folder in the project tree) and a set of views in a folder called "outboundCall."  


Run the App

Let's see how our application looks.... go ahead and start it.
In Intellij we can click the green "play" like triangle at the top of the screen.  

It should load without issue.  Once it loads, it will give you a url, like http://localhost:8080/... click that link.  

Iniitally you will land on a page that Grails generates to show you all your plugins used in the project and has links to your controllers.  Click on the controller link.

This UI is all pre-built for you.  You can "create" and it will load a UI with the fields we set in the Domain class. Go ahead and put some data in there and save it.

You'll be back at the List, showing your data you just saved.

It's not doing anything yet, other then capturing user input.  But we can see that the applicaiton is up and running and taking user input.

Let's add some logic!

Controller

Go ahead and open up the controller we generated.

You'll see a lot of code.  There will be all your crud actions in there.  You'll see things like
def create() 
def save()
def show()

This is the basic functionality of creating, saving, showing your data....

Our goal here is to take a user's input, and pass it to SIPP and validate the results we get.  So we want to 

SIPP

Back in SIPP, lets create some test scenarios.... Grails is going to let users send the phone number, the proxy and the carrier for the test.  So lets get SIPP scenarios set up to handle this.

on the command line do a:
sipp -sd uac > outbound_call.xml
The above command will export a base template from SIPP to the XML file specified.

Once we have that file, lets edit it.  We need to specify the carriers.  In our example, we are going to allow users to send calls using Sprint, Verizon and TCast.  To make this simple, we'll create three XML scenario files - one for each carrier.  In each one, we'll hard code the carrier into the SIP Invite header.

If setting the carrier is applicable to you, you can define the carrier in the XML scenario.  If it isn't applicable to you, then just omit the carrier reference in Grails (in the domain class) and just capture the phone number and proxy.

Some people can hard code a carrier value like:
Carrier: SPRINT
which is mapped to rules in their Sip Server (Berkeke or OpenSIPS) to send the call to the appropriate carrier.

If that's of interest, then lets do it.... let's copy that template as three files:
sprint_outbound_call.xml, verizon_outbound_call.xml, tcast_outbound_call.xml

Edit each one and modify the carrier value you read in from your sip server to be hard coded to this value.  

Now when you run the sipp call to use this scenario it will be set to use this carrier - testing the carrier itself in handling the call.

Run a sipp scenario here that we made and make sure the call will go through to a phone number you have defined on the sip server.  

If it works... then continue to Part II (Part II will cover setting up a Service and updating the Controller as well as Validating the results.)

No comments:

Post a Comment