Group
Extension

GRNOC-WebService-Client/bin/wsutil.pl

#!/usr/bin/perl

=head1 NAME

wsutil - a GRNOC webservice testing utility.

=head1 SYNOPSIS

  This is a handy little script to help diagnose a webservice and provide 
  timing of the various stages between initiating a request and actually 
  getting the results back. It offers support for customizing everything 
  that the GRNOC::WebService::Client does.

  Usage:

  wsutil  -U <url> [options]
    or
  wsutil  -S <service_name> 
             [-c <service_cache_file> | -n <name_services>] [options]

  Options:

  -m              which method to run on the remote service. This defaults to 
  --method        "help" if not given.  This will be given as 
                  "method=<input>" when actually calling the remote service.


  -a              what args to pass to the remote service. This should be 
  --args          in the form "foo=1&bar=2&biz=3"


  -u              username to authenticate to the service with. If this is 
  --username      given you will be prompted for a password.
 

  -P              password for service authentication. If a username is 
  --password      provided but no password, the user will be interactively 
                  prompted for a password.


  -r              realm to use. This is required for basic auth and ignored for 
  --realm         CoSign auth.


  -t              timeout in seconds to use for the request.
  --timeout


  -p              use POST instead of GET
  --post          This is a boolean field and does not require an argument.


  -k              enable keepalives
  --keepalive     This is a boolean field and does not require an argument.


  -o              pass raw output from the service instead of trying to 
  --raw           decode it as json This is a boolean field and does not 
                  require an argument. 


  -l              path to cookie file location. If given this will use any 
                  cookies in the file when authenticating to a service and 
                  also save them to this file when done. If not given 
                  cookies are maintained only for the current run.

                  It is probably prudent to make sure that the cookies that 
                  are generated using your own credentials are cleaned up 
                  afterwards or at least not made publicly available.


  -c              service cache file (in case of specifying -S Service Name)
  --servicecache  This file should be in the same format as the grnoc proxy 
                  cronjob creates from the name service.


  -n              name service lookup URL (in case of specifying -S 
  --nameservice   Service Name)    


  -X              do not print the results of the webservice call
  --noresult      This is a boolean field and does not require an argument.


  -b              Takes a numerical argument. Do a batch of requests 
  --batch         instead of a single request. The numerical argument
                  is the number of requests sent. When doing a batch,
                  the individual responses are not displayed (see -X).
                  Instead call turn-around information is printed 
                  and a summary is printed after all the requests
                  have been sent.


  -s              Silent. Do not print responses (see -X) when doing a 
  --silent        single request timing information and summary when 
                  doing a batch of requests.
 

  -d              Takes a numerical argument. When sending a batch of 
  --delay         requests (see -b), pause for that many microseconds
                  between each request. Without delay options, batch
                  requests are sent as fast as possible


  -h              show this message
  --help

=cut







use strict;
use Data::Dumper;
use GRNOC::WebService::Client;
use Getopt::Long qw( :config bundling no_ignore_case );
use Term::ReadKey;
use Pod::Text;
use Time::HiRes qw( gettimeofday tv_interval usleep );


FUNCTIONS: {

  sub usage {
    my $parser = Pod::Text->new(
                                  'sentence'  => 0,
                                  'loose'     => 1,
                                  'width'     => 78,
                                  'indent'    => 0,
                );
    $parser->parse_from_file( $0 );
  }

  sub errorExit {
    my $msg = shift;
    usage();
    print "\n\n  Error:\n  ".$msg."\n\n";
    exit(0);
  }

}


MAIN: {

  #
  # You know, they were just like these variables...
  #
  my  @response_times;
  my  @responses;
  my  $good_requests          = 0;
  my  $error_requests         = 0;
  my  $cur_request_nbr        = 0;


  #
  # Sort out command line options
  #
  my $url;                
  my $service_name;      
  my $method;           
  my $args;            
  my $username;            
  my $realm;         
  my $timeout;      
  my $usePost;     
  my $use_keep_alive;     
  my $raw_output;        
  my $cookies_location; 
  my $service_cache_file; 
  my $name_services;     
  my $suppress_output;  
  my $help;            
  my $password;
  my $batch;
  my $silent;
  my $delay;
  
  my  %opts = (

    'U|url=s'           =>  \$url,
    'S|service=s'       =>  \$service_name,
    'm|method=s'        =>  \$method,
    'a|args=s'          =>  \$args,
    'u|username=s'      =>  \$username,
    'P|password=s'      =>  \$password,
    'r|realm=s'         =>  \$realm,
    't|timeout=i'       =>  \$timeout,
    'p|post'            =>  \$usePost,
    'k|keepalive'       =>  \$use_keep_alive,
    'o|raw'             =>  \$raw_output,
    'l|cookiejar=s'     =>  \$cookies_location,
    'c|servicecache=s'  =>  \$service_cache_file,
    'n|nameservice=s'   =>  \$name_services,
    'X|noresponse'      =>  \$suppress_output,
    'b|batch=i'         =>  \$batch,
    's|silent'          =>  \$silent,
    'd|delay=i'         =>  \$delay,
    'h|help'            =>  \$help,
  
  );

  unless( GetOptions( %opts ) ) {
    errorExit( "Bad command line option [".$!."]" );
  }
 

  #
  # simplest use case
  if( $help ) {
    usage();
    exit(1);
  }

  #
  # Sort out command line options
  #

  #
  # Either URL or Service Name
  unless( $url xor $service_name ) {
    errorExit( "One, and only one of, url or service name must be specified");
  }
  
  #
  # Need a cache file or name service of service_name is specified
  if(
          defined $service_name 
      &&  !defined $service_cache_file 
      &&  !defined $name_services
  ) {
    errorExit( "service_name requires a name_service or a service_cache_file" );
  }
  
  #
  # Verify cache file
  if(
          defined $service_name 
      &&  defined $service_cache_file 
      && !(-e $service_cache_file)
  ) {
    errorExit( "service_cache_file $service_cache_file does not exist" );
  }
  
  #
  # Verify name service
  if(
          defined $service_name 
      &&  defined $name_services 
      && ($name_services !~ /^http/)
  ) {
    errorExit( "name_service must begin with http" );
  }
  
  #
  # handle defaults
  $method   = ( $method || "help" );
  $batch    = ( $batch || 1 );

  #
  # If the user gave us a usename, we will prompt them for a password. 
  # This avoids having to read something off the command or otherwise 
  # expose private data.
  if( $username && !$password ) {
    print "password: ";
    ReadMode('noecho');
    chomp($password = <STDIN>);
    ReadMode('normal');
    print "\n";
  }
  
  #
  # Turn the args string into a perl hash so we can pass it along to the 
  # webservice. Split first based on & then split each subthing by = to 
  # get key/value
  my %argshash = ();
  my @arguments = split /&/, $args;
  foreach my $item (@arguments) {
    my @pair = split /=/, $item;
    my $name  = $pair[0],
    my $value = $pair[1];
    $argshash{$name} = $value;
  }
  
  #
  # In case of using service name, if both cache file and name service are 
  # specified, cache file will be used by default.
  my @name_services_array = ();
  if(
          defined $service_name 
      &&  defined $service_cache_file 
      &&  defined $name_services
  ) {
    $name_services = undef;
    undef @name_services_array;
  }

  if($name_services) {
    push @name_services_array, $name_services;
  }
   
  #print "ARGS HASH ".Dumper( \%argshash );
  #print "URL [".$url."]\n"; #!!!!!

  #
  # Create our client object using all the things we were passed in.
  my $svc = GRNOC::WebService::Client->new(
             'url'                =>  $url,
             'uid'                =>  $username,
             'passwd'             =>  $password,
             'realm'              =>  $realm,
             'timeout'            =>  $timeout,
             'usePost'            =>  $usePost,
             'use_keep_alive'     =>  $use_keep_alive,
             'raw_output'         =>  $raw_output,
             'cookieJar'          =>  HTTP::Cookies->new(
                                        'file'            => $cookies_location,
                                        'autosave'        => 1,
                                        'ignore_discard'  => 1,
                                      ),
            'service_name'        =>  $service_name,
            'service_cache_file'  =>  $service_cache_file,
            'name_services'       =>  \@name_services_array,
            'timing'              =>  1,
  );
  
  #
  # Process the request(s)
 
  my $tmp_response;
  my $tmp_stat;
  while( $cur_request_nbr < $batch ) {

    my $start = [ gettimeofday() ];
    $tmp_response = $svc->$method(%argshash);
    push @response_times, tv_interval( $start, [ gettimeofday() ] );
    

    if( $tmp_response ) {
      push @responses, $tmp_response;
      $tmp_stat = "Good";
      $good_requests++;
    }
    else {
      push @responses, $svc->get_error();
      $tmp_stat = "Error";
      $error_requests++;
    }
    
    if( $batch > 1 && !$silent ) {
      printf(
              "Request [%d] Turn - around (secs) [%3.6f] Stat [%s]\n",
              $cur_request_nbr,
              $response_times[ $#response_times ],
              $tmp_stat,
      );
    }
    if( $delay ) {
      usleep( $delay );
    }

    $cur_request_nbr++;

  }
  
  #
  # Show any summary/final information
  if( $batch == 1 && !$suppress_output ){
    print "\nResponse: ".Dumper(\$responses[ $#responses ])."\n";
  }
  elsif( $batch > 1 && !$silent ) {
    my $min = 99999999999;
    my $max = 0;
    my $total = 0;
    for my $time (@response_times)  {
      $min = ( $time < $min ) ? $time : $min;
      $max = ( $time > $max ) ? $time : $max;
      $total += $time;
    }
    printf( 
            "Total requests sent [%d] Good responses [%d] Errors [%d]\n",
            $cur_request_nbr,
            $good_requests,
            $error_requests,
    );
    printf(
              "Average turn-around time [%3.6f]\n"
            . "Minimum turn-around time [%3.6f]\n"
            . "Maximum turn-around time [%3.6f]\n",
            $total / $cur_request_nbr,
            $min,
            $max
    )
  }
};


Powered by Groonga
Maintained by Kenichi Ishigaki <ishigaki@cpan.org>. If you find anything, submit it on GitHub.