Writing to and rotating custom log files
Sometimes I need to log information from iRules to debug something. So I add a simple log statement, like this: when HTTP_REQUEST { if { [HTTP::uri] equals "/secure" } { log local0. "[IP::remote_addr] attempted to access /secure" } } This is fine, but it clutters up the /var/log/ltm log file. Ideally I want to log this information into a separate log file. To accomplish this, I first change the log statement to incorporate a custom string - I chose the string "##": when HTTP_REQUEST { if { [HTTP::uri] equals "/secure" } { log local0. "##[IP::remote_addr] attempted to access /secure" } } Now I have to customize syslog to catch this string, and send it somewhere other than /var/log/ltm. I do this by customizing syslog with an include statement: tmsh modify sys syslog include '" filter f_local0 { facility(local0) and not match(\": ##\"); }; filter f_local0_customlog { facility(local0) and match(\": ##\"); }; destination d_customlog { file(\"/var/log/customlog\" create_dirs(yes)); }; log { source(local); filter(f_local0_customlog); destination(d_customlog); }; "' save the configuration change: tmsh save / sys config and restarting the syslog-ng service: tmsh restart sys service syslog-ng The included "f_local0" filter overrides the built-in "f_local0" syslog-ng filter, since the include statement will be the last one to load. The "not match" statement is regex which will prevent any statement containing a “##” string from being written to the /var/log/ltm log. The next filter,"f_local0_customlog", catches the "##" log statement and the remaining include statements handle the job of sending them to a new destination which is a file I chose to name "/var/log/customlog". You may be asking yourself why I chose to match the string ": ##" instead of just "##". It turns out that specifying just "##" also catches AUDIT log entries which (in my configuration) are written every time an iRule with the string "##" is modified. But only the log statement from the actual iRule itself will contain the ": ##" string. This slight tweak keeps those two entries separated from each other. So now I have a way to force my iRule logging statements to a custom log file. This is great, but how do I incorporate this custom log file into the log rotation scheme like most other log files? The answer is with a logrotate include statement: tmsh modify sys log-rotate syslog-include '" /var/log/customlog { compress missingok notifempty }"' and save the configuration change: tmsh save / sys config Logrotate is kicked off by cron, and the change should get picked up the next time it is scheduled to run. And that's it. I now have a way to force iRule log statements to a custom log file which is rotated just like every other log file. It’s important to note that you must save the configuration with "tmsh save / sys config" whenever you execute an include statement. If you don't, your changes will be lost then next time your configuration is loaded. That's why I think this solution is so great - it's visible in the bigip_sys.conf file -not like customizing configuration files directly. And it's portable.2.4KViews0likes8CommentsGetting Up And Running With F5 ARX Virtual Edition
ARX (Adaptive Resource Switch) is a filesystem virtualization switch, which provides intelligent file virtualization for storage environments. ARX can be used to tier files across multiple filers, provide disaster recovery services, and allow administrators to migrate files between backend filers without disruption of service. These are a few of the use cases for ARX, but by no means all of them. Over the next few months, we will be covering ARX heavily here on DevCentral. Let’s get started by deploying ARX Virtual Edition in our ESX environment. ARX Virtual Edition resource requirements The resource requirements are difference depending on the license you have for your ARX VE. If you have a trial license obtained from http://www.f5.com/trial the requirements are as follows: 1 CPU core, 64-bit architecture 2 GB of memory 1 Virtual NIC (VNIC) 40 GB or more of hard drive space If you have a production license or evaluation license, the requirements are as follows: 2 CPU cores, 64-bit architecture 4 GB of memory 1 Virtual NIC (VNIC) 40 GB or more of hard drive space Prerequisites Download and install VMWare ESXi Download VMWare vSphere Client from ESXi host and install on local workstation Generate registration key and download F5 ARX Virtual Edition (VE) Installation Connect to ESXi host using VMWare vSphere Client Select ‘File’, then ‘Deploy OVF Template…’ Browse to the location of the OVF template (arxve-esx-trial-XXXXX.ova), select it, and click ‘Next’ Check the ‘OVF Template Details and make sure everything looks correct, then click ‘Next’ Name your Virtual Edition instance, click ‘Next’ Select ‘Trial’ from the ‘Configuration’ drop-down, click ‘Next’ Select ‘Thick provisioned format’ to allocate the disk space immediately (only select ‘Thin provisioned format’ if disk space is an issue and you fully understand its implications), click ‘Next’ Select the network you would like to attach the ARX VE VNIC to, click ‘Next’ Verify all of the deployment details and click ‘Finish’ if everything is correct The OVF template will now be deployed onto the ESXi box. This process can take 5-10 minutes. Configuration – command-line portion Power on ARX VE virtual machine Open console tab Press ‘enter’ to start the ‘Switch Configuration Wizard’ Enter the following parameters (substitute your values where necessary) Management port IP address 10.0.0.150 Management port subnet mask 255.255.255.0 Management port gateway IP address 10.0.0.254 Switch private IP address (accept default) Switch private subnet mask (accept default) Chassis UUID (accept default) Crypto-officer username admin Crypto-officer password (use your own) System password (use your own) Master key (accept default) Once you have completed the form, type ‘yes’ to accept your changes Wait for all the services to restart Configuration – web interface portion Open a web browser and navigate to the filer over HTTPS (https://10.0.0.150 in our case) Login with the crypto-officer username and password From the ‘Common Operations’ leaf, select ‘Initial Setup…’ Begin the initial setup wizard (these values will vary with your local environment) Switch name test-arx-ve Management protocols SSH, API Access – HTTPS (we may want this for some iControl examples later on) Proxy IP addresses 10.0.0.160 (1 is required for ARX VE) NTP server 10.0.0.254 Time zone region North and South America contrinents Time zone city PST (-0800)/PDT (-0700) United States Pacific time DNS domain name arx-test-ve.f5-test.com Primary DNS server 10.0.0.254 DNS search domains f5-test.com Fully qualified domain name of e-mail server smtp.f5-test.com E-mail recipients admin@f5-test.com SNMP (optional) Confirm your configuration and click ‘Finish’ to finalize the initial setup At this point, the VE instance should be fully configured with the exception of licensing the unit Activating the license automatically Locate the email you received containing the registration key and copy the key to your clipboard Select ‘Activate License…’ from the web interface of ARX Manager Paste the registration key into the field of the pop-up window and select ‘Automatic’ if your ARX has a route to the Internet, click ‘Next’ Agree to the EULA terms and click ‘Next’ If automatic activation worked correctly, you should be presented with a confirmation screen, click ‘Finish’ Activating the license manually Follow steps 1-2 from the previous selection Paste the registration key into the field of the pop-up window and select ‘Manual’ Copy the dossier to your clipboard and click ‘Click here to access F5 licensing server’ Paste dossier into the text field and click ‘Next’ Copy the license to your clipboard and post it into the ‘License’ field of the pop-up window, click ‘Next’ If manual activation worked correctly, you should be presented with a confirmation screen, click ‘Finish’ (should look like confirmation from previous section, step 5) Conclusion Now that you’ve got ARX VE up and running in your environment, you can begin playing with some of the functionality. We will be using a combination of commercial filer simulators in the examples that follow over the next few months. If you want to get ahead of the game, get your favor filer up and running on your ESX box. See everyone next week!344Views0likes1CommentiRule Editor - Auto Connection
In the latest release of the iRule Editor v 0.10.1, Iadded several new features. This tutorial will walk through the Auto Connection features I've added that allow you to supply command line arguments to have the iRule editor automatically connect to your specified BIG-IP. Usage: iRuler.exe [args] args ----------- /h hostname - the hostname (or ip) of your BIG-IP /u username - the admin username /p password - the admin password The following video will walk you through the steps to auto connect from the command line as well as how to create a desktop shortcut to each of your BIG-IP systems.359Views0likes1CommentTroubleshooting TLS Problems With ssldump
Introduction Transport Layer Security (TLS) is used to secure network communications between two hosts. TLS largely replaced SSL (Secure Sockets Layer) starting in 1999, but many browsers still provide backwards compatibility for SSL version 3. TLS is the basis for securing all HTTPS communications on the Internet. BIG-IP provides the benefit of being able to offload the encryption and decryption of TLS traffic onto a purpose specific ASIC. This provides performance benefits for the application servers, but also provides an extra layer for troubleshooting when problems arise. It can be a daunting task to tackle a TLS issue with tcpdump alone. Luckily, there is a utility called ssldump. Ssldump looks for TLS packets and decodes the transactions, then outputs them to the console or to a file. It will display all the components of the handshake and if a private key is provided it will also display the encrypted application data. The ability to fully examine communications from the application layer down to the network layer in one place makes troubleshooting much easier. Note: The user interface of the BIG-IP refers to everything as SSL with little mention of TLS. The actual protocol being negotiated in these examples is TLS version 1.0, which appears as “Version 3.1” in the handshakes. For more information on the major and minor versions of TLS, see the TLS record protocol section of the Wikipedia article. Overview of ssldump I will spare you the man page, but here are a few of the options we will be using to examine traffic in our examples: ssldump -A -d -k <key file> -n -i <capture VLAN> <traffic expression> -A Print all fields -d Show application data when private key is provided via -k -k Private key file, found in /config/ssl/ssl.key/; the key file can be located under client SSL profile -n Do not try to resolve PTR records for IP addresses -i The capture VLAN name is the ingres VLAN for the TLS traffic The traffic expression is nearly identical to the tcpdump expression syntax. In these examples we will be looking for HTTPS traffic between two hosts (the client and the LTM virtual server). In this case, the expression will be "host <client IP> and host <virtual server IP> and port 443”. More information on expression syntax can be found in the ssldump and tcpdump manual pages. *the manual page can be found by typing 'man ssldump' or online here <http://www.rtfm.com/ssldump/Ssldump.html> A healthy TLS session When we look at a healthy TLS session we can see what things should look like in an ideal situation. First the client establishes a TCP connection to the virtual server. Next, the client initiates the handshake with a ClientHello. Within the ClientHello are a number of parameters: version, available cipher suites, a random number, and compression methods if available. The server then responds with a ServerHello in which it selects the strongest cipher suite, the version, and possibly a compression method. After these parameters have been negotiated, the server will send its certificate completing the the ServerHello. Finally, the client will respond with PreMasterSecret in the ClientKeyExchange and each will send a 1 byte ChangeCipherSpec agreeing on their symmetric key algorithm to finalize the handshake. The client and server can now exchange secure data via their TLS session until the connection is closed. If all goes well, this is what a “clean” TLS session should look like: New TCP connection #1: 10.0.0.10(57677) <-> 10.0.0.20(443) 1 1 0.0011 (0.0011) C>S Handshake ClientHello Version 3.1 cipher suites TLS_DHE_RSA_WITH_AES_256_CBC_SHA [more cipher suites] TLS_RSA_EXPORT_WITH_RC4_40_MD5 Unknown value 0xff compression methods unknown value NULL 1 2 0.0012 (0.0001) S>C Handshake ServerHello Version 3.1 session_id[0]= cipherSuite TLS_RSA_WITH_AES_256_CBC_SHA compressionMethod NULL 1 3 0.0012 (0.0000) S>C Handshake Certificate 1 4 0.0012 (0.0000) S>C Handshake ServerHelloDone 1 5 0.0022 (0.0010) C>S Handshake ClientKeyExchange 1 6 0.0022 (0.0000) C>S ChangeCipherSpec 1 7 0.0022 (0.0000) C>S Handshake Finished 1 8 0.0039 (0.0016) S>C ChangeCipherSpec 1 9 0.0039 (0.0000) S>C Handshake Finished 1 10 0.0050 (0.0010) C>S application_data 1 0.0093 (0.0000) S>C TCP FIN 1 0.0093 (0.0000) C>S TCP FIN Scenario 1: Virtual server missing a client SSL profile The client SSL profile defines what certificate and private key to use, a key passphrase if needed, allowed ciphers, and a number of other options related to TLS communications. Without a client SSL profile, a virtual server has no knowledge of any of the parameters necessary to create a TLS session. After you've configured a few hundred HTTPS virtuals this configuration step becomes automatic, but most of us mortals have missed step at one point or another and left ourselves scratching our heads. We'll set up a test virtual that has all the necessary configuration options for an HTTPS profile, except for the omission of the client SSL profile. The client will open a connection to the virtual on port 443, a TCP connection will be established, and the client will send a 'ClientHello'. Normally the server would then respond with ServerHello, but in this case there is no response and after some period of time (5 minutes is the default timeout for the browser) the connection is closed. This is what the ssldump would look like for a missing client SSL profile: New TCP connection #1: 10.0.0.10(46226) <-> 10.0.0.20(443) 1 1 0.0011 (0.0011) C>SV3.1(84) Handshake ClientHello Version 3.1 random[32]= 4c b6 3b 84 24 d7 93 7f 4b 09 fa f1 40 4f 04 6e af f7 92 e1 3b a7 3a c2 70 1d 34 dc 9d e5 1b c8 cipher suites TLS_DHE_RSA_WITH_AES_256_CBC_SHA [a number of other cipher suites] TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 TLS_RSA_EXPORT_WITH_RC4_40_MD5 Unknown value 0xff compression methods unknown value NULL 1 299.9883 (299.9871) C>S TCP FIN 1 299.9883 (0.0000) S>C TCP FIN Scenario 2: Client and server do not share a common cipher suite This is a common scenario when really old browsers try to connect to servers with modern cipher suites. We have purposely configured our SSL profile to only accept one cipher suite (TLS_RSA_WITH_AES_256_CBC_SHA in this case). When we try connect to the virtual using a 128-bit key, the connection is immediately closed with no ServerHello from the virtual server. The differentiator here, while small, is the quick closure of the connection and the ‘TCP FIN’ that arises from the server. This is unlike the behavior of the missing SSL profile, because the server initiates the connection teardown and there is no connection timeout. The differences, while subtle, hint at the details of the problem: New TCP connection #1: 10.0.0.10(49342) <-> 10.0.0.20(443) 1 1 0.0010 (0.0010) C>SV3.1(48) Handshake ClientHello Version 3.1 random[32]= 4c b7 41 87 e3 74 88 ac 89 e7 39 2d 8c 27 0d c0 6e 27 da ea 9f 57 7c ef 24 ed 21 df a6 26 20 83 cipher suites TLS_RSA_WITH_AES_128_CBC_SHA Unknown value 0xff compression methods unknown value NULL 1 0.0011 (0.0000) S>C TCP FIN 1 0.0022 (0.0011) C>S TCP FIN Conclusion Troubleshooting TLS can be daunting at first, but an understanding of the TLS handshake can make troubleshooting much more approachable. We cannot exhibit every potential problem in this tech tip. However, we hope that walking through some of the more common examples will give you the tools necessary to troubleshoot other issues as they arise. Happy troubleshooting!7.7KViews0likes5CommentsHTTP Basic Access Authentication iRule Style
I started working on an administrator control panel for my previous Small URL Generator Tech Tip (part 1 and part 2) and realized that we probably didn’t want to make our Small URL statistics and controls viewable by everyone. That led me to make a decision as to how to secure this information. Why not the old, but venerable HTTP basic access authentication? It’s simple, albeit somewhat insecure, but it works well and is easy to deploy. First, a little background on how this mechanism works: First, a user places a GET request for a page without providing authentication. The server then responds with a “401 Authorization Required” status code and the authentication type and realm specified by the WWW-Authenticate header. In our case we will be using basic access authentication and we’ve arbitrarily named our realm “Secured Area". The user will then provide a username and password. The client will then join the username and password with a colon and apply base-64 encoding to the concatenated string. The client then presents this to the server via the Authorization header. If the credentials are verified by the server, the client will then be granted access to the “Secured Area”. Authentication Required for “Secured Area” This transaction normally takes place between the client and application server, but we can emulate this functionality using an iRule. The mechanism is rather simple and easy to implement. We need to look for the client Authorization header and if present, verify the credentials. If the credentials are valid, grant access to the virtual server’s content, if not, display the authentication box and then repeat the process. On the BIG-IP side, we are storing the username and MD5-digested password in a data group (which we aptly named authorized_users). While this is not as secure as a salted hash, it does provide some security beyond storing the credentials in plain text on our BIG-IP. Once we took these elements into consideration, this is the iRule we developed: 1: when HTTP_REQUEST { 2: binary scan [md5 [HTTP::password]] H* password 3: 4: if { [class lookup [HTTP::username] $::authorized_users] equals $password } { 5: log local0. "User [HTTP::username] has been authorized to access virtual server [virtual name]" 6: 7: # Insert iRule-based application code here if necessary 8: } else { 9: if { [string length [HTTP::password]] != 0 } { 10: log local0. "User [HTTP::username] has been denied access to virtual server [virtual name]" 11: } 12: 13: HTTP::respond 401 WWW-Authenticate "Basic realm=\"Secured Area\"" 14: } 15: } There are a couple different ways this iRule could be implemented. It can be applied as-is directly to any HTTP virtual and begin protecting the virtual’s contents. It can also be used to secure an iRule-based application. In which case the application code would need to be encapsulated where the comment is located in the above code. I will be publishing a more functional example of this in the near future, but you can start using it now if you have such a necessity. Earlier we discussed the inherent insecurity of basic access authentication due to the username and password being transmitted in plain text. To combat this, you’ll probably want to conduct this transaction over an SSL secured connection. Additionally you’ll want to generate the passwords as MD5 hashes before adding them to your data group. Here are a few ways to accomplish this: Bash % echo -n "password" | md5sum => 5f4dcc3b5aa765d61d8327deb882cf99 - Perl % perl -e "use Digest::MD5 'md5_hex'; print md5_hex("password") => 5f4dcc3b5aa765d61d8327deb882cf99 Ruby (using irb) irb(main):001:0> require "md5" => true irb(main):002:0> MD5::md5("password") => #<MD5: 5f4dcc3b5aa765d61d8327deb882cf99> While HTTP basic access authentication may not be the best authentication method for every case, it definitely has its advantages. It is easy to deploy (and even easier via an iRule), provides basic authentication without having to configure or depend on an external authentication service, and is supported by any browser developed in the last 15 years. Through the use of different data groups you can easily separate access to different virtuals or provide a simple SSO (Single Sign-On) solution for a number of virtual servers. We hope you find this tidbit of code to be useful in your environment. Stay tuned for more extensive examples and usages of this tech tip in the near future!4.2KViews0likes14CommentsFailover with iControl part II: The utility.
Last time we developed a Java tool that will let you query the status of a BIG-IP to determine if it is in a redundant pair, and if so, what its status is. In this article we're going to develop that utility into a command-line utility for bringing BIG-IP boxes up and down by toggling between the members of a redundant pair. The actual work to toggle between two members of a redundant pair is pretty trivial with iControl, there are routines - set_standby() and set_failback() in the System.Failover interface. The problem comes in when you consider that you really don't want the ability to failover and failback to be arbitrarily on all the time, so the developers put in a bit of a break to make certain you knew what you were doing and you proved you wanted to do it. There is a key that must exist in the database on the BIG-IP that tells that particular BIG-IP it is to honor failover and failback requests submitted from anywhere but the redundant pair. So to build this little utility, we need to perform several steps: 1. Write a command line parsing routine to allow users to pass commands in (and since we're doing that, you'll find the one we developed takes username, password, and BIG-IP address also). We leave that to the source at the end of the article, you can peruse it at your liesure since it has little to do with failing over the BIG-IP and is really just an excercise in parsing Java command lines. 2. Write a set of routines to query for the database key and its value, and if it doesn't exist, set the value. 3. Write a set of routines to actually put a unit into standby or active. None of these are a huge deal to implement, and we'll just walk through the two we're covering. First up is the routines to query for and create the database key. We want them to be generic enough that should we run into a similar problem in the future, we can utilize them again. Toward that end, we wrap the web services calls in much the same manner that we used for many calls in the Java Wrappers Project - very lightly. First, the routine to ask about the DB key... public static boolean dbVarDefined(String varName) { ManagementDBVariableBindingStub stub; boolean ret = false; String param[] = {varName}; try { stub = (ManagementDBVariableBindingStub) new ManagementDBVariableLocator().getManagementDBVariablePort(new URL(fullUrl)); stub.setTimeout(6000); // Make the call, take first element of the return array - since we only sent one varname. ret = (stub.is_variable_available(param)[0]); } catch (Exception e) { System.out.println("dbVarDefined Error: " + e.getLocalizedMessage()); } return ret; } Notice that if an exception of any kind is raised by the call, ret will never get set. Thus we have a routine that will tell us if any given database variable key is defined on our BIG-IP. So now we can ask if it exists, the next problem is what to do if it doesn't. For that we have the create method... public static boolean createKey(String varName, String value) { ManagementDBVariableBindingStub stub; ManagementDBVariableVariableNameValue nv[] = {new ManagementDBVariableVariableNameValue()}; nv[0].setName(varName); nv[0].setValue(value); try { stub = (ManagementDBVariableBindingStub) new ManagementDBVariableLocator().getManagementDBVariablePort(new URL(fullUrl)); stub.setTimeout(6000); stub.create(nv); // create the name/value combination that is. } catch (Exception e) { System.out.println("createKey Error: " + e.getLocalizedMessage()); return false; } return true; } In this case, our default is to return true, so we explicitly return false in the exception handler to make certain word got out. Okay, so now we have the key that says "this BIG-IP can be failed over manually" - assuming we call these routines. The key (variable) name we need is Failover.ManFailBack and the value we're setting is enable. Next up are the actual routines that will switch the BIG-IP between active and standby modes. These are regular old iControl calls with not too much to them, so I'll just present them here: /* will take an active device and make it the standby, automatically promoting the other device to the active device. */ public static void setStandby() { SystemFailoverBindingStub stub; try { stub = (SystemFailoverBindingStub) new SystemFailoverLocator().getSystemFailoverPort(new URL(fullUrl)); stub.setTimeout(6000); stub.set_standby(); } catch (Exception e) { System.out.println("isRedundant Error: " + e.getLocalizedMessage()); } } /* will take a device in standby and make it the active device if it has failed in the past, automatically promoting the other device to the active device. */ public static void setFailback() { SystemFailoverBindingStub stub; try { stub = (SystemFailoverBindingStub) new SystemFailoverLocator().getSystemFailoverPort(new URL(fullUrl)); stub.setTimeout(6000); stub.set_failback(); } catch (Exception e) { System.out.println("isRedundant Error: " + e.getLocalizedMessage()); } } That's all there is to them. The first one sets the device you're communicating with into standby mode (which will make the other go active if they are an active/standby pair), the second brings it back. Note that "failback" is not 100% active, this routine will take any device in standby and make it active... It does not require a previous failure, just that the device be in standby mode. Now with those four routines, and the information from our previous article, we're ready to build that command line tool. There are some serious changes to the main() routine from last time, so we'll talk a bit about them before I add the source at the end and we call this story done. First off, since we set up parseArgs to take our authentication information, we need to have it return a boolean value to tell us if all is well.We don't want to try and hook up with a BIG-IP without an address or username, for example. So in main, we set up an if statement to check the return of parseArgs: if(!parseArgs(args)) { System.out.println("Incorrect parameters, exiting."); return; } Of course, parseArgs() provides more detailed error reporting when it finds problems with the parameters. Next we wrap all of the important stuff in a call to see if this box is even part of a redundant pair - if not, we should not do anything (you may wish to change this if you have a BIG-IP you want to force into Standby mode that is not part of a redundant pair, but we don't recommend doing that. if(checkRedundant()) In the next few lines, we check the current state of the BIG-IP against the request - if you've asked it to "failback", but the machine is in the active state, we tell you that there's a problem, for example. After we're comfortable with the state, we output some information for the user - the current state of the BIG-IP, and the requested action. Then we take a few lines to check for, and then set the BIG-IP into the state the user requested. That's it! You can toggle them back and forth to your heart's content. The team has told me I'm not allowed to use the formatting style that I've used in these two articles for source code anymore... So I'll try regular old Courier New in the "toggle standby" source code. USAGE NOTES: The best use of this program is to call the primary with /failover, perform the upgrade, then call the secondary with /failover. While calling with /failback, we encountered some difficulties that we're lookiing into. package f5Sample; import iControl.SystemFailoverBindingStub; import iControl.SystemFailoverLocator; import iControl.SystemFailoverFailoverState; import iControl.SystemFailoverFailoverMode; import iControl.ManagementDBVariableBindingStub; import iControl.ManagementDBVariableLocator; import iControl.ManagementDBVariableVariableNameValue; import java.net.URL; /** * @author dmacvittie * @copyright 2007, F5 Networks, Inc. * */ public class FailoverMain { static String username = "", password = "", ipAddress = "", fullUrl; static boolean failover = false, failback = false; /** * */ public FailoverMain() { // TODO Auto-generated constructor stub } /** * @param args */ public static void main(String[] args) { System.setProperty("javax.net.ssl.trustStore", System.getProperty("user.home") + "/.keystore"); XTrustProvider.install(); if(!parseArgs(args)) { System.out.println("Incorrect parameters, exiting."); return; } if(checkRedundant()) { // This is part of a redundant pair SystemFailoverFailoverState failoverState = checkFailoverState(); if(failoverState.toString().equals(SystemFailoverFailoverState._FAILOVER_STATE_ACTIVE) && failback) { System.out.println("Error: System is not in failover state, but request was to fail back. exiting."); return; } if(failoverState.toString().equals(SystemFailoverFailoverState._FAILOVER_STATE_STANDBY) && failover) { System.out.println("Error: System is in failover state, but request was to fail over. exiting."); return; } System.out.println("Current failover state is: " + failoverState.toString()); String foRequest; if(failover) foRequest = "Failover"; else // Assume it's one or the other by this point - parseArgs fails if neither or both are set. foRequest = "Failback"; System.out.println("Requested: " + foRequest + ".\nAttempting requested action..."); /* Now the tricky part... There is a key that must exist and have a specific value * in the Database on the BIG-IP for you to start flicking redundant pairs back and forth... So let's go looking for it. */ if(!dbVarDefined("Failover.ManFailBack")) { System.out.println("Manual failover DB key does not exist. Creating..."); if(!createKey("Failover.ManFailBack", "enable")) { System.out.println("Error creating DB Variable for manual failover. Please check user rights."); return; } } if(failover) { // Can assume device is in active state or would have exited. setStandby(); } else { // Can assume failback is set and device is in standby, or wouldn't have gotten here. setFailback(); } } else System.out.println("Your BIG-IP is not part of a redundant pair. Exiting."); System.out.println("Done."); } public static boolean parseArgs(String args[]) { // Parse them out... for(int i=0;i<args.length;i++) { if(args[i].compareToIgnoreCase("/failover") == 0) failover=true; else if(args[i].compareToIgnoreCase("/failback") == 0) failback=true; else if(args[i].toUpperCase().startsWith("/UNAME")) username = args[i].substring(7); else if(args[i].toUpperCase().startsWith("/PASSWORD")) password = args[i].substring(10); else if(args[i].toUpperCase().startsWith("/IP")) ipAddress = args[i].substring(4); else System.out.println("Unrecognized command: " + args[i] + " ignored."); } // Check that we have all of our params, and that exactly one of failover or failback is set. if(username.length() == 0 || password.length() == 0 || ipAddress.length() ==0 || (failover == failback)) { System.out.println("Usage: \nJava FailoverSample /username=XXXX /password=YYYY /IP=111.111.111.111 (/failover or /failback)") System.out.println("\tEither /failover or /failback may be specified, not both."); return false; } // Check that there are at least digits in the first character of the IP address (looking for DNS names). if(!Character.isDigit(ipAddress.charAt(0))) { System.out.println("Parameter error. This version of Failover does not resolve host names. Please supply a valid IP address."); return false; } // Bulid the connection string. setInfo(); return true; } public static void setInfo() { fullUrl = "https://" + username + ":" + password + "@" + ipAddress + "/iControl/iControlPortal.cgi"; } public static boolean dbVarDefined(String varName) { ManagementDBVariableBindingStub stub; boolean ret = false; String param[] = {varName}; try { stub = (ManagementDBVariableBindingStub) new ManagementDBVariableLocator().getManagementDBVariablePort(new URL(fullUrl)); stub.setTimeout(6000); // Make the call, take first element of the return array - since we only sent one varname. ret = (stub.is_variable_available(param)[0]); } catch (Exception e) { System.out.println("isRedundant Error: " + e.getLocalizedMessage()); } return ret; } public static boolean createKey(String varName, String value) { ManagementDBVariableBindingStub stub; ManagementDBVariableVariableNameValue nv[] = {new ManagementDBVariableVariableNameValue()}; nv[0].setName(varName); nv[0].setValue(value); try { stub = (ManagementDBVariableBindingStub) new ManagementDBVariableLocator().getManagementDBVariablePort(new URL(fullUrl)); stub.setTimeout(6000); // Make the call, take first element of the return array - since we only sent one varname. stub.create(nv); } catch (Exception e) { System.out.println("isRedundant Error: " + e.getLocalizedMessage()); return false; } return true; } public static boolean checkRedundant() { SystemFailoverBindingStub stub; boolean ret = false; try { stub = (SystemFailoverBindingStub) new SystemFailoverLocator().getSystemFailoverPort(new URL(fullUrl)); stub.setTimeout(6000); ret = stub.is_redundant(); } catch (Exception e) { System.out.println("isRedundant Error: " + e.getLocalizedMessage()); } return ret; } /* will take an active device and make it the standby, automatically promoting the other device to the active device. */ public static void setStandby() { SystemFailoverBindingStub stub; try { stub = (SystemFailoverBindingStub) new SystemFailoverLocator().getSystemFailoverPort(new URL(fullUrl)); stub.setTimeout(6000); stub.set_standby(); } catch (Exception e) { System.out.println("isRedundant Error: " + e.getLocalizedMessage()); } } /* will take a device in standby and make it the active device if it has failed in the past, automatically promoting the other device to the active device. */ public static void setFailback() { SystemFailoverBindingStub stub; try { stub = (SystemFailoverBindingStub) new SystemFailoverLocator().getSystemFailoverPort(new URL(fullUrl)); stub.setTimeout(6000); stub.set_failback(); } catch (Exception e) { System.out.println("isRedundant Error: " + e.getLocalizedMessage()); } } public static SystemFailoverFailoverState checkFailoverState() { SystemFailoverBindingStub stub; SystemFailoverFailoverState ret = SystemFailoverFailoverState.FAILOVER_STATE_ACTIVE; try { stub = (SystemFailoverBindingStub) new SystemFailoverLocator().getSystemFailoverPort(new URL(fullUrl)); stub.setTimeout(6000); ret = stub.get_failover_state(); } catch (Exception e) { System.out.println("State Error: " + e.getLocalizedMessage()); } return ret; } public static SystemFailoverFailoverMode checkMode() { SystemFailoverBindingStub stub; SystemFailoverFailoverMode ret = SystemFailoverFailoverMode.FAILOVER_MODE_ACTIVE_ACTIVE; try { stub = (SystemFailoverBindingStub) new SystemFailoverLocator().getSystemFailoverPort(new URL(fullUrl)); stub.setTimeout(6000); ret = stub.get_failover_mode(); } catch (Exception e) { System.out.println("Mode Error: " + e.getLocalizedMessage()); } return ret; } public static String findPeer() { SystemFailoverBindingStub stub; String ret[] = {"No peer found"}; try { stub = (SystemFailoverBindingStub) new SystemFailoverLocator().getSystemFailoverPort(new URL(fullUrl)); stub.setTimeout(6000); ret = stub.get_peer_address(); } catch (Exception e) { System.out.println("Peering Error: " + e.getLocalizedMessage()); } return ret[0]; } public static boolean setFailbackDBVal() { boolean ret[] = {false}; String input[] = {"Failover.ManFailBack"}; ManagementDBVariableBindingStub stub; try { stub = (ManagementDBVariableBindingStub) new ManagementDBVariableLocator().getManagementDBVariablePort(new URL(fullUrl)); stub.setTimeout(6000); ret = stub.is_variable_available(input); if(ret[0] == false) { String values[] = {"enable"}; ret[0] = createDBVariable(input, values); } } catch (Exception e) { System.out.println("Peering Error: " + e.getLocalizedMessage()); } return ret[0]; } public static boolean createDBVariable(String names[], String values[]) { boolean ret = false; ManagementDBVariableVariableNameValue input[] = {new ManagementDBVariableVariableNameValue()}; input[0].setName(names[0]); input[0].setValue(values[0]); ManagementDBVariableBindingStub stub; try { stub = (ManagementDBVariableBindingStub) new ManagementDBVariableLocator().getManagementDBVariablePort(new URL(fullUrl)); stub.setTimeout(6000); stub.create(input); ret = true; } catch (Exception e) { System.out.println("Peering Error: " + e.getLocalizedMessage()); } return ret; } }195Views0likes1CommentPerl: How to work with methods with multiple outbound parameters
I've seen this quesion asked in several forms so I thought I'd try to shed some light on how to handle accessing methods with multiple outbound parameters using Perl's SOAP::Lite: Let's take a look at these function prototypes: void func1(out string param1); void func2(out string param1, out string [] param2); string func3(out string param1, out string [] param2); string[] func4(out string param1, out string [] param2); string func5(out string[] param1, out string [][] param2); I know there are more combinations than this but this should cover most of the bases (other object types can take the place of string in these examples). I'll take each of these point by point and note how to access the returned parameters. Keep in mind that SOAP::Lite doesn't distinguish a returned value vs an outbound parameter, it uses the FIRST returned value whether it's the return value or the first outbound parameter. Then it stuffs all subsequent parameters into it's paramsout array. 1. void func1(out string param1); Here we have no return value but a single outbound parameter. Since the outbound parameter is the first returned value, it will be stored in the result. $soapResponse = $soap->func1(); $param1 = $soapResponse->result; # Scalar value print "param1 => $param1\n"; 2. void func2(out string param1, out string[] param2) This method has no return value but two outbound parameters so param1 will be stored in the result and param2 will be the first element of the paramsout array. $soapResponse = $soap->func2(); @params = $soapResponse->paramsout; $param1 = $soapResponse->result; # Scalar value @param2 = @{@params[0]); # Array print "param1 => $param1\n"; print "param2 => {"; foreach $val (@param2) { print "$val, "; } print "}\n"; 3. string func3(out string param1, out string [] param2); This method returns a string as a return value and has 2 outbound parameters. So, the returned value will be in the result and params 1 and 2 will be stored in the paramsout array. $soapResponse = $soap->func3(); @params = $soapResponse->paramsout; $retval = $soapResponse->result; $param1 = @params[0]; # Scalar value @param2 = @{@params[1]); # Array print "retval => $retval\n"; print "param1 => $param1\n"; print "param2 => {"; foreach $val (@param2) { print "$val, "; } print "}\n"; 4. string[] func4(out string param1, out string [] param2); This method is similar to number 2, but it returns an array which will be stored in the result and, again, params 1 and 2 will be sotred in the paramsout array. $soapResponse = $soap->func4(); @params = $soapResponse->paramsout; @retval = @{$soapResponse->result}; $param1 = @params[0]; # Scalar value @param2 = @{@params[1]); # Array print "retval => {"; foreach $val (@retval) { print "$val, "; } print "}\n"; print "param1 => $param1\n"; print "param2 => {"; foreach $val (@param2) { print "$val, "; } print "}\n"; 5. string func5(out string[] param1, out string [][] param2); Here we've introduced an Array of Arrays as an outbound parameter. the Return value will again be returned in the result with the parameters stored in the paramsout array. $soapResponse = $soap->func5(); @params = $soapResponse->paramsout; $retval = @{$soapResponse->result}; @param1 = @{@params[0]}; # Array @param2 = @{@params[1]); # Array of Array print "retval => $retval\n"; print "param1 => {"; foreach $val (@param1) { print "$val, "; } print "}\n"; print "param2 => {\n"; for $i ( 0 .. $#param2 ) { print " {"; for $j ( 0 .. $#{$param2[$i]} ) { print "$param2[$i][$j], "; } print "}\n"; } print "}\n"; You could have used the foreach statement for the array of array but this just shows another way to access the data by index. -Joe References SOAP::Lite SOAP::Lite Perldocs Manipulating Arrays of Arrays in perl615Views0likes2CommentsGetting Started With iControl And Java – Setting Up Eclipse
This is the first article in a series focused on developing iControl applications with the Java language. I’ve picked Eclipse development platform for this set of platforms. The goal here is to give you the directions and tools you need to make the most out of iControl when you are involved with Java development. For this article, I’m going to assume that you are starting off of a clean system. In most cases, you will already have one or more of the prerequisites installed so you can feel free to ignore that section. This article was developed with all the components on a Windows 7 System, but everything should work similarly on other platforms. The Pieces To The Puzzle Java The first item you will need to install is Java itself. This can be downloaded from the Java Website At Oracle.com. There are a lot of different versions of Java but I always stick to the bare bones “Java SE” JDK distribution. I’m not going to walk through the installation of Java as I assume you can figure that one out for yourself. If you are really stuck, do a Google Search for “How To Install Java” and that should point you in the right direction. Eclipse Eclipse is an open source development platform build with extensible frameworks, tools and runtimes for many different languages – Java being one of them. The JDT (Java Development Tools) project provides the tool plug-ins that implement a Java IDE supporting the development of any Java application. To install Eclipse for Java, visit the downloads page at http://www.eclipse.org/downloads/ and select the appropriate “Eclipse IDE for Java Developers distribution. Even though I am running 64-bit windows, I had an issue getting the 64-bit build of Eclipse to work. This was possibly due to the version of Java I had installed. So, I picked the “Windows 32 Bit” download and it worked out find. The download is just a .zip file containing the product directory. Just unzip this to a location of your choosing. iControl Library For Java At this point you should have a development environment setup for building Java applications. The last piece of the puzzle is the iControl libraries for Java. iControl is a set of Web Services and we have taken the Web Service Description Language (WSDL) files, built client proxy code for them, and packaged them within a nice wrapper class and packed them into a Java library to make it as easy as possible for you to integrate iControl into a new or existing Java application. To get the libraries, you’ll need to visit the iControl Assembly (we have a .Net version as well) Labs project on DevCentral and click on the “Discussions and Downloads” link. From there Click into the latest release in the “Downloads” section (as of now it is at iControl v10.2) and download the “Java Binary Distribution” release. The “Java Source Distribution” is available if you want to see how I built the library. The binary download will include two directories: Lib and 3rdparty. The client proxy code was built with Apache Axis so we’ve included those libraries to include in your projects as well so that you don’t have to go hunting elsewhere for all the other dependent libraries. Unzip this directory somewhere on your disk (ie \iControl\javalib). Building Your First Project I figured that a video would be easier to follow than me describing the steps it takes to create a project and import the iControl libraries in it so I make this walkthrough where I: Created a new Java project in Eclipse. Imported the iControl Library for Java and associated 3rd party libraries. Created a class with a Main method to be used as a console application. Created an instance of the iControl.Interfaces object. Initialized the iControl.Interfaces object with the credentials for my BIG-IP. Added logic to query the System information and output that to the console. Debug and demonstrate the application. I did all that in 5 minutes! …with a little help from cut-and-paste B-). Conclusion You should now be able to build a Java application to make use of the automation features of your BIG-IP. In future articles, I will illustrate other aspects of the iControl API with Java. Download The Java class for this example can be downloaded from the iControl CodeShare under JavaSystemInfo. Related Articles on DevCentral More caffeine please... Simple Ways to Make iControl Development Easier with Java Tools ... iControl Tech Tips Archive Java assembly version - DevCentral - F5 DevCentral > Community ... iControl Quickstart for Java > DevCentral > F5 DevCentral > Tech Tips Updating domain info using iControl - DevCentral - F5 DevCentral ... Technorati Tags: iControl, Java, Eclipse, Joe Pruitt553Views0likes6CommentsTech Tip: Saving Your iControl Changes
When making changes to almost any networking device via the command line, the last step is to "save the running config". This is so that you can make changes, tweak them, test them, go for coffee, etc. Only after you are positive that all is behaving as you expect do you take that last fatal step of telling the device to save the running config. This is true of the BIG-IP just as it is of most products. It's a nice feature to have because right up until you are ready to say the changes work correctly and save them, you can always revert to the saved configuration (or default configuration if you're on some vendor's platforms). In the Web UI, we eliminate this need for you by saving the changes automatically. That does mean that you have to remember what you did and reverse the changes in order to get the state back to where you started, but that is the expected behavior of a web user interface - when you make a change, you expect that it is actually made on the box, not that it's there temporarily. In iControl, we opted to go the route of the command line from an API perspective - this gives you the flexibility to roll back changes that your code has made, or commit them to the hardware so that they are active on the next reboot of your BIG-IP. This means that any changes you make via iControl are applied immediately and are active on the BIG-IP that your code is managing, but if you want them to be a permanent part of the configuration you will have to make an iControl API call to save them. Rather than leave you to reinvent the wheel, this article will present you with a simple routine that you can call to save these changes. Then all you have to worry about is when you want to save the configuration. BIG-IP saves information in two different configurations - one for over-arching information that keeps many default objects and things like the default gateway, and one that contains items that your organization has created. The first is called the "base config" and the latter is called the "high-level config". The iControl API uses a single routine to save both of these with a parameter to tell it which we wish to save, so we'll need to cover both, and then build a single routine to do either or both. All of the configuration saving routines are in the System.ConfigSync object. First we'll cover the simple case of saving the high and low-level configurations. The routine presented assumes only one thing - that you have imported iControl.SystemConfigSyncSaveMode, iControl.SystemConfigSyncBindingStub and iControl.SystemConfigSyncLocator. Once you have, you can cut and paste this routine into any class you like. saveConfig(boolean high, boolean base) { SystemConfigSyncBindingStub stub = null; // Get a connection to the VirtualServer services on the bigIP and ask it for the list of Virtual Servers. try { stub = (SystemConfigSyncBindingStub) new SystemConfigSyncLocator().getSystemConfigSyncPort(new URL(fullUrl)); stub.setTimeout(6000); if(high) stub.save_configuration("", SystemConfigSyncSaveMode.SAVE_HIGH_LEVEL_CONFIG); if(low) stub.save_configuration("", SystemConfigSyncSaveMode .SAVE_BASE_LEVEL_CONFIG); } catch(Exception e) { System.out.println(e.getLocalizedMessage()); } } That will save the running configuration to the default configuration, but what if you want to save it to another named configuration that you can load at a later date? Well, our team thought of that also, that is what the first parameter to save_configuration() is for - you can pass in a filename, and if you set the SystemConfigSyncSaveMode parameter to SAVE_FULL you can pass in a filename to use instead of the defaults. When you save in this manner, you have to call load_configuration() to access the configuration you saved. Thus, save_configuration("my_base_config.conf", SystemConfigSyncSaveMode.SAVE_FULL); Will save the configuration to the file my_vase_config.conf, and load_configuration("my_base_config.conf", SystemConfigSyncLoadMode.LOAD_HIGH_LEVEL_CONFIG) will load the high-level information from the configuration file and make it the running config. This could be useful if you wish to save many alternate configurations to use at different times, or if you are working on a configuration and need to stop working on it for a while. As is normally the case with iControl, you have a lot of power and flexibility at your fingertips, you just have to make a few extra calls to take advantage of this power. Next time - saving encrypted configurations. Get the Flash Player to see this player.416Views0likes1CommentJava iControl Objects - LTM Pool
The first comment I usually get when developers look at the iControl API has something to do with it’s “flat” nature. Developers are used to working with “objects”. They create an instance of an object and then get and set attributes on that object. In iControl-speak, they would create a pool object and then get and set the load balancing method on that pool they were holding on to. iControl was meant to be a very low level API to give you access to every type of object and it’s attributes. But iControl was designed as a Web Services API, and thus, it was exposed as a set of methods, not as objects. But, that doesn’t mean we can’t make iControl look like a set of objects, does it! Rounding The Flatness Fortunately, iControl is grouped into a set of “interfaces” that, in most cases, can be treated as objects. Pools, PoolMembers, VLANs, and WideIPs, can all be thought of as objects. This is the first article in a series centered around creating a object library around the various types of iControl objects. In this article I will start with where most of the initial iControl applications begin with: Pools. I would have started with Pool Members, but a Pool Member needs to belong to a Pool and I couldn’t create the PoolMember object without first defining the Pool object. So, be on the lookout for that one coming next time around. Prerequisites The code in this article, relies on the iControl library for Java that can be downloaded in it’s DevCentral labs project. If you haven’t watched it already, check out my previous article on Configuring Eclipse for iControl development with Java. The Class I’ll be using the package “iControl.Objects” for this code library I’m building. The classes will be in sub packages for the product (LTM, GTM, ASM, etc) and the class names will be the same as the existing iControl interfaces. For the Pool class, we need the underlying iControl.Interfaces object from the iControl Library for Java as well as a name for the pool. 1: package iControl.Objects.LTM; 2: 3: public class Pool { 4: private iControl.Interfaces _interfaces = null; 5: private String _name = null; 6: 7: 8: //----------------------------------------------------------------------- 9: // Member Accessors 10: //----------------------------------------------------------------------- 11: public iControl.Interfaces getInterfaces() { return _interfaces; } 12: public void setInterfaces(iControl.Interfaces interfaces) { _interfaces = interfaces; } 13: 14: public String getName() { return _name; } 15: public void setName(String name) { _name = name; } 16: 17: //----------------------------------------------------------------------- 18: // Constructors 19: //----------------------------------------------------------------------- 20: public Pool(iControl.Interfaces interfaces, String name) 21: { 22: _interfaces = interfaces; 23: _name = name; 24: } 25: ... Above, I’ve defined the member variables, the accessor methods for those variables, and the constructor that takes as input the defining values of the Pool. Object Attribute Accessors If you look at the iControl API methods in the Pool interface, you’ll see lots of “get_*” and “set_*” methods. We defined the API in this way to make it very easy to know which attributes you can set and which you can retrieve. Some are read-only and, in that case, will only have a get_* method. The next section of the class will include all the get_ and set_ methods. Here’s the code for the get_lb_method() and set_lb_method() methods. 1: // lb_method 2: public void setLBMethod(iControl.LocalLBLBMethod lbMethod) throws Exception 3: { 4: validateMembers(); 5: 6: String [] pool_names = { _name }; 7: iControl.LocalLBLBMethod [] lb_methods = { lbMethod}; 8: 9: _interfaces.getLocalLBPool().set_lb_method(pool_names, lb_methods); 10: } 11: public iControl.LocalLBLBMethod getLBMethod() throws Exception 12: { 13: validateMembers(); 14: 15: String [] pool_names = { _name }; 16: iControl.LocalLBLBMethod [] lb_methods = 17: _interfaces.getLocalLBPool().get_lb_method(pool_names); 18: 19: return lb_methods[0]; 20: } You’ll notice that I’ve chosen to abstract away all the arrays in the method calls. This removes the burden of packaging up single and two-dimensional arrays before making method calls for a single object. I’ve repeated this type of methodology for all the other get_* and set_* methods in the LocalLB.Pool interface. Action Methods Each iControl interface has a set of actions you can perform. Whether it’s creating a new pool, removing a pool, or adding members to a pool, these methods need to be exposed to the user. Below is how I chose to expose the ability to add new members to the Pool. It essentially wraps the iControl LocalLB.Pool.add_member() method. 1: public void addMembers(iControl.CommonIPPortDefinition [] members) throws Exception 2: { 3: validateMembers(); 4: 5: String [] pool_names = { _name }; 6: iControl.CommonIPPortDefinition [][] membersAofA = { members }; 7: 8: _interfaces.getLocalLBPool().add_member(pool_names, membersAofA); 9: } I’ve also included methods that allow you to create the pool, delete the pool, delete persistence records, querying the pool members, removing pool members, and querying statistics. Static Methods Static methods are meant to be able to be used without an instance of the object. Querying the list of available pools on the system seems to fit into this category. You need to know the name of a pool to be able to create the instance, but it is also part of the Pool class. This is illustrated below. 1: public static String [] getList(iControl.Interfaces interfaces) throws Exception 2: { 3: String [] pool_list = null; 4: 5: if ( null != interfaces ) 6: { 7: pool_list = interfaces.getLocalLBPool().get_list(); 8: } 9: 10: return pool_list; 11: } An Example Usage Using the above class is very straightforward. First you must create the core iControl.Interfaces object defined in the iControl Library for Java. This must be initialized with the connection information (hostname, username, and password) to the BIG-IP. I’ve then included some code to create a new pool, query some of it’s attributes, and then delete it. 1: // Initialize the iControl Interfaces Object 2: iControl.Interfaces interfaces = new iControl.Interfaces(); 3: interfaces.initialize("bigip_addr", "username", "password"); 4: 5: // Call the static getList method to return the list of existing pools. 6: String [] poolList = iControl.Objects.LTM.Pool.getList(interfaces); 7: 8: // Create a new Pool object with the name dummyPool. 9: iControl.Objects.LTM.Pool pool = new iControl.Objects.LTM.Pool(interfaces, "dummyPool"); 10: 11: // Create pool 12: iControl.CommonIPPortDefinition[] membersToAdd = new iControl.CommonIPPortDefinition[1]; 13: membersToAdd[0] = new iControl.CommonIPPortDefinition("10.10.10.10", 80); 14: pool.create(iControl.LocalLBLBMethod.LB_METHOD_ROUND_ROBIN, membersToAdd); 15: 16: System.out.println("Pool " + poolName + " created..."); 17: 18: // Query LB Method 19: iControl.LocalLBLBMethod lbMethod = pool.getLBMethod(); 20: System.out.println(" + LB Method : " + lbMethod.toString()); 21: 22: 23: // Query pool members 24: iControl.CommonIPPortDefinition [] membersInPool = pool.getMembers(); 25: System.out.println(" + Members"); 26: for(int i=0; i<membersInPool.length; i++) 27: { 28: System.out.println(" [" + i + "] : " + 29: membersInPool[i].getAddress() + ":" + membersInPool[i].getPort()); 30: } 31: 32: // Query statistics 33: iControl.CommonStatistic [] poolStatistics = pool.getStatistics(null); 34: System.out.println(" + Statistics"); 35: for(int i=0; i<poolStatistics.length; i++) 36: { 37: iControl.CommonStatisticType type = poolStatistics[i].getType(); 38: iControl.CommonULong64 ul64 = poolStatistics[i].getValue(); 39: StatisticValue sv = new StatisticValue(ul64); 40: 41: System.out.println(" [" + type + "] : " + sv.doubleValue()); 42: } 43: 44: // Delete pool 45: pool.remove(); 46: System.out.println("Pool " + poolName + " deleted..."); View The Source The source code can be found in the iControl CodeShare under JavaObjectLtmPool. Related Articles on DevCentral DevCentral Wiki: iControl Wiki Home Audio White Paper - F5 iControl DevCentral Wiki: Java Pool Member Control Custom BIG-IP Object MetaData With Data Groups > DevCentral > F5 ... Getting Started With iControl And Java – Setting Up Eclipse ... SSL Trust Provider for Java iControl 101 #22 - GTM Data Centers > DevCentral > F5 DevCentral ... How to instrument your Java EE applications for a virtualized ... Technorati Tags: Java, iControl, Object, Joe Pruitt407Views0likes4Comments