vADC Docs

Tech Tip: using Perl SOAP::Lite with Stingray's SOAP Control API

by on ‎02-25-2013 07:38 AM (1,687 Views)

Stingray provides a SOAP-based Control API, with an interface clearly defined using a collection of WSDL files. Regrettably, Perl's SOAP::Lite implementation completely ignores WSDL specifications, making the task of communicating with a SOAP service much harder than it should be. This article describes how to call Stingray's SOAP methods and use SOAP enumerations and structures with SOAP::Lite.

Overview

  • Calling SOAP methods:  When you call SOAP methods using SOAP::Lite, you invoke the method without any type checking on the SOAP::Lite connection object. SOAP::Lite will make a best effort guess, and it usually gets it close enough that the SOAP server application can figure out what you mean.

  • Using SOAP structures: Again, there's no type checking, and Perl makes a best guess to wrap your structures up and submit to the SOAP server.  Most if the time, things work, but if not, there are techniques to help debug the interaction.

  • Using SOAP enumerations: This is challenging with SOAP::Lite; you can use a general purpose Deserializer to help manage enumeration types.

Control API methods

A method can be invoked from a SOAP::Lite connection object that specifies the appropriate interface:


getVirtualServerNames()



Gets the names of all the configured virtual servers.




Prototype:


String[] getVirtualServerNames()



Call it as follows:


# Create a connection object that uses the VirtualServer interface


my $conn = SOAP::Lite


    -> ns('http://soap.zeus.com/zxtm/1.0/VirtualServer/')


    -> proxy("$admin_server/soap");


  


# You can invoke any of the methods of the VirtualServer interface


my $res = $conn->getVirtualServerNames();


my @names = @{$res->result};



If you need to use several interfaces (for example, VirtualServer and Pool), you will need to construct a SOAP::Lite connection object for each.

If you attempt to invoke a method that does not exist for the interface, the method call will fail and the on_fault fault handler (if specified) will be called.

Control API Structures

A Structure is a complex datatype that contains several parameters. For example, the key configuration settings for a Virtual Server are represented by a VirtualServer.BasicInfo structure that defines the port, protocol and default pool for that Virtual Server:


VirtualServer.BasicInfo



This structure contains the basic information for a virtual server. It is used when creating a server, or modifying the port, protocol or default pool of a server.



struct VirtualServer.BasicInfo {


   # The port to listen for incoming connections on.


   Integer port;




   # The protocol that this virtual server handles.


   VirtualServer.Protocol protocol;



   # The default pool that traffic to this virtual server will go to.


   String default_pool;


}



This structure contains three elements; an Integer (the port number), an Enumeration (VirtualServer.Protocol – the protocol) and a string (the name of the default pool). The method VirtualServer.addVirtualServer() takes a VirtualServer.BasicInfo structure which can be constructed as follows:


my $basicInfo = {


   port         => '443',


   protocol     => 'https',


   default_pool => 'Server Pool 1'


};



$res = $conn->addVirtualServer( [ $vsName ], [ $basicInfo ] );



If you call the method VirtualServer.getBasicInfo(), it will return a corresponding array of VirtualServer.BasicInfo structures that can be unpacked as follows:


$res = $conn->getBasicInfo( [ $vsName ] );


my $r = @{$res->result}[0];



print "Virtual Server $vsName:\n";


print "   port $r->{port}, protocol $r->{protocol}, pool $r->{default_pool}\n";



However, to get this example working end-to-end, you first need to understand how Perl interperts enumeration types (such as the protocol).

Control API Enumerations

An Enumeration is a particular datatype with a restricted, named set of values. For example, a Pool has a limited set of load balancing algorithms that are represented by the Pool.LoadBalancingAlgorithm enumeration. The enumeration is defined as follows:


Pool.LoadBalancingAlgorithm



enum Pool.LoadBalancingAlgorithm {


   roundrobin,     # Round Robin


   wroundrobin,    # Weighted Round Robin


   cells,          # Perceptive


   connections,    # Least Connections


   wconnections,   # Weighted Least Connections


   responsetimes,  # Fastest Response Time


   random          # Random node


}



The Pool interface contains two methods that use that enumeration:


getLoadBalancingAlgorithm( names )



Get the load balancing algorithms that each of the named pools uses.



Pool.LoadBalancingAlgorithm[] getLoadBalancingAlgorithm(


   String[] names


)



setLoadBalancingAlgorithm( names, values )



Set the load balancing algorithms that each of the named pools uses.



void setLoadBalancingAlgorithm(


   String[] names


   Pool.LoadBalancingAlgorithm[] values


)



Perl’s SOAP::Lite library encodes enumerations in SOAP requests, so you can use them in a literal fashion:


$conn->setLoadBalancingAlgorithm( [ $poolName ], ['connections'] );



In a SOAP response, you need to provide a custom Deserializer so that the SOAP::Lite library can convert the values in the SOAP response into appropriate internal representations (i.e. literal strings):


BEGIN {


   package MyDeserializer;


   @MyDeserializer::ISA = 'SOAP:Smiley Very Happyeserializer';



   sub typecast {


      my( $self, $val, $name, $attrs, $children, $type ) = @_;


      if( $type && $type =~ m@http://soap.zeus.com/zxtm/@ ) {


         return $val;


      }


      return undef;


   };


}



my $conn = SOAP::Lite


    -> ns('http://soap.zeus.com/zxtm/1.0/Pool/')


    -> proxy("$admin_server/soap")


    -> deserializer( MyDeserializer->new );



The following code sample illustrates how to use Control API methods that use Enumerations:


#!/usr/bin/perl -w



use SOAP::Lite 0.6;



# Provide our own Deserializer to deserialize enums correctly


BEGIN {


   package MyDeserializer;


   @MyDeserializer::ISA = 'SOAP:Smiley Very Happyeserializer';



   sub typecast {


      my( $self, $val, $name, $attrs, $children, $type ) = @_;


      if( $type && $type =~ m@http://soap.zeus.com/zxtm/@ ) {


         return $val;


      }


      return undef;


   };


}



# This is the url of the Stingray admin server


my $admin_server = 'https://usernameSmiley Tongueassword@host:9090';



# The pool to edit


my $poolName = $ARGV[0] or die "No pool specified";



my $conn = SOAP::Lite


    -> ns('http://soap.zeus.com/zxtm/1.0/Pool/')


    -> proxy("$admin_server/soap")


    -> deserializer( MyDeserializer->new )


    -> on_fault( sub  {


         my( $conn, $res ) = @_;


         die ref $res?$res->faultstring:$conn->transport->status; } );



# Get the load balancing algorithm


my $res = $conn->getLoadBalancingAlgorithm( [ $poolName ] );


my $alg = @{$res->result}[0];


print "Pool $poolName uses load balancing algorithm $alg\n";



# Change the algorithm to least connections, and check it worked


$conn->setLoadBalancingAlgorithm( [ $poolName ], ['connections'] );


$res = $conn->getLoadBalancingAlgorithm( [ $poolName ] );


print "Algorithm has been changed to @{$res->result}[0]\n";



# Now change it back again


$conn->setLoadBalancingAlgorithm( [ $poolName ], [ $alg ] );


$res = $conn->getLoadBalancingAlgorithm( [ $poolName ] );


print "Algorithm changed back to @{$res->result}[0]\n";



Final example

The following code sample illustrates how to create a virtual server and manage the BasicInfo structure:


#!/usr/bin/perl -w



use SOAP::Lite 0.6;



# Provide our own Deserializer so to deserialize enums correctly


BEGIN {


   package MyDeserializer;


   @MyDeserializer::ISA = 'SOAP:Smiley Very Happyeserializer';



   sub typecast {


      my( $self, $val, $name, $attrs, $children, $type ) = @_;


      if( $type && $type =~ m@http://soap.zeus.com/zxtm/@) {


         return $val;


      }


      return undef;


   };


}



# This is the url of the Stingray admin server


my $admin_server = 'https://userSmiley Tongueassword@hostname:9090';



# The virtual server to create


my $vsName = $ARGV[0] or die "No vs specified";



my $conn = SOAP::Lite


    -> ns('http://soap.zeus.com/zxtm/1.0/VirtualServer/')


    -> proxy("$admin_server/soap")


    -> deserializer( MyDeserializer->new )


    -> on_fault( sub  {


         my( $conn, $res ) = @_;




# Construct the basic info structure


my $basicInfo = {


   port         => '443',


   protocol     => 'https',


   default_pool => 'discard'


};



$res = $conn->addVirtualServer( [ $vsName ], [ $basicInfo ] );



$res = $conn->getBasicInfo( [ $vsName ] );


my $r = @{$res->result}[0];



print "Virtual Server $vsName:\n";


print "   port $r->{port}, protocol $r->{protocol}, pool $r->{default_pool}\n";



Where to find out more...

Stingray's Control API Documentation (Stingray Product Documentation) describes how you can communicate with Stingray in a language-independent manner (so long as your application has a SOAP library). The examples above should help kick-start your next Stingray / Perl development project. Good luck!

Read more