by Joe Pruitt, F5 Senior Architect


So, you're ready to start migration of your BIG-IP's to the next generation architecture but for business reasons you are planning on migrating devices over a period of time and will have to support both the older platforms (4.x) as well as the new 9.0. Also, you've deployed an iControl solution to manage your 4.x versions of the products and you heard that in version 9.0 the interfaces are new and improved. Well, this introduces a problem: How do I enhance my existing application (or build a new one) that will support both the legacy interfaces in 4.x as well as the new interfaces in 9.0?


With the upgrade in our interfaces we were well aware of the customer impact this would cause but due to the underlying architecture changes in BIG-IP and the customer requests for performance and usability enhancements, the choice was clear: Make the interfaces better and help customers with the building solutions to fit both environments.


Legacy Methods


Our first step in helping bridge the product gaps, was to include a set of Legacy Interfaces in version 9.0 of the product. These are 100% backward compatible methods that we determined to be the most widely used set of methods in production. This is by no means an exhaustive list but from the data we've accumulated, this accounts for a wide majority of what's being used. The full list of these methods is available in the "iControl SDK v9.0 Release Notes" included in the SDK and also in the article Migrating iControl Applications.


These methods can be used but we are encouraging users to start a migration plan and build mixed-mode applications while managing both versions of BIG-IP systems. Also, the legacy methods will be marked deprecated and supported from a year of the release of BIG-IP v9.0 per the standard iControl deprecation policy (with the one possible exception of the ITCMSystem::SystemInfo interface which is the only way to determine the BIG-IP version number.


The Mixed Mode Application


The concept is fairly straight forward in building a Mixed Mode application. This will take you through this with a step-by-step example written in perl. I'm going to modify the sdk/samples/soap/perl/soaplite/LocalLB/LocalLBPool.pl sample application included in version 9.0 of the SDK to work both on 9.0 and version 4.x of BIG-IP.


Get both versions of the SDK


First you will have to download both versions of the iControl SDK. As of the date of this article, the latest versions are 4.6.2 and 9.0. Extract the contents of these into separate directories for reference to the interfaces.


Declare the interfaces from each version


Each language is different but this example will focus on how to do this in perl. First I'm going to create variables for the following interfaces: ITCMSystem::SystemInfo, ITCMLocalLB::Pool (from 4.x) and LocalLB::Pool, LocalLB::PoolMember (from 9.0).

$ITCMSystemInfo = SOAP::Lite
-> uri('urn:iControl:ITCMSystem/SystemInfo')
-> proxy("$sProtocol://$sHost:$sPort/iControl/iControlPortal.cgi");
$Pool_4 = SOAP::Lite
-> uri('urn:iControl:ITCMLocalLB/Pool')
-> proxy("$sProtocol://$sHost:$sPort/iControl/iControlPortal.cgi");
$Pool = SOAP::Lite
-> uri('urn:iControl:LocalLB/Pool')
-> proxy("$sProtocol://$sHost:$sPort/iControl/iControlPortal.cgi");
$PoolMember = SOAP::Lite
-> uri('urn:iControl:LocalLB/PoolMember')
-> proxy("$sProtocol://$sHost:$sPort/iControl/iControlPortal.cgi");

Query the product version


I'm going to query the BIG-IP's product version using the legacy ITCMSystem::SystemInfo::get_product_info() method.

$version_info = &getBIGIPVersion();
#--------------------------
# sub getBIGIPVersion
#--------------------------
sub getBIGIPVersion()
{
$bigip_version = "Unknown";

$soapResponse = $ITCMSystemInfo->get_product_info();
&checkResponse($soapResponse);

my @ProductInfoSeq = @{$soapResponse->result};
foreach $ProductInformation (@ProductInfoSeq)
{
$product_code = $ProductInformation->{"product_code"};
if ( "BIG-IP" eq $product_code )
{
$product_version = $ProductInformation->{"product_version"};

# Determine if this is 9.0 or 4.x...
if ( $product_version =~ /9/ )
{
$bigip_version = $product_version;
}
else
{
$bigip_version = $product_version;
$bigip_version =~ s/BIG-IP Kernel //g;
$bigip_version =~ s/ Build.+$//g;
}
}
}

# extract major and minor versions
$version_major = $bigip_version;
$version_major =~ s/..+$//g;
$version_minor = $bigip_version;
$version_minor =~ s/^[0-9].//g;

$version_info =
{
full => $bigip_version,
major => $version_major,
minor => $version_minor
};

return $version_info;
}

Use the appropriate interfaces


Now, depending on the major version number, we will either use the methods in the 4.x interfaces or the methods in 9.0.

if ( "9" eq $version_info->{"major"} )
{
# Version 9.0 and above so use 9.0 interfaces
if ( $sPool eq "" )
{
&getAllPoolInfo_9();
}
else
{
&getPoolInfo_9($sPool);
}
}
elsif ( "4" eq $version_info->{"major"} )
{
# Version 4.x so use 4.x interfaces
if ( $sPool eq "" )
{
&getAllPoolInfo_4();
}
else
{
&getPoolInfo_4($sPool);
}
}
else
{
print "Could not determine product version";
}

Conclusion


With little effort, it will be straight-forward to implement an iControl solution that fits into a mixed BIG-IP environment by separating the code into logical groups of version specific functionality.


References




  • Download the iControl SDK to get started today!
  • Have a question? Post it in the DevCentral Forum
  • Full source for sample application:
    #!/usr/bin/perl
    #----------------------------------------------------------------------------
    # The contents of this file are subject to the "END USER LICENSE AGREEMENT FOR F5
    # Software Development Kit for iControl"; you may not use this file except in
    # compliance with the License. The License is included in the iControl
    # Software Development Kit.
    #
    # Software distributed under the License is distributed on an "AS IS"
    # basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
    # the License for the specific language governing rights and limitations
    # under the License.
    #
    # The Original Code is iControl Code and related documentation
    # distributed by F5.
    #
    # The Initial Developer of the Original Code is F5 Networks,
    # Inc. Seattle, WA, USA. Portions created by F5 are Copyright (C) 1996-2004 F5 Networks,
    # Inc. All Rights Reserved. iControl (TM) is a registered trademark of F5 Networks, Inc.
    #
    # Alternatively, the contents of this file may be used under the terms
    # of the GNU General Public License (the "GPL"), in which case the
    # provisions of GPL are applicable instead of those above. If you wish
    # to allow use of your version of this file only under the terms of the
    # GPL and not to allow others to use your version of this file under the
    # License, indicate your decision by deleting the provisions above and
    # replace them with the notice and other provisions required by the GPL.
    # If you do not delete the provisions above, a recipient may use your
    # version of this file under either the License or the GPL.
    #----------------------------------------------------------------------------
    #use SOAP::Lite + trace => qw(method debug);
    use SOAP::Lite;
    BEGIN {push (@INC, "..");}
    use iControlTypeCast;
    #----------------------------------------------------------------------------
    # Validate Arguments
    #----------------------------------------------------------------------------
    my $sHost = $ARGV[0];
    my $sPort = $ARGV[1];
    my $sUID = $ARGV[2];
    my $sPWD = $ARGV[3];
    my $sPool = $ARGV[4];
    my $sProtocol = "https";
    if ( ("80" eq $sPort) or ("8080" eq $sPort) )
    {
    $sProtocol = "http";
    }
    if ( ($sHost eq "") or ($sPort eq "") or ($sUID eq "") or ($sPWD eq "") )
    {
    die ("Usage: LocalLBPool.pl host port uid pwd [pool_name]");
    }
    #----------------------------------------------------------------------------
    # Transport Information
    #----------------------------------------------------------------------------
    sub SOAP::Transport::HTTP::Client::get_basic_credentials
    {
    return "$sUID" => "$sPWD";
    }
    $ITCMSystemInfo = SOAP::Lite
    -> uri('urn:iControl:ITCMSystem/SystemInfo')
    -> proxy("$sProtocol://$sHost:$sPort/iControl/iControlPortal.cgi");
    $Pool_4 = SOAP::Lite
    -> uri('urn:iControl:ITCMLocalLB/Pool')
    -> proxy("$sProtocol://$sHost:$sPort/iControl/iControlPortal.cgi");
    $Pool = SOAP::Lite
    -> uri('urn:iControl:LocalLB/Pool')
    -> proxy("$sProtocol://$sHost:$sPort/iControl/iControlPortal.cgi");
    $PoolMember = SOAP::Lite
    -> uri('urn:iControl:LocalLB/PoolMember')
    -> proxy("$sProtocol://$sHost:$sPort/iControl/iControlPortal.cgi");
    # Determine product version
    $version_info = &getBIGIPVersion();
    if ( "9" eq $version_info->{"major"} )
    {
    # Version 9.0 and above
    if ( $sPool eq "" )
    {
    &getAllPoolInfo_9();
    }
    else
    {
    &getPoolInfo_9($sPool);
    }
    }
    elsif ( "4" eq $version_info->{"major"} )
    {
    # Version 4.x
    if ( $sPool eq "" )
    {
    &getAllPoolInfo_4();
    }
    else
    {
    &getPoolInfo_4($sPool);
    }
    }
    else
    {
    print "Could not determine product version";
    }
    #----------------------------------------------------------------------------
    # checkResponse
    #----------------------------------------------------------------------------
    sub checkResponse()
    {
    my ($soapResponse) = (@_);
    if ( $soapResponse->fault )
    {
    print $soapResponse->faultcode, " ", $soapResponse->faultstring, "";
    exit();
    }
    }
    #----------------------------------------------------------------------------
    # getBIGIPVersion
    #----------------------------------------------------------------------------
    sub getBIGIPVersion()
    {
    $bigip_version = "Unknown";

    $soapResponse = $ITCMSystemInfo->get_product_info();
    &checkResponse($soapResponse);

    my @ProductInfoSeq = @{$soapResponse->result};
    foreach $ProductInformation (@ProductInfoSeq)
    {
    $product_code = $ProductInformation->{"product_code"};
    if ( "BIG-IP" eq $product_code )
    {
    $product_version = $ProductInformation->{"product_version"};

    # Determine if this is 9.0 or 4.x...
    if ( $product_version =~ /9/ )
    {
    $bigip_version = $product_version;
    }
    else
    {
    $bigip_version = $product_version;
    $bigip_version =~ s/BIG-IP Kernel //g;
    $bigip_version =~ s/ Build.+$//g;
    }
    }
    }

    # extract major and minor versions
    $version_major = $bigip_version;
    $version_major =~ s/..+$//g;
    $version_minor = $bigip_version;
    $version_minor =~ s/^[0-9].//g;

    $version_info =
    {
    full => $bigip_version,
    major => $version_major,
    minor => $version_minor
    };

    return $version_info;
    }
    #----------------------------------------------------------------------------
    # getAllPoolInfo_9
    #----------------------------------------------------------------------------
    sub getAllPoolInfo_9()
    {
    $soapResponse = $Pool->get_list();
    &checkResponse($soapResponse);
    my @pool_list = @{$soapResponse->result};
    &getPoolInfo_9(@pool_list);
    }
    #----------------------------------------------------------------------------
    # getPoolInfo_9
    #----------------------------------------------------------------------------
    sub getPoolInfo_9()
    {
    my @pool_list = @_;
    # Get LBMethods
    $soapResponse = $Pool->get_lb_method
    (
    SOAP::Data->name(pool_names => [@pool_list])
    );
    &checkResponse($soapResponse);
    my @lb_methods = @{$soapResponse->result};
    # Get min/cur active members
    $soapResponse = $Pool->get_minimum_active_member
    (
    SOAP::Data->name(pool_names => [@pool_list])
    );
    &checkResponse($soapResponse);
    my @min_active_members = @{$soapResponse->result};
    $soapResponse = $Pool->get_active_member_count
    (
    SOAP::Data->name(pool_names => [@pool_list])
    );
    &checkResponse($soapResponse);
    my @cur_member_list = @{$soapResponse->result};
    # Get Pool Statistics
    $soapResponse = $Pool->get_statistics
    (
    SOAP::Data->name(pool_names => [@pool_list])
    );
    &checkResponse($soapResponse);
    my $pool_stats = $soapResponse->result;
    @PoolStatisticsEntryList = @{$pool_stats->{"statistics"}};
    # Get Member Statistics
    $soapResponse = $PoolMember->get_all_statistics
    (
    SOAP::Data->name(pool_names => [@pool_list])
    );
    &checkResponse($soapResponse);
    my @member_stats = @{$soapResponse->result};
    # Process Data
    $i = 0;
    foreach $pool_name (@pool_list)
    {
    print "POOL $pool_name ";
    print "LB_METHOD @lb_methods[$i] ";
    print "MIN/CUR ACTIVE MEMBERS: @min_active_members[$i]/@cur_member_list[$i]";
    foreach $PoolStatisticsEntry (@PoolStatisticsEntryList)
    {
    $poolName = $PoolStatisticsEntry->{"pool_name"};
    if ( $poolName eq $pool_name )
    {
    @statistics = @{$PoolStatisticsEntry->{"statistics"}};
    foreach $stat (@statistics)
    {
    $type = $stat->{"type"};
    $value = $stat->{"value"};
    $low = $value->{"low"};
    $high = $value->{"high"};
    $value64 = ($high<<32)|$low;
    $time_stamp = $statistics->{"time_stamp"};
    print "| $type : $value64";
    }
    }
    }
    $MemberStatistics = @member_stats[$i];
    @MemberStatisticsEntryList = @{$MemberStatistics->{"statistics"}};
    $MemberStatisticsTimeStamp = $MemberStatistics->{"time_stamp"};
    foreach $MemberStatisticEntry (@MemberStatisticsEntryList)
    {
    $member = $MemberStatisticEntry->{"member"};
    $address = $member->{"address"};
    $port = $member->{"port"};
    print "+-> MEMBER $address:$port";
    @StatisticList = @{$MemberStatisticEntry->{"statistics"}};
    foreach $stat (@StatisticList)
    {
    $type = $stat->{"type"};
    $value = $stat->{"value"};
    $low = $value->{"low"};
    $high = $value->{"high"};
    $value64 = ($high<<32)|$low;
    $time_stamp = $stat->{"time_stamp"};
    print " | $type : $value64";
    }
    }
    $i++;
    }
    }
    #----------------------------------------------------------------------------
    # getAllPoolInfo_4
    #----------------------------------------------------------------------------
    sub getAllPoolInfo_4()
    {
    $soapResponse = $Pool_4->get_list();
    &checkResponse($soapResponse);
    my @pool_list = @{$soapResponse->result};
    &getPoolInfo_4(@pool_list);
    }
    #----------------------------------------------------------------------------
    # getPoolInfo_4
    #----------------------------------------------------------------------------
    sub getPoolInfo_4()
    {
    my @pool_list = @_;
    foreach $pool_name (@pool_list)
    {
    $soapResponse = $Pool_4->get_lb_method
    (
    SOAP::Data->name(pool_name => $pool_name)
    );
    &checkResponse($soapResponse);
    $lb_method = $soapResponse->result;

    $soapResponse = $Pool_4->get_minimum_active_member
    (
    SOAP::Data->name(pool_name => $pool_name)
    );
    &checkResponse($soapResponse);
    $min_active_members = $soapResponse->result;

    # Get Pool Statistics
    $soapResponse = $Pool_4->get_statistics
    (
    SOAP::Data->name(pool_name => $pool_name)
    );
    &checkResponse($soapResponse);
    my $stats = $soapResponse->result;
    my $thruput_stats = $stats->{"thruput_stats"};
    my $bits_in = $thruput_stats->{"bits_in"};
    my $bits_out = $thruput_stats->{"bits_out"};
    my $packets_in = $thruput_stats->{"packets_in"};
    my $packets_out = $thruput_stats->{"packets_out"};

    my $conn_stats = $stats->{"connection_stats"};
    my $cur_conn = $conn_stats->{"current_connections"};
    my $max_conn = $conn_stats->{"maximum_connections"};
    my $total_conn = $conn_stats->{"total_connections"};

    # Get Member Statistics
    $soapResponse = $Pool_4->get_all_member_statistics
    (
    SOAP::Data->name(pool_name => $pool_name)
    );
    &checkResponse($soapResponse);
    @MemberStatisticsEntryList = @{$soapResponse->result};
    # Print Results
    print "POOL $pool_name ";
    print "LB_METHOD $lb_method ";
    print "MIN ACTIVE MEMBERS: $min_active_members";
    print "--> bits (in, out) ";
    print "(", $bits_in, ", ", $bits_out, ")";
    print " packets (in, out) ";
    print "(", $packets_in, ", ", $packets_out, ")";
    print " connections (cur, max, tot) ";
    print "(", $cur_conn, ", ", $max_conn, ", ", $total_conn, ")";

    foreach $MemberStatisticEntry (@MemberStatisticsEntryList)
    {
    $member = $MemberStatisticEntry->{"member_definition"};
    $address = $member->{"address"};
    $port = $member->{"port"};
    print "+-> MEMBER $address:$port";

    $stats = $MemberStatisticEntry->{"stats"};
    $thruput_stats = $stats->{"thruput_stats"};
    $bits_in = $thruput_stats->{"bits_in"};
    $bits_out = $thruput_stats->{"bits_out"};
    $packets_in = $thruput_stats->{"packets_in"};
    $packets_out = $thruput_stats->{"packets_out"};

    $conn_stats = $stats->{"connection_stats"};
    $cur_conn = $conn_stats->{"current_connections"};
    $max_conn = $conn_stats->{"maximum_connections"};
    $total_conn = $conn_stats->{"total_connections"};

    print " | bits (in, out) ";
    print "(", $bits_in, ", ", $bits_out, ")";
    print " packets (in, out) ";
    print "(", $packets_in, ", ", $packets_out, ")";
    print " connections (cur, max, tot) ";
    print "(", $cur_conn, ", ", $max_conn, ", ", $total_conn, ")";
    }
    }
    }