Simple
Each node runs a single daemon process for receiving and processing events from the messaging system. An agent is a python module that is loaded by the daemon process. To add your own agent type, you need to create a python class that extends Agent or a subclass and place the file in a modules directory of a SEER package directory.
You must defined some class variables in your agent that are used to determine message types, variables to accept, control panel look, etc. Any class level docstrings will also be collected for display in the GUI.
When extending the basic agent class, you need to call the Agent __init__ method, and then implement the methods to respond to commands from a GUI or script. The Agent class will process incoming variable sets and call handleX where X is an event type. The Agent class provides TGStart and TGStop for basic usage of a traffic generation tool. Just assign these methods tohandleSTART and handleSTOP.
Variables types are defined in messages_pb2.py. The current list is:
The NodeList and IPList types provide a wrapper around list with some additional behaviour. NodeList is is a basic list of strings with an additional method myNodeMemberOf that returns true if the current node is a member of the list. IPList is a basic list of strings with an additional method matchMyIPs that returns the set of strings that match an IP address on the local machine.
The agent variables for each instance are available as a regular class variable. For example: self.servers will refer to the servers variable. They are initialized with your default value up creation of each new group (object). When setting variables from a command, they also come with a node name so each variables is actually stored as a default dictionary. When you refer to a variable, if your node name appears as a key into the dictionary, that value is loaded. Otherwise, the ‘*’ value is loaded. If you want to see what another node has for its value, you can call getNodeVar to get the entry for another node name. Accessing the variable directly is the same as calling getNodeVar(myname)
Other instance variables that are preset are: self.pids array of PID values self.runningserver used by TGStart/TGStop code to note if a server was started self.group the group name for this instance self.log link to a Logger object for this instance Here is the Web agent class as an example:
class HTTPAgent(agent.Agent):
"""
The HTTP generator controls a set of wget clients that make HTTP requests
to a set of servers running apache
"""
DEPENDS = ['ApacheService']
SOFTWARE = []
AGENTGROUP = 'Traffic Generation'
AGENTTYPE = 'HTTP'
NICENAME = 'Web'
COMMANDS = ['START','STOP']
VARIABLES = [
Variable(NODELIST, 'clients', [], 'Clients', 'Select the nodes that will become HTTP agents'),
Variable(NODELIST, 'servers', [], 'Servers', 'Select the nodes that will become HTTP servers'),
Variable(DIST, 'think', 1, 'Thinking Time', 'Function to determine time between requests'),
Variable(DIST, 'sizes', 1, 'File Sizes', 'Function to determine the size of the page requested')
]
def __init__(self): agent.Agent.__init__(self)
handleSTART = TGGStart
handleSTOP = TGStop
def serverExec(self): services.ApacheService.start()
def serverStop(self): services.ApacheService.stop()
def clientExec(self, src, dst, size):
cmd = "wget -nv -t 1 -O /dev/null "
if (src is not None):
cmd += "--bind-address %s " % (src)
cmd += "http://%s/getsize.py?length=%d " % (dst, size)
subpid = spawn(cmd, None)
def handleBLAH(self):
""" This method would be called when the command BLAH is received
for self.group. It will print out the current set of variables
"""
print self.clients, self.servers, self.think, self.sizes
In the example we make use of TGStart and TGStop from the base class. These functions require that the variables (clients, services, think, sizes) are avaiable and so we define them in VARIABLES. TGStart() and TGStop() also require you to override some base methods. In this case: serverExec(), serverStop() and clientExec().
Note that when using TGStart(), a new process group is created for you if the local node is a member of clients and calls to clientExec() are made from this new process so that killing off an agent client tree is just killing the process group.
If you want to change the inner workings you can also override clientOneLoop() to override the loop behavior or launchTrafficController() to control all of the start behavior. However, if you are thinking of overriding launchTrafficController() it may be better to just write your own handleSTART() routine.
The best place to find examples is in the repository modules directory http://seer.isi.deterlab.net/trac/browser/code/trunk/modules.