How can I use Perl's SOAP::Lite with ZXTM's Control API?

ZXTM 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 ZXTM's SOAP methods and use SOAP enumerations and structures with SOAP::Lite.

Although we'll concentrate on using SOAP::Lite with ZXTM's Control API, the principles can be applied to other SOAP applications as well.

Overview

  • Calling SOAP methods: Play fast and loose, invoking 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, no type checking, but if you create a Perl hash with the correct members, SOAP::Lite will wrap it up and send it to your SOAP server. If you get the types right, all will work. If you don't, it's time to haul out your debugger (or +trace => debug).

  • Using SOAP enumerations: This is the bit where most people give up and do something else, but (at least in the case of ZXTM's Control API), a working Deserializer is at hand.

If this begins to give you a glimmer of confidence, read on...

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.

    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();

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 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 correctly 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::Deserializer';
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::Deserializer';
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 ZXTM admin server
my $admin_server = 'https://username:password@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";

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";

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::Deserializer';
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 ZXTM admin server
my $admin_server = 'https://user:password@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 ) = @_;
die ref $res?$res->faultstring:$conn->transport->status; } );
# 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...

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

Owen Garrett [Zeus Dev Team] 17 July 2009 Bookmark with del.icio.us Post this article to Digg Post this article to reddit Post this article to Facebook Tweet this article  
Leave a comment ...
Your email address will not be displayed.
Your URL will be displayed.
This public messageboard is not a forum for technical support. To report technical support problems, please contact our dedicated Support team using the instructions at the bottom of this page.
Options:
 
(Line breaks become <br />)
(Set cookies for name, email & url)

Recently...

Other Resources