Test Generator API

So far, we have reviewed how a new Custom test can be integrated into eG Enterprise . The one aspect that was not covered in the previous sections is how a test class can be generated to perform the specific functions expected of the Custom test. This section explains the test generator API that is provided with eG Enterprise to enable users to design and implement new testing capability. 

The test generator API consists of a GenericTest abstract class. In order to extend eG’s monitoring functionality, a user has to develop new tests that extend the GenericTest and implement new monitoring capabilities.

Figure 1 shows the architecture of the eG test generator API.  The API is the module that links user-defined tests to other eG agent components.

Figure 1 : Architecture of eG’s test generator API

System Requirements

The test generator API is included as part of the eG agent package. In order to use the API, the CLASSPATH environment setting for the user who is crafting new tests must be set so as to include the Java archive file <EG_HOME_DIR>\lib\eg_agent.jar and <EG_HOME_DIR>\lib\eg_plus.jar, where EG_HOME_DIR is the installation directory of the eG manager and agents (eg. /opt/egurkha on Unix, C:\Program Files\eGurkha on Windows). Consequently, all new test developments must be performed on a system on which the eG agent has been installed.  Once the test is developed and compiled to produce the class file, this file must be made available on the eG manager system, so that the test can be integrated into the eG Enterprise system using the Integration Console.

Component Classes

The Test generator API consists of a single abstract class called GenericTest whose functionality has to be implemented in order to develop a test that would monitor a component type of user’s choice.

Summary of Methods

The following methods of the Test generator API must be used for developing new tests.

Method Signature Description

public void computeMeasures(Hashtable paramList)

This method must be overridden to implement specific test execution functionality. This method should also make necessary calls to other methods in the API as explained above. Apart from that the method may call other user-specific methods or include within itself the functionality for executing the test and assigning values to measures. The result of the execution decides the further calls in the method.

Note:  It is mandatory to override this method.

final void setMeasureCount(int measureCount)

 

This method must be called in the constructor to enable the eG Agent component to register the number of measures this test would report.

Note:  It is mandatory to call this method from the constructor. The count of measures should be the same as the number of measurements configured via the eG Integration Console interface.

Description of arguments:

easureCount:This argument indicates the number of measures the test would report.

final void addNewMeasure(ArrayList measureList)

This method is called whenever a set of measures are to be reported. This is to be used only by a non-descriptor based Test.

Note: The ArrayList passed should always contain a group of Double objects and the list should always follow a same order.

Description of arguments:

measureList: This argument represents an ArrayList collection of all double values encapsulated into Double objects.

final void addNewMeasure(String descriptor, ArrayList measureList)

This method is called whenever a set of measures are to be reported. This is to be used only by a descriptor based Test.

Note: The ArrayList passed should always contain a group of Double objects and the list should always follow a same order.

Description of arguments:

descriptor: This argument represents a String that describes the name with which the corresponding set of measures are associated.

measureList: This argument represents an ArrayList collection of all double values encapsulated into Double objects.

final void setErrorMessage (String errorMsg)

This method can be called whenever the developer wants to log an error or unexpected output for a test.

Note: The error can be read from the agent/logs/error_log file that is available in the agent install directory.

Description of arguments:

errorMsg: This argument represents a string which describes the error that has occurred.

Note:

Tests that are to be run by an external agent can determine the target server and port number that  they are monitoring using the variables - targetHost (italics) and portNo (italics) that are defined in  the base class - i.e., the GenericTest class.

Writing Tests using the Test Generator API

This section outlines how user-defined tests that extend eG’s functionality can be written using the test generator API. A user defined test class must extend the GenericTest abstract class and override the only abstract method to report measures to the eG agent system.

Given below is a sample test that reports the availability of a server.

import java.util.*;
public class AvailabilityTest extends GenericTest
{
// declare necessary variables
double availability = 0;// default value of server availability
public AvailabilityTest ( String [] args )
   {
/*** Calling super initializes several parameters for the test namely
targetHost-Target Host for the test
portNo -Target Port for the test value is “NULL” if its not a port-based server
other parameters configured using the Integration Console interface
***/
super(args);

/*** Call setMeasureCount to initialize the number of measures for the test
***/
setMeasureCount(1);// One measure namely - availability
   }
/*** Call computeMeasures to report measures ***/
public void computeMeasures ( Hashtable paramList )
   {
/*** We may call new methods to perform different tasks. Assume we use a method called getAvailability which performs the availability check and returns the value.***/

availability = getAvailability();
ArrayList al = new ArrayList();
al.add(new Double(availability));

/*** Measures have to be reported by populating an arraylist and passing it as argument to the addNewMeasure() method ***/

addNewMeasure(al);
   }
// User defined methods
private double getAvailability()
   {
/*** This is a user-defined implementation. The user can choose one of several approaches to determine a server’s availability:

Eg. 1 Can Establish socket connection to the targetHost and portNo and find out the availability
Eg. 2 Can ping the targetHost and find out the availability
Eg. 3 Can establish a HTTP connection to the targetHost and portNo and find out the availability

Note : The values of target host and port can be accessed by using the public variables namely “targetHost” and “portNo” respectively. These variables are defined in the GenericTest class itself.
***/

boolean status = connectToServer (); // user defined
if (status) // connection succeeded
return (100);
else
return (0);
   }
}

The above test is an example of a non-descriptor test in the sense that the results of the tests are not specific to a descriptor.

To illustrate how a descriptor based test works, suppose we are monitoring an application that has several instances of servers executing. In this case, it is essential for us to measure and report the availability of each of the server instances.  In this case, the test is required to discover the server instances first and then check the availability of each of these instances. For each server instance, the test reports the availability of the instance.

public class AvailabilityTest extends GenericTest
{
// declare necessary variables
double availability = 0;
boolean serverInstancesDiscovered = false;
// internal variable that is used to indicate if we have already discovered the server instances
String[] servers = null; // used to maintain a list of
// serverInstances
public AvailabilityTest (String [] args)
   {

/*** Calling super initializes several parameters for the test namely
targetHost-Target Host for the test
portNo -Target Port for the test value is “NULL” if its not a port-based server
other parameters configured using the Integration Console interface
***/

super(args);

/*** Call setMeasureCount to initialize the number of measures for the test
***/

setMeasureCount(1);// One measure namely – availability
   }
/*** Call computeMeasures to report measures ***/

public void computeMeasures ( Hashtable paramList )
   {
if (serverInstancesDiscovered == false)
discoverServerInstances(paramList); //user defined function
if (servers == null || servers.length == 0)
return; // unable to discover any servers
for(int i=0; i<servers.length; i++)
      {
/*** We may call new methods to perform different tasks. Assume we use a method called getAvailability which performs the availability check and returns the value.***/

availability = getAvailability(servers[i]);
ArrayList al = new ArrayList();
al.add(new Double(availability));

/*** Measures have to be reported by populating an arraylist and passing it as an argument to the addNewMeasure() method. In descriptor based tests and additional parameter defining the descriptor has to be passed to the method to enable the agent report appropriate availability values for different server instances of the application.***/

addNewMeasure(servers[i], al);
// indicate that this is the measure for servers[i
        }
     }
// User defined methods

private double getAvailability (String serverId)
{
/*** This is a user-defined implementation. The user can choose one of several approaches to determine a server’s availability:

Eg. 1 Can Establish socket connection to the targetHost and portNo and find out the availability

Eg. 2 Can ping the targetHost and find out the availability

Eg. 3 Can establish a HTTP connection to the targetHost and portNo and find out the availability

Note : The values of target host and port can be accessed by using the public variables namely “targetHost” and “portNo” respectively. These variables are defined in the GenericTest class itself.
***/

boolean status = connectToServer (serverId); // user defined
if (status) // connection succeeded
return (100);
else
return (0);
       }
private void discoverServerInstances (Hashtable paramList)
{
/*** Lets assume the discovery is performed by accessing a URL, which in turn returns the details of the existing server instances. In such a case the test would require a parameter ( “URL” in this case ). Such parameters can be configured using the Integration Console interface. These parameters are accessible from the test at runtime. The “paramList” variable which is a java.util.Hashtable object provides access to all such variables configured by the user.***/

String param1 = paramList.get("URL");

/*** We may call new methods to perform different tasks. Assume we use a method called discoverServers which downloads the file available at “URL” and parses the response to find out the list of currently running server instances.***/

servers = discoverServers(param1);
// set the global variable to indicate the list of server instances
serverInstancesDiscovered = true;
// indicate that we have discovered the instances
return;
}
public String [] discoverServers(String url)
{
/*** User defined Implementation
In this example we assume that we discover the server instances by making a url connection to the url configured for this purpose. connection to the targetHost and portNo and find out the availability ***/
}
}

A custom test can use various mechanisms to obtain measurements, eg., Processing log files, using sockets, SNMP etc. The following example depicts a test that uses SNMP to monitor a target. Any test that uses SNMP must extend the EgSnmpGenericTest class.

import java.util.*;
/* Extend EgSnmpGenericTest instead of GenericTest to implement common functionality of Snmp based tests
*/
public class SnmpAvailabilityTest extends EgSnmpGenericTest
{
      private String OID  = “.1.3.6.1.2.1.4.8.1.1.2”;
private double availability = 0.0;
public SnmpAvailabilityTest (String [] args)
        {
super(args);
setMeasureCount(1);

/* Arguments – snmpPort and snmpCommunity, are required for the test and are processed in the super class */
        }

public void computeMeasures (Hashtable params)
    {
  /* Call the following method to walk the specified MIB OIDs. The output of the snmpwalk command is assigned to the String arrays lhs and rhs
lhs -> stores left hand side of the output
rhs -> stores right hand side of the output
  */
              runSnmpCmdForOid(OID);
              if(lhs != null && lhs.length > 0)
/* some output is stored ..
the device is available
*/
availability = 100.0;
else
{
/* No output stored .. the device is not available */

availability = 0.0;
ArrayList al = new ArrayList(); al.add(new Double(availability));
addNewMeasure(al);
        }
}
}

A custom test can also be developed to collect configuration metrics from target components. Given below is a sample custom test script that reports the names of shared folders on a target, the full path to the folders, and the user-defined remarks for each folder.

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.StringTokenizer;
import com.egurkha.util.EgUtilities;
/**
Measures:
1. Resource
2. Remark
 */

public class NetShare_cf_ex extends GenericTest
{
public NetShare_cf_ex(String[] args)
{
super(args);
setMeasureCount(3);
setConfigInfoTestFlag(true); // true for info based
}
public void computeMeasures(Hashtable ht)
{
try
{
EgUtilities egUtil = EgUtilities.createInstance();
String netShareCmd = “net share”;
ArrayList data = (ArrayList) egUtil.getExecOutputLines(netShareCmd);
if (data == null || data.size() < 2)
{
configError = true; // info based only
return;
}
data = (ArrayList) data.get(0);
if (data == null || data.size()  == 0)
{
return;
}
StringTokenizer st = null;
ArrayList measureList = null;
int size = data.size();
for (int g = 0; g < size; g++)
{
String line = (String) data.get(g);
//System.out.println(line);
if (line == null)
{
continue;
}
line = line.trim();
if (line.length() == 0 || line.startsWith(“Share”) || line.startsWith(“------“) || line.startsWith(“The command comp”))
{
continue;
}
st = new StringTokenizer(line);
int count = st.countTokens();
String shareName = “”;
String resource = “-“;
String remark = “-“;
if (count >= 2)
{
shareName = st.nextToken().trim();
if (shareName.equalsIgnoreCase(“IPC$”)) // Remote IPC
{
remark = st.nextToken().trim() + “ “ + st.nextToken().trim();
}
else
{
resource = st.nextToken().trim();
if (count >= 3)
{
remark = st.nextToken().trim() + “ “ + st.nextToken().trim();
}
}
}
measureList = new ArrayList();
measureList.add(resource);
measureList.add(remark);
addNewMeasure(shareName,measureList);
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
public static void main(String[] args)
{
NetShare_cf_ex net = new NetShare_cf_ex(args);
net.computeMeasures(new Hashtable());
}
}

The sample script above is for a descriptor-based configuration test. Non-descriptor-based configuration tests can also be developed. Given below is a sample script for the same.  This script simply reports the number of shared folders on a target.

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.StringTokenizer;
import com.egurkha.util.EgUtilities;
/**
Measure:
1. No of share folders
*/
public class NetShareCount_cf_ex extends GenericTest
{
public NetShareCount_cf_ex(String[] args)
{
super(args);
setMeasureCount(1);
}
 
public void computeMeasures(Hashtable ht)
{
try
{
EgUtilities egUtil = EgUtilities.createInstance();
String netShareCmd = “net share”;
ArrayList data = (ArrayList) egUtil.getExecOutputLines(netShareCmd);
if (data == null || data.size() < 2)
{
return;
}
 
data = (ArrayList) data.get(0);
if (data == null || data.size() == 0)
{
return;
}
 
StringTokenizer st = null;
ArrayList measureList = new ArrayList();
int k = 0;
int size = data.size();
for (int g = 0; g < size; g++)
{
String line = (String) data.get(g);
if (line == null)
{
continue;
}
line = line.trim();
if (line.length() == 0 || line.startsWith(“Share”) || line.startsWith(“------“) || line.startsWith(“The command comp”))
{
continue;
}
++k;
}
measureList.add(k+””);
addNewMeasure(measureList);
}
catch (Exception e)
{
e.printStackTrace();
}
}
public static void main(String[] args)
{
NetShareCount_cf_ex net = new NetShareCount_cf_ex(args);
net.computeMeasures(new Hashtable());
}
}

Writing Detailed Diagnosis Tests

This section outlines how to write detailed diagnosis tests for the user-defined classes that extend eG’s functionality.

The Java class that implements detailed diagnosis for a test must be named after the test and be suffixed by an _ex_DD. Such tests extend EgTest_DD and must include a method for every measure for which detailed diagnosis is desired. The name of the class is case-sensitive.

For the MsFileTest_ex in our example, the Java class that implements detailed diagnosis should be called MsFileTest_ex_DD. Since detailed diagnosis has been configured for the measure File_locks_count, a method with the name File_locks_count_dd has to be implemented. Like the class name, the method name is also case-sensitive.

Given below is a sample test that gives a detailed diagnosis on the files opened for MsFileTest_ex:

/*
Detailed diagnosis for MS_FILE_SERVER_ex component type is enabled for the measure File_locks_count
*/
import java.util.*;
/*
Tests for Detailed Diagnosis must by suffixed by _ex_DD.
*/
public class MsFileTest_ex_DD extends EgTest_DD
{
/*
Declare the necessary variables
*/
private boolean uploadStatus = false;
public MsFileTest_ex_DD(String entity)
{
super(entity);
}
/* For every measure that requires detailed diagnosis, add a method, the name of which is of the type <measureName>_dd */

public void File_locks_count_dd(
String methodName,
String measureName,
String targetHost,
String reportingName,
String portNo,
String siteName,
String info,
String msmtHost,
String msmtTime,
String state,
EgTest test)
{
String ddData;
 
/* The data that is to be displayed as part of detailed diagnosis is collected using the
* addLine() method.
*/
this.addLine(ddData);

/* Once all the data for detailed diagnosis has been added, send the results to the manager using the uploadResults() method */

uploadStatus = this.uploadResults(
methodName,
measureName,
reportingName,
portNo,
siteName,
info,
msmtHost,
msmtTime,
state);
}
}
}

Note:

Though any number of lines of data can be added using the addLine() method, it is recommended that the implementation of the detailed diagnosis does not send large amounts of data to the manager everytime.

Troubleshooting

To troubleshoot problems when a user-defined test is integrated into eG Enterprise, check the entries in the agent error log, which is available in the file <EG_HOME_DIR >\agent\logs\error_log. For entries specific to a user-defined test to be available in the error log, it is necessary for the test to log errors explicitly.