#!/usr/bin/perl use warnings; use strict; use forks; #use threads; ## # Startup definitions ## unless ( defined( $ARGV[0] ) ) { print "st.pl macrofile [concurrent] [total] [sleep] [verbosity] [chartfile]\n"; print "\n"; print " macrofile - The file contain telnet commands\n"; print " concurrent - amount of threads to run conncurrently (default = 1)\n"; print " total - Total amount of runs to complete (default = 1)\n"; print " verbosity - How much stuff to print out, 0-5 (default = 1)\n"; print " sleep - Max time to sleep before starting starting unit test\n"; print " chartfile - To create a chart, specify a file name\n"; print "\n"; exit 1; } my $tmf = $ARGV[0]; unless ( -r $tmf) { print "Could not read macro file $tmf\n"; exit 1; } ### # terminate and intterupt handler ### sub sigterm() { if (threads->tid() == 0) { print "\nAbort request received\n"; &summary(); } else { threads->join(); } } $SIG{TERM} = \&sigterm; $SIG{INT} = \&sigterm; my $concur = (defined($ARGV[1])) ? $ARGV[1] : 1; my $runtotal = (defined($ARGV[2])) ? $ARGV[2] : 1; my $debug = (defined($ARGV[4])) ? $ARGV[4] : 1; my $swait = (defined($ARGV[3])) ? $ARGV[3] : 1; my $chartname = (defined($ARGV[5])) ? $ARGV[5] : undef; #if (defined($chartname) && ! -w $chartname) { # print "warning: could not write to file $chartname\n"; # $chartname = undef; #} my $timeout = 180; my $ctlchars = { '\^E' => "\x05", '\^X' => "\x18", '\^n' => "\x0a", '\^s' => "\x20", # spacebar '%F4' => "\x1b\x4f\x53", '%F11%' => "\x1b\x5b\x32\x33\x7e" }; ## # Load our macro file ## print "$tmf $concur $runtotal\n" if ($debug>2); my @repeats = (); my @cmds = ( ); { my $incmd = 0; open (FH, "<$tmf"); while (<FH>) { chop; if ($_ =~ /^#/) { # Comment next; } elsif ($_ =~ /^$/) { # Blank line next; } elsif ($_ =~ /\.\.\./) { $incmd = 1; } elsif ($incmd == 1) { push (@cmds, $_); } elsif ($incmd == 0 && $_ =~ /^HOST.*$/) { my $parms = { }; foreach my $pairs (split(",", $_)) { my ($name, $value) = split("=", $pairs); $parms->{$name} = $value; } push(@repeats, $parms); } } close (FH); } ## # Start our scheduling ## my @data = (); my $runstart = time(); { my $replen = $#repeats; my $repidx = 0; my $seq = 0; while (1) { my $donework = 0; my @thrlist = threads->list(threads::running); my $thrcount = $#thrlist +1; # Create a new thread if the time is right if ( $thrcount < $concur && $seq < $runtotal ) { $seq++; my $parm = $repeats[$repidx]; my $thr = threads->create({'exit' => 'threads_only'}, \&unittest, \@cmds, $parm, $seq, $thrcount +1 ); my $tid = $thr->tid(); print "thread: $seq $thrcount tid: $tid repidx $repidx\n" if ($debug > 1); # get the next repeat entry $repidx++; $repidx = 0 if ($repidx > $replen); $donework = 1; next; } # Get data back from our threads my @jlist = threads->list(threads::joinable); foreach (@jlist) { my $rd = $_->join(); if (defined($rd)) { push (@data, $rd); print "$rd->{'msg'}\n" if ($debug > 0); $donework = 1; } } # If all our threads are finished, and have been joined # and we have run all sequences, then terminate @thrlist = threads->list(threads::running); $thrcount = $#thrlist + 1; last if ($thrcount == 0 && $seq >= $runtotal && $#jlist == -1); # Give other procs runtime #select(undef, undef, undef, 0.1); # unless ($donework==1) ; } } # Some time to clean up the threads sleep 1; &summary(); ### # Print out our average ### sub summary() { if ($#data <= 0) { print "Not enough data collected\n"; return; } my $sum = 0; my $count = 0; foreach (@data) { $sum += $_->{'dur'}; $count++; } my $avg = ($sum / $count); print "Total Sequences: $count Average Duration: $avg\n"; &chart($avg) if (defined($chartname) && $#data >= 0); } sub chart() { my ($avg) = @_; use Chart::Clicker; use Chart::Clicker::Decoration::Legend::Tabular; use Chart::Clicker::Renderer::Bar; use Chart::Clicker::Context; use Chart::Clicker::Data::DataSet; use Chart::Clicker::Data::Series; # Sort our array of hashes my @sorted = sort { $a->{seq} <=> $b->{seq} } @data; my $cc = Chart::Clicker->new(width => 900, height => 400, format => 'png'); $cc->title->text("Stress Test Results ($concur/$runtotal,$swait)"); $cc->title->font->size(14); # Duration (duration/sequence) my $ser1 = createSeries(\@sorted, 'seq', 'dur', 'Duration'); my $ds1 = Chart::Clicker::Data::DataSet->new(series => [ $ser1 ]); #my $ctx1 = Chart::Clicker::Context->new(name => 'Duration'); #$ctx1->range_axis->label('Duration'); #$ctx1->domain_axis->label('Sequence'); #$ds1->context('Duration'); #$cc->add_to_contexts($ctx1) # Start Time (start/sequence) my $ser2 = createSeries(\@sorted, 'seq', 'delta', 'Start Time'); my $ser3 = createSeries(\@sorted, 'seq', 'procs', 'Proc Count'); my $ds2 = Chart::Clicker::Data::DataSet->new(series => [ $ser2, $ser3 ]); my $ctx2 = Chart::Clicker::Context->new(name => 'Count'); $ctx2->range_axis->label('Count'); $ctx2->domain_axis->label('Sequence_#'); $ds2->context('Count'); $cc->add_to_contexts($ctx2); $cc->add_to_datasets($ds1); $cc->add_to_datasets($ds2); my $defctx = $cc->get_context('default'); $defctx->range_axis->label('Duration_secs)'); $defctx->domain_axis->label('Sequence_#'); $defctx->domain_axis->tick_label_angle(0.785398163); $defctx->renderer->brush->width(1); # Procs (procs/sequence) #my $ds3 = Chart::Clicker::Data::DataSet->new(series => [ $ser3 ]); #my $ctx3 = Chart::Clicker::Context->new(name => 'Procs'); #$ctx3->range_axis->label('Procs'); #$ctx3->domain_axis->label('Sequence'); #$ds3->context('Procs'); #$cc->add_to_contexts($ctx3); #$cc->add_to_datasets($ds3); #foreach (@sorted) { # $ds_dur->add_data( 'Duration', $_->{'dur'} ); #} #use List::Util qw(max min); #use Statistics::Basic qw(median mean); #$cc->legend(Chart::Clicker::Decoration::Legend::Tabular->new( # header => [ qw(Name Min Max Median Mean) ], # data => [ # [ min(&hashv2arr(\@data, 'dur' ) )."", max(&hashv2arr(\@data, 'dur' ) )."", median(&hashv2arr(\@data, 'dur' ) )."", mean(&hashv2arr(\@data, 'dur' ) )."" ], # ] #)); $cc->write_output($chartname); print "Chart $chartname written\n"; } ## # This runs the actual test ## sub unittest() { my ($cmds, $parms, $seq, $num) = @_; if ($seq < $concur) { my $srand = int(rand($swait)); print "sleep $seq $num $srand\n" if ($debug > 1); #select(undef, undef, undef, $srand); } my $data = { }; my $start = time(); $data->{'start'} = $start; $data->{'delta'} = $start - $runstart; $data->{'seq'} = $seq; $data->{'num'} = $num; my @thrlist = threads->list(threads::running); $data->{'procs'} = $#thrlist +1; $data->{'msg'} = "$start $seq $num "; use Net::Telnet (); my $t = new Net::Telnet( Timeout => $timeout, cmd_remove_mode => 1,); my $DH = $t->dump_log('dump/' . $parms->{'DUMP'} . "_$seq.log") if (defined($parms->{'DUMP'})); `mkdir 'dump/'` if (defined($parms->{'DUMP'}) && ! -d 'dump/') ; $t->timeout($parms->{'TIMEOUT'}) if (defined($parms->{'TIMEOUT'})); $t->open($parms->{'HOST'}); $data->{'host'} = $parms->{'HOST'}; $data->{'dump'} = (defined($parms->{'DUMP'})) ? $parms->{'DUMP'} : ""; foreach (@$cmds) { # Replace parms in the cmd foreach my $p (keys(%$parms)) { $_ =~ s/\%$p\%/$parms->{$p}/g; } if ($_ =~ /^\@.*$/) { # Wait $_ =~ s/^@/ /g; my ($tok1, $tok2) = split('~', $_); print "$seq $num wait: $_ ($tok2)\n" if ($debug > 2); $t->waitfor(Timeout => $tok2, Match => $tok1); } elsif ($_ =~ /^\!.*$/) { # Delay $_ =~ s/^!/ /g; print "$seq $num delay: $_\n" if ($debug > 2); select (undef, undef, undef, $_); } else { # send keystroke # Replace control characters print "$seq $num cmd: $_\n" if ($debug > 2); foreach my $cc (keys(%$ctlchars)) { $_ =~ s/$cc/$ctlchars->{$cc}/g; } $t->put($_); } } my $end = time(); my $dur = $end - $start; $data->{'msg'} .= " $dur"; $data->{'end'} = $end; $data->{'dur'} = $dur; return $data; } sub stub() { my ($cmds, $parms, $seq, $num) = @_; my $data = { }; $data->{'start'} = time(); $data->{'seq'} = $seq; $data->{'num'} = $num; my $sleep = int(rand(5)); sleep $sleep; $data->{'end'} = time(); $data->{'dur'} = $data->{'end'} - $data->{'start'}; $data->{'sleep'}= $sleep; $data->{'tid'} = threads->tid(); $data->{'msg'} = "$seq $num $sleep $data->{'start'} $data->{'end'} $data->{'dur'}"; return $data; } sub hashv2arr() { my ($arr, $hash) = @_; my @new = (); my @meh = @$arr; foreach (@$arr) { push (@new, $_->{$hash}); } return @new; } sub createSeries() { my ($data, $key, $value, $name) = @_; my @keys = &hashv2arr($data, $key); my @values = &hashv2arr($data, $value); my $series = Chart::Clicker::Data::Series->new( keys => \@keys, values => \@values, name => $name ); return $series; } #exit 0; #foreach (@repeats) { # &unittest(\@cmds, $_); #} #print "Repeats\n---------------------------------\n"; #my $count = 0; #foreach my $r (@repeats) { # print "$count: "; # foreach (keys(%$r)) { # print "$_=$r->{$_},"; # } # print "\n"; # $count++; #} # #print "\n\nMacro\n---------------------------------\n"; #foreach (@cmds) { # print $_."\n"; #}
HOST=server,USER=newio,PASS=apassword,DUMP=dump ... @/login[: ]*$/i %USER%^n @/password[: ]*$/i %PASS%^n @/%USER%\@%HOST%.*$/i sitemenu.sh^n !0.25 1^n !5 ^n !1 ^n !1 1 !0.25 1 !0.25 1 !0.25 ^E !0.25 ^E !0.25