#!/usr/local/bin/perl
#!/opt/ActivePerl-5.8/bin/perl
#!/usr/bin/perl
#sptest 20060622 NHA
# Program for testing SPT system. This is the single-threaded version.
# Sends a request to the SPT, awaits the asynchronous response, repeats.
# With -P (performance) option, it functions either as a Feeder or Listener.
#
#
# To Do:
# sptreq(5)
# automatic (-a)
# encrypted (-x) -- make it work
# interactive (-i) -- revise and extend
# improved ^C handling while awaiting asynchronous response
# one ^C to stop at end of current sequence, two to stop right now
# ...
use warnings;
use strict;
use Data::Dumper;
use Carp;
use Getopt::Std;
use Readonly;
use File::Slurp qw(slurp);
use FindBin;
use List::Util qw(first);
use lib "$FindBin::Bin:$FindBin::Bin/lib";
use SPT::Times qw(now);
use SPT::Logger qw($error_count log_info log_debug log_error log_str);
use SPT::Sequence qw($STATI $REQUESTS send_seq);
use SPT::Feeder qw(listen1);
use SPT::Response qw($response_count);
use SPT::Configuration;
#use SPT::User;
#configuration parameters
Readonly my $LOCKFILE => '/tmp/sptest.lock';
#global variables
$0 =~ s&.*/&&; #basename of this perl script
our $id; #RCS identifier
#local variables
my %opt = (); #command-line options
my $errcnt = 0; #count of input files which couldn't be read
my @requests; #array of anon arrays with filenames, contents reference
my $wrote_lockfile = 0; #we wrote the lock file, so we should delete it
my $sent_count = 0;
my $stop_requested = 0; #set by _shutdown() to request orderly shutdown
#- this bit of trickery lets us include the RCS header in the output
my $dollar = "\$";
$id = "\$Header: /export/home/naz/bin/RCS/sptest,v 1.30 2006/12/14 15:38:54 naz Exp naz $dollar";
$id =~ s/^\$Header:\s*//o;
$id =~ s/\s*\$$//o;
# E N D
# This ensures that any error writing stdout (e.g., disk full) will be reported.
END {
close(STDOUT) || croak "$0: can't close stdout: $^E";
if ($wrote_lockfile) {
unlink $LOCKFILE;
}
} # END()
# _ u s a g e
# Dump an usage summary plus the list of possible items, and exit with error.
sub _usage {
print STDERR "$0 -- an outline for perl scripts\n";
print STDERR "Use 'perldoc $0' to see the manpage\n";
print STDERR "$id\n";
#presumably the lockfile hasn't been created yet
exit 2;
} # _usage()
# _ s t a t s
# Log a line giving elementary statistics.
sub _stats {
my $seconds = now() - $^T;
my $average = $sent_count / $seconds;
log_info($error_count, ' errors detected');
log_info(
sprintf "%u requests sent in %.03f seconds; average %.1f/second",
$sent_count, $seconds, $average
);
} # _stats()
# _ s h u t d o w n
# Close down things tidily, in particular closing log files.
# Invoked when nearly any signal is caught.
# SIGINT (^C) causes a soft landing, stopping at the next convenient
# opportunity, such as at the end of a sequence.
# Any other signal, e.g., SIGQUIT (^|) or SIGTERM (default sent by kill),
# causes an immediate abort.
# NOTE that END can only be called automatically. See Cookbook recipe 12.7
# See Cookbook recipe 16.17.
sub _shutdown {
#print STDERR "_shutdown(@_)\n";
my $name = shift;
if ($name eq 'INT') {
$SIG{INT} = \&_shutdown;
print STDERR "sptest: SIGINT caught; will stop when convenient\n";
++$stop_requested;
## do something special if multiple ^C's are received???
}
else {
log_str("\n");
log_info("caught signal SIG$name; shutting down");
log_info("hard shutdown by user after sending $sent_count requests");
if ($opt{v} && !$opt{i}) {
_stats ($sent_count);
}
sleep 2;
exit 1;
}
} # _shutdown()
# _ a d j u s t
# Given a filename, adjust it be removing any leading/trailing whitespace,
# and pre-pending $opt{B} if it is a relative pathname.
# Returns the adjusted filename.
sub _adjust {
my $file = $_[0];
$file =~ s{\s*(.+?)\s*$}{$1}xms; #lose surrounding whitespace
if (defined $opt{B} && '/' ne substr($file, 0, 1)) {
$file = "$opt{B}/$file";
}
#print STDERR "#$file = _adjust($_[0])\n";
return $file;
} # _adjust()
# _ g e t f i l e
# Slurps in the entire contents of the specified file,
# pushing the name and a reference to the contents onto global @requests.
# If the file is empty or somehow unreadable, complain and increment
# the global $errcnt.
sub _getfile {
my $file = $_[0];
log_debug(6, "_getfile($file)");
if ('-' eq $file) { #stdin can't be checked
my $contents_ref = slurp( \*STDIN, scalar_ref => 1 );
push @requests, [$file, $contents_ref];
}
elsif (-e $file && -f $file && -r $file && -s $file) {
#my $contents_ref = slurp( (($file eq '-') ? \*STDIN : $file),
my $contents_ref = slurp( $file, scalar_ref => 1 );
push @requests, [$file, $contents_ref];
}
else {
my $basename;
($basename = $file) =~ s&.*/&&;
if ($opt{k}) {
log_debug(2, "#skipping unreadable or empty file '$basename'");
++$errcnt;
next;
}
else {
croak "$0: unreadable or empty file '$basename', aborted\n";
}
}
return;
} # _getfile()
#- handle command-line options
getopts("?AB:C:D:F:IL:O:P:RS:T:Vbcd:ehikl:m:np:qrs:t:vx", \%opt);
$opt{D} = 0 unless defined $opt{D};
@ARGV = ('-') unless @ARGV; #read from stdin when no files given
my $facility = shift; #nominally Lab1, Lab2, or Lab3
if (6 <= $opt{D}) {
my $line;
while (my ($key,$val) = each %opt) {
$line .= " -$key$val";
}
print STDERR "$id\nOptions are: $line\n facility = $facility\n files = @ARGV\n";
}
_usage() if $opt{h} || $opt{'?'};
# sanity checking [Note: can't log_fatal() until Logger::init() has been called]
if ($opt{c}) {
croak "Options -c and -P are incompatible, aborted\n" if $opt{P};
croak "Options -c and -n are incompatible, aborted\n" if $opt{n};
croak "Option -c is disallowed on production, aborted\n"
if 'production' eq lc($facility);
}
croak "Option -T with value 0 is not allowed, aborted\n"
if defined($opt{T}) && 0 == $opt{T};
croak "Option -F requires a positive integer, aborted\n"
if defined($opt{F}) && $opt{F} !~ m{\A \d+ \z}xms;
if ( defined $opt{n} # dry run only
|| ($^O =~ m{win}xmsi) # Windows is single-user anyway
|| (defined($opt{P}) && $opt{P} eq 'f')) { # feeder-only; no listener
$wrote_lockfile = 0;
}
else {
#check and set lockfile
-f $LOCKFILE && croak "$0: a listener is already running (see $LOCKFILE), aborted\n";
open my $FH, '>', $LOCKFILE
or croak "$0: can't write $LOCKFILE, aborted\n";
print $FH "$0: PID = $$, real UID = $<, effective UID = $>\n";
close($FH) and $wrote_lockfile = 1;
}
#- initialize things
SPT::Logger::init(%opt);
if ($opt{c}) {
require SPT::Checker;
SPT::Checker::init($facility, %opt);
}
SPT::Feeder::init($facility, %opt);
if ((!defined $opt{P}) || $opt{P} ne 'l') {
SPT::Sequence::init(%opt);
}
#SPT::User::init() if $opt{i};
#necessary to handle these signals so that we can close the XML log properly
#otherwise the last 5K will probably go missing (because we don't autoflush it)
#$SIG{INT} = '_shutdown'; #catch ^C and shut down tidily
#$SIG{TERM} = '_shutdown'; # and the default sent by kill too
# This makes sure the exit handlers (END) get invoked for all signals
# see Cookbook recipe 12.7
#use sigtrap qw(die normal-signals error-signals);
use sigtrap qw(handler _shutdown normal-signals QUIT die untrapped error-signals);
if (defined $opt{S}) {
srand $opt{S};
}
#- log configuration
if ($opt{v}) {
my $line = '';
while (my ($key,$val) = each %opt) {
$line .= " -$key$val";
}
#- show command-line options and filenames
log_info(`date` . " \@$id\n \@Options are: $line\n \@Facility = $facility\n \@Directory = $ENV{PWD}\n \@Files = '",
join("', '", @ARGV), "'");
#- display the description of the SPT-IL software on the target facility
if ((!defined $opt{P}) || $opt{P} ne 'l') {
if (eval { require Net::SSH::Perl }) {
my $version_info = get_uut_info($facility)->{'version'};
if ($version_info) {
my ($host, $user, $passwd, $command) = split '\|', $version_info;
my $ssh = Net::SSH::Perl->new($host)
or croak "can't ssh to '$host', aborted\n";
$ssh->login($user, $passwd)
or croak "can't login to '$host', aborted\n";
my ($description, $err, $stat) = $ssh->cmd($command);
if ($stat || $err) {
log_debug(1, "can't get description, status='$stat', err='$err'");
$description = 'UNKNOWN';
}
$description =~ s{ \s*</?description> }{}xmsg;
chomp $description;
log_info("Facility $facility is running with software '$description'");
}
}
else {
log_info("software version unknown because Net::SSH::Perl not found");
}
}
}
#- listen-only when -Pl given
if (defined($opt{P}) && $opt{P} eq 'l') {
my $resp_cnt;
my $first_time = 0; #unix second when first resp received
my $last_time = 0; #unix second when last resp received
for ($resp_cnt = 0 ; 1 ; ++$resp_cnt) { #terminating condition ?????
log_str("\n"); #blank line between entries
my $retval = SPT::Feeder::listen1();
$last_time = time;
if (!$first_time) {
$first_time = $last_time; #this is the first response ever
}
last if $retval eq 'TIMEOUT';
last if !$opt{k} && $retval eq 'RCVERR';
last if $stop_requested; #^C
}
if ($opt{v}) {
#my $seconds = (time - $opt{T}) - $^T;
my $seconds = $last_time - $first_time;
my $average = $response_count ? $seconds / $response_count : 0;
# don't use _stats() here because we didn't send anything
log_info("$response_count responses received in $seconds seconds\n",
sprintf " \@average is %.3f seconds per response", $average);
}
exit 0;
}
#- read test sequences from the input files (and cmd line) into @requests
my @cmdline_sequence = ();
for my $arg (@ARGV) {
#log_debug(7, "checking input parameter '$arg'");
#- open input file
last if $stop_requested; #^C
if ($arg eq '-') { #read XML from stdin
_getfile('-');
}
elsif ('--' eq $arg) { #grab filenames from stdin
while (<STDIN>) {
#_getfile(_adjust($_));
s{\s*(.+?)\s*$}{$1}xms; #lose surrounding whitespace
_getfile($_);
}
}
elsif (-d $arg) { #file is really a directory
for (glob("$arg/*")) { #process all regular files in directory
_getfile($_) if -f $_;
}
}
elsif (-d _adjust($arg)) { #directory in $opt{B}
$arg = _adjust($arg);
for (sort glob("$arg/*")) {
_getfile($_) if -f $_;
}
}
elsif (-f $arg) { #normal file
_getfile($arg);
}
# else { #file in $opt{B}
# _getfile(_adjust($arg));
# }
elsif (-f _adjust($arg)) { #file in $opt{B}
_getfile(_adjust($arg));
}
elsif ($arg =~ m{\A\s* (?:(?:$STATI)\s*=\s*)? <[-\w]+:Envelope}xmso) {
push @cmdline_sequence, ['---', \$arg]; #XML directly on command line
}
elsif ($arg =~ m{\A\s* (?:(?:$STATI)\s*=\s*)? (?:$REQUESTS)\b}xsmo) {
push @requests, ['---', \$arg]; #parameters directly on command line
}
else {
if ($opt{k}) {
carp "$0: bad parameter '$arg', ignoring\n";
}
else {
croak "$0: bad parameter '$arg', aborted\n";
}
}
}
if (@cmdline_sequence) {
push @requests, @cmdline_sequence;
}
undef @cmdline_sequence;
log_debug(2, scalar(@requests), " sequences read\n");
if ($errcnt and !$opt{k}) {
croak "$0: $errcnt empty or unreadable files, stopped\n";
}
if (!@requests && !$opt{i}) {
croak "$0: nothing to test, aborting\n";
}
#- if wildcards are present, data will need to be generated, so initialize it
#map { print "filename=@{$_}[0], contents=>>${@{$_}[1]}<<\n" } @requests;
#if (first { m{(?:\*|!)}xms } #first wildcard line in
if (first { m{[*!]}xms } #first wildcard line in
grep { ! m{^\s*\#}xms } # non-comment lines in
map { split '\n', ${@{$_}[1]} } # list of lines in
@requests) { # all sequences
log_debug(2, "wildcard(s) detected, firing up generator");
require SPT::Generate;
SPT::Generate::init($facility);
}
# NOTE that if we decide to offer shuffle capability,
# the best way to do it is with List::Util::shuffle()
#- wait for the designated start time
if ($opt{s} && !$stop_requested) {
if ('+' eq substr($opt{s}, 0, 1)) {
$opt{s} += $^T; #make target time absolute
}
if (time() < $opt{s}) {
my $snooze_time = $opt{s} - time();
log_info 'waiting ', $snooze_time, ' seconds' if $opt{v};
sleep $snooze_time;
} else {
my $overshoot = time() - $opt{s};
log_error "missed $opt{s} start target by ", $overshoot, ' seconds';
}
}
#- send sequences
#NOTE that all steps in a sequence are always sent, in order,
#even if we have to exceed the maximum count (-m) to do so.
my $index = $opt{r} ? rand(@requests - 1) : 0; #first test to send
my $st = time();
if (defined($opt{F}) && 0 < $opt{F}) {
log_info("skipping first $opt{F} requests");
}
while (1) {
my $filename = $requests[$index][0];
last if $stop_requested;
log_debug(4, "$0 sending $filename:$index len=",
length ${$requests[$index][1]});
if (defined($opt{F}) && 0 < $opt{F}) {
log_debug(3, "skipping $filename:$index");
--$opt{F};
}
else {
$sent_count += SPT::Sequence::send_seq($requests[$index][1], $filename);
}
last if !defined($opt{m}) && $#requests <= $index; #default is 1
last if $opt{m} && ($opt{m} <= $sent_count); #-m0 => forever
last if defined($opt{t}) && ($opt{t} <= (time() - $st)); #time limit
$index = $opt{r} ? rand(@requests - 1) #next test
: (($index + 1) % scalar(@requests));
}
log_str("\n");
if ($stop_requested) {
log_info ("soft shutdown by user after sending $sent_count requests");
}
else {
log_info ("terminated normally after sending $sent_count requests");
}
if ($opt{v} && !$opt{i}) {
_stats();
}
#- clean up and exit
exit (0 < $error_count);
=head1 NAME
sptest - the SPT automatic tester
=head1 SYNOPSIS
sptest [B<-?>|B<h>] [B<-A>] [B<-B>I<dir>] [B<-C>I<CIDprefix>] [B<-D>[I<level>]] [B<-F>I<skipcount>] [B<-I>] [B<-L>I<xmllog>] [B<-O>I<datafile>] [B<-P>I<f|l>] [B<-R>] [B<-S>I<seed>] [B<-T>I<timeout>] [B<-V>] [B<-b>] [B<-c>] [B<-d>[B<s>|B<r>][B<=>|B<~>]I<delay>] [B<-e>] [B<-i>] [B<-k>] [B<-l>I<logdir/file>] [B<-m>I<max>] [B<-n>] [B<-p>I<prefix>] [B<-q>] [B<-r>] [B<-s>(I<startTime>|B<+>I<delta>)] [B<-t>I<seconds>] [B<-v>] [B<-x>] [I<Facility> {F<infile>|F<dir>|B<->|B<-->|I<request>...}]
=head1 DESCRIPTION
This tool facilitates testing of the Swisscom Fixnet SPT system. It is
designed to be useful for interactive, automatic, and regression testing.
It communicates with the SPT-IL by means of SOAP (XML) messages.
The default mode is to send one request and await the asynchronous response
before continuing on to the next request.
By default logging information is written to the standard output
as well as to file F<spt.log> in the current directory.
The essential data for all requests, receipts, and asynchronous responses
are logged in a condensed form.
By default, all XML is logged to F<xml.log> in the current directory.
In general, for any logging file, if the target file already exists
it will first be renamed to I<file>B<.bak>, and then the new target
file will be created empty. In general, logging can be disabled by
specifying F</dev/null> as the logfile.
For I<feeder> (B<-Pf>) and I<listener> (B<-Pl>) modes, the default logging
is different. This is to minimize the chances of both feeder and listener
writing to the same log files. By default the normal logging is only to
standard output, and there is no XML logging.
The optional parameter I<Facility> is the system to be tested, one of:
I<1>, I<2>, I<3>, I<Lab1>, I<Lab2>, I<Lab3>, I<E2E1>, or I<E2E2>.
It is required unless B<-Pl> is given. Upper/lowercase doesn't matter,
and a single digit is shorthand for the associated Lab.
The remainder of the command line is used to specify SOAP requests.
These requests are packaged together into sequences. A file is defined
to be a sequence, containing one or more SOAP requests. Files can be
specified individually, or by directory, in which case all files in
the directory are used in alphabetical order.
With file F<--> a list of filenames is read from standard input, one per line.
File F<-> means that SOAP requests should be read from standard input.
All requests read in thus are considered to be steps in one sequence,
identified as B<-> in the logfile.
It is also possible to give the SOAP requests right on
the command line, complete with optional expected status.
The entire SOAP message (plus optional status) must be a I<single> parameter
on the command line, and it must follow the facility (1st parameter).
All such requests on the command line are considered as steps of one sequence,
identified as B<---> in the logfile.
By default all sequences are processed just once in the order specified.
With the B<-m> option you can set the maximum number of messages to be
sent, whereby 0 means no limit.
With the B<-r> option the sequences can be sent in a random order.
Note that, within a sequence, all steps are always sent in the given order.
Input files can contain either SOAP (XML) definitions, or they can
contain just the parameters required to generate SOAP requests, one line
of parameter information per request.
Each input file is a test sequence, consisting of one or more steps (test
messages). Test sequences are lists of requests
which are always sent together in the order in which they appear.
Input files
can also contain comment lines, defined as a line whose first non-blank
character is a B<#>. Blank lines are of course also allowed.
Normally, all input filenames are sanity-checked before any processing is done.
If any are unreadable, empty, or otherwise unusable, then processing
terminates immediately unless B<-k> has been given.
NOTE that only one listener can be active at a time on a machine.
By judicious use of B<-P> and B<-A> options, this restriction can be
worked around.
When B<sptest> is running, sending it a SIGINT (^C) causes it to
perform an orderly shutdown at the next convenient opportunity. In
listening mode, that means after the next asynchronous response has
been received and processed. Otherwise it means after the end
of the current sequence. The SIGQUIT (^|) signal causes an immediate
semi-orderly shutdown. Any other signal aborts the program rudely.
=head1 SOAP
A SOAP message can be given either as the full-blown XML, complete with
SOAP envelope, or as a line of parameter values. In the latter case,
B<sptest> uses the parameter values to construct the complete SOAP message
on the fly. Aside from making the input more tractable, it also offers
the advantage that some parameters can be replaced at runtime with
appropriate values. Here are the possibilities:
=over 4
=item B<%now>
The current date and time
=item B<%yesterday>, B<%today>, B<%tomorrow>
The appropriate date and time
=item B<%%>
The previous value of this parameter. For example, if you have sent a
createSubscriberService request, then on the next line you can delete that
new subscriber by executing a deleteSubscriberService using B<%%> for each
of the parameters.
=item B<*>
A random, valid, value.
=item B<!>
An invalid value, picked at random, which should cause the operation to fail,
always with the same error status.
=item B<null>
This parameter is not output.
=back
Note that parameters are delimited by whitespace (and/or commas),
so if you have a parameter which contains whitespace (such as the
relay-agent-circuit-id of the reportLeaseStateChange request), then
you will have to either quote the entire parameter, or quote the
whitespace within the parameter. This is true no matter how the
request is input; from a file, over stdin, or directly on the
command line.
If a parameter is missing altogether, then this is treated as a B<*>
by the generator, and as a B<null> by the XML creator. This allows
you to do, for example:
sptest 2 'reportLeaseStateChange *'
and all the parameters will be chosen by the generator. Note that B<sptest>
needs to see at least one wildcard (*) so that it knows it has to start up the
generator. The generator is normally not started because it takes a while.
So the bottom line is that if you have any wildcards in any of your input
files, then any missing parameters will be treated as the B<*> wildcard.
Otherwise, they will be handled the same as 'null'. If you're uncertain
how missing parameters will be handled, just specify all parameters.
=head1 STATUS
Each SOAP message can optionally be preceded by something like B<OK = >.
This means that the expected result is success. The complete
list of statuses which can be expected is:
# message could not be sent
SYNTAX #SOAP syntax is No Good, detected by sptest
SEMANTIC #SOAP semantics are NG (e.g., bad operation)
NOP #not sent (-n)
# message was sent, but no asynch response is expected
SYNTAX #SOAP syntax is No Good, detected by SPT-IL
SOAPFAULT #SOAP problem detected by WebSphere
NACK #rejected by SPT-IL
LSC_OK #Lease State Change report (receipt), success
LSC_TE #Lease State Change report, Temporary Error
LSC_PE #Lease State Change report, Permanent Error
ANNOUNCEMENT #no synchronous response requested
# status of the asynchronous response
TIMEOUT #no asynch response from SPT-IL
RCVERR #other receive failure
IA #Inconsistency Announcement
ERRANT #asynch resp from some other request received
UNKNOWN #unrecognized receipt or response was received
TECH #Technical Exception
FAIL #non-zero code
nnnnnn #6-digit result code
TRUNC #result is incomplete (partial success)
0 #that's a zero digit, which is the same as OK
OK #success
=head1 OPTIONS
=over 4
=item B<-?>|B<-h>
Give a two-line summary of this program and how to obtain more
information about it.
=item B<-A>
All messages are sent as announcements.
This can be useful if you are testing just the queueing speed.
If B<-R> is used, then only the interaction-type attribute is
modified.
=item B<-B>I<dir>
F<dir/> is prefixed to any relative filenames that aren't otherwise found.
As an added bonus, it will be subsequently stripped in the log entry where
the filename is used to identify the location of the request.
Note that this also works for directories, so you can say, for example:
C<sptest -Bxml/mpm lab2 good bad>
to get all the files found in F<xml/mpm/good> and F<xml/mpm/bad>.
=item B<-C>I<CIDprefix>
Prefix the automatically-generated invoker-id with I<CIDprefix>.
This is mostly useful with B<-Pf> when multiple instances of sptest
are running.
By invoking each one with a different I<CIDprefix>, you can ensure
that each invoker-id is indeed unique.
=item B<-D>I<level>
Set the highest level of debugging that will be output; 0 disables.
Following levels are suggested:
=over 23
=item B<0> debugging disabled
=item B<1> exceptional condition
=item B<2> unusual condition
=item B<3> major state change
=item B<4> minor state change
=item B<5> detailed info
=item B<6> paranoia
=back
=item B<-F>I<skipcount>
Skip over the First I<skipcount> requests.
This can be useful if a large dataset somehow stops prematurely.
With the B<-F> option you can simply re-start the same dataset,
but skip the ones that have already been completed.
=item B<-I>
Timestamps in the standard logfile are shown as Interval (time since the
previous log entry) instead of the time of day.
=item B<-L>I<xmllog>
All XML sent and received is logged to F<xmllog>.
If F<xmllog> is an existing directory, write to file F<xml.log> in that
directory. Otherwise F<xmllog> is the pathname to write the XML to.
By default, all XML is logged to F<xml.log> in the current directory; to
disable this, you can, for instance, specify B<-L/dev/null>.
=item B<-P>I<f|l>
Run I<sptest> in Performance mode, in which case certain
options become invalid, and I<sptest> acts as either a I<feeder>
or a I<listener>, but never both.
In I<feeder> mode (B<-Pf>) no listener is started.
In I<listener> mode (B<-Pl>), only the listener is started,
and any input files mentioned on the command line are ignored.
=item B<-R>
By default most of the operational parameters (e.g., time) are replaced
by this program. With B<-R> this is not done, the XML is sent exactly as read.
If, however, B<-A> is given, then the interrogation-type attribute may
be modified.
=item B<-S>I<seed>
Initialize the pseudo-random number generator with this seed.
=item B<-T>I<timeout>
If ever I<timeout> seconds passes without receiving any expected
responses, we consider the input as timed out. Normally this terminates
the program, but with the B<-k> flag we continue with the next message.
By default this timeout is 24 hours. 0 is not a valid timeout value.
=item B<-V>
ERRORS, WARNINGS, and DEBUGGING logfile entries are also written to stdout.
=item B<-b>
Send all MPM requests (create/modify/deleteSubscriberService)
at priority level 1 (bulk) instead of the default 2.
=item B<-c>
Check that the results are as expected in the various subsystems.
This assumes synchronous operation (no B<-P> option given).
ONLY PARTIALLY IMPLEMENTED!!!!!!!!!!!!!!!!!!!!!!
=item B<-d>[B<s>|B<r>][B<=>|B<~>]I<delay>
Delay between requests, in milliseconds. With B<s> (default),
the I<delay> is measured from the time that the previous request was sent,
whereas with B<r> it is measured from the reception of the last response.
With B<=> (default), I<delay> is the delay time in milliseconds,
whereas with B<~> it is I<average> delay time, with the maximum being
twice the average.
=begin comment
This is the average time
between the sending of one request and the sending of the next. The maximum
time will be twice the average and the minimum will be no delay at all.
Value B<0> (zero) has the special meaning that the next request will not
be sent until the I<asynchronous> response has been received. This may
be necessary when B<-R> is given, to avoid collisions with the correlation ID.
When B<-Pf> is given, there is by default no delay between transmissions.
Otherwise, the default is 0, meaning that the next transmission doesn't
occur until the asynchronous response comes.
NOTE that checking (B<-c>) implies (B<-d0>) since the checking cannot be
performed until the asynchronous response has been received. Although possible,
using B<-c> and B<-d> together is dubious at best.
In the current single-threaded implementation, by default we wait for the
asynchronous response before thinking about sending the next request.
When we are ready to send the next request, we can wait for a random
number of milliseconds whose average is specified with the B<-d> option.
Note that this delay is measured from when the previous request was sent,
I<not> when the asynchronous response was received.
=end comment
The delay is mostly useful in the feeder mode B<-Pf>, where we don't
await the asynchronous response, e.g., for load testing.
=item B<-e>
Stop on Error. At present this can only happen when an expected result
has been given (see B<STATUS> above).
=item B<-h>
Issue a short usage summary and quit.
=item B<-i>
Run in Interactive mode.
At the moment this simply pauses before sending each step.
Execution continues when the user hits Enter.
=item B<-k>
Keep going in spite of (non-fatal) errors.
=item B<-l>F<log>
When F<log> is the name of an existing directory, then the logfile is
written there with the name F<sptI<YYYYMMDDHHMMSS>.log>,
where I<YYYYMMDDHHMMSS> is the current date and time.
Otherwise I<log> is taken to be the name of the logging file.
In either case, if it already exists the destination file will be truncated,
otherwise it will be created.
If B<-l> is not specified, output is to the standard output and (assuming
B<-Pf> is not given) to F<spt.log>.
=item B<-m>I<count>
The maximum number of requests to send. If zero (0) then the tests
run forever. Request specification files are repeated as necessary.
Ignored in interactive mode (B<-i>).
=item B<-n>
Don't actually output anything to the SPT, but perform as much of the
usual processing and logging as feasible.
This implies B<-Pf> (don't start up a listener).
This can be useful for debugging, for checking XML file syntax,
and for getting an idea of how fast all the non-WebServices processing is.
=item B<-o>[I<datafile>]
Additionally output all request-specific lines to F<datafile>.
Each entry in I<datafile> consists of a comment line with the operation name
and location or (non-null) query parameters (B<-q), followed by zero or more
lines of request-specific data, followed by a blank line.
=item B<-p>I<prefix>
The I<prefix> that they're using in the lab today, e.g., B<SPTIL2_>
=item B<-q>
In the second line of each log entry, instead of showing the location of
the query, show the query parameters directly.
=item B<-r>
Send requests in Random order. Actually, more correctly, send
sequences in Random order.
Requests within a sequence are always sent in the order given.
=item B<-s>I<startSecond>|B<+>I<deltaSeconds>
Wait until second I<startSecond> before actually sending any requests.
This is intended for batch operation where multiple instances are
being lined up to perform load testing (i.e., B<-Pf> was given).
When the time is given as a delta time (e.g., B<-s+5>), then the startSecond
is set to be the current time plus the given delta time, in seconds.
=item B<-t>I<secs>
Terminate after I<secs> (measured from the start of sending), no matter what.
=item B<-v>
Increase the verbosity level.
=item B<-x>
Use SSL to encrypt the SOAP communications.
Implemented, but won't work until the appropriate modules can be installed.
=back
=head1 EXAMPLES
=over 4
=item C<sptest 2 delete.xml>
The SOAP request file contained in F<delete.xml> is sent to the SPT
in Lab2, the asynchronous response is awaited, and information is logged
and written to stdout.
F<spt.log> contains information about the request and the asynchronous
response, and F<xml.log> contains the details of all the XML which was
exchanged between I<sptest> and the SPT.
=item C<sptest 2 'OK = deleteSubscriberService 1234567890 100008'>
The SOAP request name and parameters have been given on the command line
directly. Note that the entire SOAP request must be quoted so that
the shell hands it to B<sptest> as one command-line parameter.
The expected result (OK) has also been given; any deviation
from this will be considered an error which will stop the testing
if B<-e> has been given.
=item C<sptest 2 'LSC_PE = reportLeaseStateChange foo bar'>
In addition to the SOAP request and parameters, the expected result (LSC_PE)
has also been given on the command line. This result is considered
the correct result, and any other result causes the program to stop
if the B<-e> option has been given.
Note that the expected result as well as the request name are case-sensitive,
and if either is incorrect, the entire command-line parameter will be
treated as a filename.
=item C<sptest -q -B xml/regression Lab1 positive negative>
All files (sequences) in directories F<./xml/regression/positive> and
F<./xml/regression/negative> will be sent to Lab1 once.
The -q option causes the parameters to be listed in second line
of each log entry instead of a reference to the sequence:step.
=item C<fgrep -l getNetwork xml/wsg/*/* | sptest LAB1 -->
The fgrep produces a list of filenames containing getNetworkDefinition
and getNetworksUtilizations SOAP messages. B<sptest> takes this list
of files as input and sends them to Lab1.
=item C<sptest -Pl -T66 -L/tmp/incoming.log -l- lab1>
Runs as a listener for asynchronous responses from lab1, logging information
only to stdout. XML is logged to F</tmp/incoming.log>.
The program exits when 66 seconds has passed without
hearing anything from the SPT.
=item C<sptest -Pl LAB1 | tee /tmp/listener.log>
Runs as a listener for asynchronous responses from lab1.
XML is not logged. The standard logging is written to stdout
and is also captured in F</tmp/listener.log>, which will be
truncated if it already exists at program startup.
=item C<sptest -Pf -l/tmp -s987654321 -I -R -A -t99 lab1 -->
Runs as a feeder, reading from stdin the names of files containing XML
and/or parameter data, and feeding the requests to the SPT in Lab1.
Doesn't start sending until unix second 987654321 (seconds since 19700101)
rolls around; this is useful when you want to have multiple feeders
starting up together.
Stops after 99 seconds have passed (since the start of sending).
All messages are sent exactly as they are, except that they are
all sent as announcements. Normally selected operation attributes
are fixed to be "more correct".
Logging is to F</tmp/spt.log>, with nothing on standard output.
All log entry timestamps (except for the first) are replaced with delta
seconds since the previous entry.
There is no XML logging
=item C<sptest -Pf -l/dev/null -s+5 -Cxyz1 -m0 -Lxyz1.xml LAB1 ->
Run as a feeder, taking input from standard input.
The test sequence read from stdin is output in a loop forever.
Once all the setup is accomplished (including reading everything from stdin),
B<sptest> will sleep for 5 seconds before sending any SOAP messages.
The invocation ID's will contain the string 'xyz1'.
Standard logging is suppressed, but XML is written to F<./xyz1.xml>.
=item C<sptest -Bxml/rel2 -r -T23 -S14 -m999 Lab2 test1 test2>
Synchronously sends all sequences in F<xml/rel2/test1> and F<xml/rel2/test2>,
in random order, to Lab2.
The results are logged on stdout and in F<spt.log>, with XML
logging to F<xml.log>. The random seed is set to 14, so that the
run is completely repeatable. In the logging output, all filenames
have the leading F<xml/rel2/> removed, making the log easier to read.
Continues until 999 messages have been sent.
=item C<echo '0=reportLeaseStateChange *' | sptest -m0 -d1000 -Ice 3 ->
Generate random valid reportLeaseStateChange requests, and send them
to Lab 3 at an average rate of 1 per second. In the log files,
the timestamp will be the delta-time since the previous logfile entry.
The return status is expected to be 0 (== LSC_OK).
The results in the DB and LDAP will be checked for correctness.
The program runs forever, but halts in the event of any error.
Note that at present it is necessary to have at least one wildcard
character in the input if you want parameters to be generated automatically.
=back
=head1 RETURNS
=over 4
=item 0
for success
=item 1
for non-fatal errors
=item 2
for fatal errors such as bad command line
=back
=head1 FILES
=over 4
=item F<spt.log>
The default logging file, changed with B<-l>.
=item F<xml.log>
The default XML logging file, changed with B<-L>.
=item F<check.log>
At the moment this is created whenever B<-c> or the generator (wildcards)
are used. This could change. It details communications with the various
subsystems such as the database and LDAP servers. This serves mostly
for B<sptest> debugging.
=item F<datafile>
An optional (-o) file containing the request parameters plus
the request-specific data lines.
=item F<infile>
An input file of some sort.
=item F<->
Stands for standard input.
=item F<-->
Stands for a list of filenames to be read from standard input,
one filename per line.
=item F<--->
Stands for SOAP requests read directly from the command line.
=back
=head1 SEE ALSO
xmlgen, seqgen
=head1 BUGS
For sure.
=head1 AUTHOR
Norman H. Azadian
=cut