-
Notifications
You must be signed in to change notification settings - Fork 3
Extending ETCE
An ETCE Wrapper encapsulates the details of configuring, starting
and stopping a single application (the wrapped application). Each
Wrapper is a Python class that inherits from the common Wrapper
base
class. The Control Node dynamically loads wrappers to start and stop
applications on worker nodes. Users extend ETCE by writing Wrappers
for new applications.
class Wrapper:
'''
The common base class for all ETCE Wrappers.
'''
def register(self, registrar):
'''
Optional method for registering:
1. The name of the input file whose presence triggers the wrapper
to run the wrapped application.
2. The name of any output file created by the wrapper. This is a
convenience method allowing the context builds an absolute
filename to the common directory where test artifacts are stored.
3. Arguments accepted by the wrapper for customizing execution -
typically a subset of the wrapped application's command line
arguments. The exposed arguments can be set at runtime.
'''
pass
def prerun(self, ctx):
'''
Optional method, called just before *run* to test or enforce wrapper
preconditions (if any) for running. The wrapper should throw an
PreconditionError when a precondition fails.
'''
pass
def run(self, ctx):
'''
Required method called to start the wrapped application.
'''
raise NotImplementedError('Wrapper.run')
def postrun(self, ctx):
'''
Optional method, called just after *run* to test or enforce wrapper
postconditions (if any) for running. The wrapper should throw an
PostconditionError when a postcondition fails.
'''
pass
def stop(self, ctx):
'''
Required method called to stop the wrapped application.
'''
raise NotImplementedError('Wrapper.stop')
Here is an example for running the Linux top
:
class Top(Wrapper):
"""
Periodically log information from the Linux "top" command.
The input file is an empty file (a "flag"). The wrapper runs
top when the file is present.
"""
def register(self, registrar):
registrar.register_argument('periodsecs',
5,
'the number of seconds to ' \
'sleep/wait between readings.')
registrar.register_infile_name('top.flag')
registrar.register_outfile_name('top.log')
def run(self, ctx):
if not ctx.args.infile:
return
periodsecs = ctx.args.periodsecs
argstr = '-b -d%s' % periodsecs
ctx.daemonize('top',
argstr,
starttime=ctx.args.starttime,
stdout=ctx.args.outfile)
def stop(self, ctx):
ctx.stop()
Wrappers are installed in the etcewrappers
Python namespace package.
They are grouped into sub-packages. For example on Ubuntu the Wrappers provided with ETCE are found here:
[you@host]$ tree -d /usr/lib/python2.7/dist-packages/etcewrappers
/usr/lib/python2.7/dist-packages/etcewrappers
|-- emane
|-- lte
|-- ostatistic
|-- otestpoint
`-- utils
Users may extend ETCE by adding Wrappers to their own sub-package. Use
the etce-wrapper
application to query details about installed
wrappers.
[you@host]$ etce-wrapper list
emane.emane
emane.emanecommand
emane.emaneeventservice
emane.emaneeventtdmaschedule
...
utils.sleepwait
utils.smcrouted
utils.sysctlutil
utils.top
utils.zebra
[you@host]$ etce-wrapper list -v utils.top
---------
utils.top
---------
description:
Periodically log information from the Linux "top" command.
input file name:
top.flag
output file name:
top.log
arguments:
periodsecs
the number of seconds to sleep/wait between readings.
default: 5
ETCE passes a WrapperContext
object to most Wrapper methods. The
context provides access to registered file name and argument values
and methods that help to eliminate the repetitive code that many
wrappers require.
Help on module etce.wrappercontext in etce:
NAME
etce.wrappercontext
CLASSES
class WrapperContext(etce.argregistrar.ArgRegistrar)
| WrapperContext aims to help eliminate repetitive, boilerplate
| Wrapper code and standardize Wrapper workflow. Specifically,
| WrapperContext:
|
| * Standardizes the way Wrapper arguments are specified, searched
| and presented to Wrappers.
|
| * Standardizes the way input and output files are searched and
| presented to Wrappers.
|
| * Provides helper methods for running/daemonizing and stopping wrapped
| applications.
|
| * Provides a uniform way for Wrappers to store meta information.
|
| * Provides access to an ETCE Platform object that provides helper
| methods for performing low level operations.
|
| A WrapperContext object is passed to most Wrapper methods as their
| "ctx" argument.
|
| Method resolution order:
| WrapperContext
| etce.argregistrar.ArgRegistrar
| __builtin__.object
|
| Methods defined here:
|
| __init__(self, impl)
|
| daemonize(self, commandstr, stdout=None, stderr=None, pidfilename=None, genpidfile=True, pidincrement=0, starttime=None)
| Run a command as a daemon process.
|
| commandstr:
| The full command string to run.
|
| stdout:
| An optional file name to capture standard output.
|
| stderr:
| An optional file name to capture standard error.
|
| pidfilename:
| An alternative file name to use to write the daemonized
| processes' PID. default_pidfilename is used if not specified.
| Only used when genpidfile is True.
|
| genpidfile:
| Do generate a PID file (True: default) or not (False).
|
| pidincrement:
| Some commands fork and exec further subprocesses in a
| manner where the original parent process is not the long
| running process whose PID is useful to capture. Sometimes
| the relationship between the long-running exec'd PID and
| the parent PID is a fixed increment. Specifying a value for
| pidincrement causes the context to store the parent PID +
| pidincrement in the PID file. Note, this mechanism has
| limited use though where tests are run over a long enough
| period to cause the range of exec'd PID numbers to wrap. In
| this case the difference between the parent PID and
| long-running exec'd PID will not reliably be fixed as the
| kernel skips PIDs when they are in use.
|
| starttime:
| An optional YYYY-MM-DDTHH:MM::SS string. After forking,
| daemonize will sleep until the provides time before
| exec'ing the command. The command is exec'd immediately
| if not specified.
|
| register_argument(self, argname, defaultval, description)
| Register an input argument used by the wrapper. Wrapper
| arguments are generally a subset of the wrapped application's
| command line arguments, especially arguments that are useful
| to change on a trial by trial basis. Log levels are a typical
| example.
|
| Argument values are passed to the wrapper as ctx.args.ARGNAME,
| with defaultval used if the argument is not set
| externally. Users set wrapper argument values in the Test
| Directory steps.xml file or in an optional configuration file
| passed into the etce-test run command.
|
| Besides user registered arguments, ETCE reserves a small
| set of arguments, also passed in through ctx.args, that
| cannot be overwritten by the user -
|
| default_pidfilename:
| Absolute (default) pidfile name. Many Wrappers write
| they PID of the wrapped application they launch to a
| file with this name. Pidfiles are placed in the `lock`
| subdirectory of the etce.conf WORK_DIRECTORY.
|
| logdirectory:
| the absolute path to the output directory. The
| logdirectory is a scoped path name that includes the
| name of the current test, a date time stamp and the name
| of the host where the Wrapper is running. Wrappers must
| use this path for any output files they produce that are
| to be collected with the test results.
|
| nodename:
| the hostname where the current wrapper is executing
|
| nodeid:
| if nodename contains an integer value, it is passed
| as an int in this member, otherwise None
|
| starttime:
| the current test's T=0 scenario time in format
| YYYY-MM-DDTHH:MM:SS
|
| stepname:
| the current step name as defined in the steps.xml file
|
| testname:
| the current test name as defined in the test.xml file
|
| wrappername:
| this wrapper's name
|
| register_infile_name(self, name)
| Register the input file name used by the wrapper.
|
| When a Wrapper registers in input file name, the context
| searches for a matching file name in the hosts's configuration
| directory and, if found, passes the absolute name to the
| wrapper in the ctx.args.infile member. ctx.args.infile is set
| to None if no matching file is found.
|
| Most wrappers use the presence of their input file as a
| trigger to run their wrapped application.
|
| register_outfile_name(self, name)
| Register the output file name used by the Wrapper.
|
| Wrapper conventionally register a log file as thier output
| file. This method is a convenience method that passes back the
| absolute name of the output file the wrapper should use in the
| ctx.arg.outfile member.
|
| run(self, commandstr, stdout=None, stderr=None, pidfilename=None, genpidfile=True, pidincrement=0)
| Run a command.
|
| commandstr:
| The full command string to run.
|
| stdout:
| An optional file name to capture standard output.
|
| stderr:
| An optional file name to capture standard error.
|
| pidfilename:
| An alternative file name to use to write the commands
| PID. default_pidfilename is used if not specified. Only
| used when genpidfile is True.
|
| genpidfile:
| Do generate a PID file (True: default) or not (False).
|
| pidincrement:
| Some commands fork and exec further subprocesses in a
| manner where the original parent process is not the long
| running process whose PID is useful to capture. Sometimes
| the relationship between the long-running exec'd PID and
| the parent PID is a fixed increment. Specifying a value for
| pidincrement causes the context to store the parent PID +
| pidincrement in the PID file. Note, this mechanism has
| limited use though where tests are run over a long enough
| period to cause the range of exec'd PID numbers to wrap. In
| this case the difference between the parent PID and
| long-running exec'd PID will not reliably be fixed as the
| kernel skips PIDs when they are in use.
|
| starttime:
| An optional YYYY-MM-DDTHH:MM::SS string. The run call
| blocks and sleeps until the specified time before running
| the command.
|
| stop(self, pidfilename=None)
| Stop the process associated with the PID in the specified file.
|
| This function sends SIGQUIT to the process associated with the
| PID contained in pidfilename and then removes the file. If
| pidfilename is not specified, default_pidfilename is used.
|
| store(self, namevaldict)
| Store the name/value pairs passed in via the dictionary
| argument to the JSON format `etce.store` file for the
| current host. Values are automatically subdivided in the storage
| file by wrapper name to avoid collision.
|
| ----------------------------------------------------------------------
| Data descriptors defined here:
|
| args
| The *args* member contains the values of the Wrapper's
| registered arguments as args.ARGNAME, the registered
| input and output filenames as args.infile and args.outfile,
| and the reserved arguments listed above (args.logdirectory,
| for example).
|
| platform
| An etce.platform.Platform object.
|