iC2I: What Pools Rely on This Server?

Prerequisites:
This article assumes you have read the first article in the series, that you have access to a Java compiler, and that you have built the iControl API on your development machine – or at least copied the JAR files to your development machine. It further assumes that you know how to make a project that includes the external iControl JAR file. It was originally written for iControl 9.4.0 using Eclipse with Java J2SE 1.60. 

If you previously cut and pasted the base class from the kickoff article, you will need to go recopy it. As might be expected, we made some minor tweaks to the class to make future subclasses easier to write.

Overview:
The iControl architecture is designed such that a Virtual knows which Pools service it, and a pool knows what Nodes are members of it. This provides you with maximum adaptability because changing nodes around from pool to pool or putting a node into multiple pools is not restricted by changes you must make to the Node itself. The node remains the same and it is simply added to or removed from a pool.

 This is a great design for 99.9% of the needs of both Web UI users and iControl programmers, but it does leave one area that we here on DevCentral answer a lot of questions about – how can you determine which pools a given node is a member of? From an administration viewpoint, this knowledge is critical in some situations – like server upgrades – so that you know what the impacts to your network will likely be.

So the point of this article is to provide you with the iControl code (in Java, but the same calls are applicable to all languages) to get a list of all pools a given node belongs in. 

While this appears trivial when the code is presented, it is non-intuitive because it is doing exactly the opposite of what the BIG-IP data model is designed for. Every design choice has strengths and weaknesses, this is one weakness we can plug pretty quickly with an article, so here goes.

Code: 
This article creates two new classes in the i2CI package – one to represent a node, one to represent pools. The point of these articles is to show you how to get a given task done, so the classes are nowhere near complete. As the series goes on, the same naming convention will be used for the classes, so you will be able to cut and paste methods directly into these classes. 

The net result of these two classes is simply a method on one of the classes that you can call from your code and get a list of pools the node belongs to. We’ll throw a simple main class up there for completeness, but you will likely want to do something more with the classes.

The first class we will look at is the BigIpPool class. From the Pool interface we need to get a list of all pools and a list of members in a pool. In this particular case, since we want a list of members from all pools, we’re going to take advantage of the iControl design feature that allows us to request information about multiple objects in a single call. 

 

package iC2i;

import iControl.CommonIPPortDefinition;
import iControl.LocalLBPoolBindingStub;
import iControl.LocalLBPoolLocator;

public class BigIpPool extends BigIpBase {
    LocalLBPoolBindingStub stub = null;

    public BigIpPool(String username, String password, String addressOrName) throws Exception {
        super(username, password, addressOrName);
        connect(); 
    }

    private void connect() throws Exception {
        stub = (LocalLBPoolBindingStub) 
 new LocalLBPoolLocator().getLocalLBPoolPort(new java.net.URL(fullUrl) );
        stub.setTimeout(6000); 
    }

    public String[] getList() throws Exception {
        return stub.get_list();
    }

    public CommonIPPortDefinition[][] getMembers(String pools[]) throws Exception {
        return stub.get_member(pools); 
    }
}

 

So the very first thing to notice about this class is that the connect () method creates a connection that only applies to this instance. A better solution in a more complex  environment would be to make a single static connection for all instances. This too has its difficulties, primary being managing state, but it is manageable, and even then there would still be a non-trivial number of connections if you had one for each object type.

The next thing to notice is that by moving the connection into a single place in the class, there is not much work to be done in the methods. That will change over time, since a call like “getMembers()” would be easier to use if it returned something other than a complex type. Thus in future installments in this series, we will likely do just that – return things that are less tough to work with. Note though that the fields of CommonIPPortDefinition are complete, and any other solution will not be. That’s okay as long as we identify the bits important in daily usage. 

The other class we need is one to work with Nodes. After all, the point of this exercise is to ask a node what pools it is a member of, so we need a node object to represent that node.

 

package iC2i;

import java.util.Vector;
import iControl.CommonIPPortDefinition;

public class BigIpNodeAddress extends BigIpBase {

    public BigIpNodeAddress( String username, String password, String addressOrName) {

        super(username, addressOrName, password); 
    }

    public Vector getPoolList(String IpAddress) throws java.lang.Exception {
        Vector retVal = new Vector();

        BigIpPool poolIF = new BigIpPool(username_, address_, password_);

        // Start with a list of all pools on the system.

        String pools[] = poolIF.getList();

        // Use that to get a list of all members for each pool.
        // We could do that one pool at a time, but that's multiple
        // trips across the Network and iControl is built to limit
        // that, so use its strengths.

        CommonIPPortDefinition poolMembers[][] = poolIF.getMembers(pools); 

        // Walk through and look for this node address in each row
        // (a row represents a Pool). 

        for(int i = 0;i < pools.length; i++) {
            for(int j = 0; j < poolMembers[i].length; j++) { 
                if(poolMembers[i][j].getAddress().equals(IpAddress))
                    retVal.add(pools[i]); 
            } 
        }
        return retVal; 
    }
}

 

Since we don’t want to require that the caller test for the validity of a node address before calling the class, we have made the working method – getPoolList – take any IP address. We could have required that an IP Address be supplied to the constructor, but that just adds complexity to our simple solution. You can extrapolate this solution to one where each instance of BigIpNodeAddress represents one Node if your environment works better that way.

To make that perfectly clear, the address passed in to this constructor is not the node address, it is the address or DNS name to communicate with the BIG-IP at. So whatever the management interface of the BIG-IP is set to, that’s what goes here. 

The single working method in this class creates an instance of BigIpPool and calls it to get a list of all pools on the BIG-IP. It then sends that list in to getMembers() to get a list of all members in each of these pools. Note that in a large installation this will be a lot of data, and you may wish to call getMembers() once per element in the pools array, processing them individually. In our opinion, this is a waste of code and network resources in all but the largest installations. The reason we feel this way is two-fold. First, you’ll have to write extra code to deal with calling getMembers() for each element in the pools list, and second, each of those calls across the network requires a certain amount of overhead – both in making the call and in the return values. Though the overhead is negligible, why introduce it if you don’t have to?

In iControl, the CommonLBPool method get_members() and by extension our BigIpPool.getMembers () method return a multi-dimensional array. The first dimension is the pools that were passed in, the second dimension is the list of members for the pool specified by location in the first dimension. Thus if the BIG-IP had three Pools - Pool1, Pool2, Pool3, the array poolMembers[0] would list all the members of Pool1. 

Thus, our nested loops walk through each pool, and look at each element in that pool, comparing it to the IP address we passed in. The solution uses some amount of brute force, but it is effective.

Finally, we’ll offer you a stripped-down main class to call these routines and print out the list of pools the node belongs to. 

 

import java.util.Vector;
import iC2i.BigIpNodeAddress;

public class BigIPLook {
    public static void main(String[] args) {
        System.setProperty("javax.net.ssl.trustStore", System.getProperty("user.home") + "/.keystore");
        XTrustProvider.install();
        BigIpNodeAddress bIpNode = new BigIpNodeAddress("username","password", "DNSNameOfBigIPAdminInterface"); 
        try {
            Vector pools = bIpNode.getPoolList("NodeIPAddress");

                if(pools.isEmpty())
                    System.out.println("Not a member of any pools.");
                else
                    System.out.println(pools);
        } catch(Exception e) { 
            System.out.println(e.getLocalizedMessage());
        }
    }
}

 

All this simple class does is set up our trust provider, create an instance of BigIpNodeAddress, and then call BigIpNodeAddress to get a Vector of pools that the indicated Node Address is a member of.

Summary:

Note that this example only takes IP addresses, not DNS names, so you’ll either have to modify BigIpNodeAddress.getPoolList() to do a DNS lookup on names when they’re passed (better solution) or do the DNS lookup here (less spectacular solution because you don’t get reuse for all instances of BigIpNodeAddress) if you wish to allow lookups by DNS name. Note that due to the fact that we are creating a connection to the BIG-IP, a Fully Qualified Domain Name (FQDN) or IP address will work for the “DNSName…” parameter in the constructor of BigIpNodeAddress. 

Note that we’re not parsing parameters here – we wanted to keep the focus on the iControl portions of the code, you can write the parameter parsing routine to suite your needs, and enhance Exception handling as you like. Most installations are going to want to take username and password on the command line. Even if your security is handled elsewhere, these are not items you should store in a Java class file on a publicly accessible drive. Nearly every installation will further want to take the node address and BIG-IP address on the command line to make the solution adaptable. 

Hopefully you see that this is a relatively simple solution, but if not, you have the source to work through and see how it handles connections.

Get the Flash Player to see this player.
Published Apr 11, 2008
Version 1.0

Was this article helpful?

3 Comments

  • The answer to this question may be obvious for careful and skilled readers of both the article text and the Java language (which isn't me), but doesn't this code more precisely return the pools for any given IP address (which isn't really the same as a server)?

     

     

    For servers that supply services handled by the BigIP on multiple IP addresses (whether attached to the same network interface or multiple interfaces), one would still have to query the node itself to find all of it's ip addresses or (assuming a shop is meticulous in configuring reverse DNS lookups) do a zone transfer on the DNS server to find all of the IP addresses homed to a particular node/server. If I'm right, is there a way to find these (multiple-ip-to-host) associations using the iControl API and parent/child relationships or something else?

     

     

    Thanks for the great product and great support!

     

    ard
  • Don_MacVittie_1's avatar
    Don_MacVittie_1
    Historic F5 Account
    Hey ard,

     

     

    You are absolutely correct. The reasons for advertising a server on two IP addresses is to make it appear as two end-points to the network. Thus, the BIG-IP treats them as two distinct endpoints because it is a networking device.

     

     

    Because of this relationship of IP to endpoint, BIG-IP stores the definition of a "node" as an IP address with a "screen name" a user-friendly name that can be displayed.

     

     

    There are some ways you could get around this - use a common naming convention for multiple IPs on a single machine and then use get_list() on the node API to get a list of all nodes and search through for the common names (like WEB1IP1, WEB1IP2 for screen names and then search the list of all nodes for anything starting with WEB1). Then this code could be run once per "node" to come up with a complete list.

     

     

    This might be a great iC2I article in the near future, it builds on this topic and is useful for anyone using virtualization or higher end servers. Even those using lower end multi-homed servers for redundancy.

     

     

    Don.
  • Thanks for the feedback. Didn't know enough about the API to come up with the workaround you suggest but that's going to be helpful for a problem we're currently faced with.

     

     

    Again, thanks for the great support!