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:
Call it as follows:
my $conn = SOAP::Lite
-> ns('http://soap.zeus.com/zxtm/1.0/VirtualServer/')
-> proxy("$admin_server/soap");
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,
wroundrobin,
cells,
connections,
wconnections,
responsetimes,
random
}
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:
use SOAP::Lite 0.6;
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 $admin_server = 'https://username:password@host:9090';
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; } );
my $res = $conn->getLoadBalancingAlgorithm( [ $poolName ] );
my $alg = @{$res->result}[0];
print "Pool $poolName uses load balancing algorithm $alg\n";
$conn->setLoadBalancingAlgorithm( [ $poolName ], ['connections'] );
$res = $conn->getLoadBalancingAlgorithm( [ $poolName ] );
print "Algorithm has been changed to @{$res->result}[0]\n";
$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 {
Integer port;
VirtualServer.Protocol protocol;
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:
use SOAP::Lite 0.6;
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 $admin_server = 'https://user:password@hostname:9090';
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; } );
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!