Forum Discussion

Steffen_Beach_8's avatar
Steffen_Beach_8
Icon for Nimbostratus rankNimbostratus
Feb 15, 2010

Polling all current connections from all default pools

So I've been playing around with iControl in preparation of building a custom web interface for my NOC and deployment tool for us engineers. While doing what I would think would be a simple query (snippet below) I've kicked the snot out of my ltm... It times out if I try to collect all data, and there is a nasty spike in CPU. This is on an 8900, which I would think could process this request no problemo.

 

 

Here is the summary of objects:

 

 

Virtual Servers 166

 

Pools 246

 

Nodes 1196

 

 

 

My question is, why does this cause such havoc, and what would be a better approach to getting what I need?

 

My initial thought to making things more manageable is storing some data (virtual server/pool names) in a DB and performing my own batch calls. More work that I thought would be needed given that the interface accepts (forces) array data types for multi object lookups.

 

 

 

Here is my code snippet:

 

 

 static void Main(string[] args)  
          {  
              Interfaces m_interfaces = new Interfaces();  
              string ipAddress = "********";  
              string user = "**********";  
              string pass = "********";  
              //Initalize the f5 interface  
              m_interfaces.initialize(ipAddress, user, pass);  
                
              //Check if inialized  
              if (!m_interfaces.initialized)  
                  Environment.Exit(Environment.ExitCode);  
    
              //Get the partition list to drop into PROD  
              ManagementPartitionAuthZPartition[] partitionList = m_interfaces.ManagementPartition.get_partition_list();  
              for (int i = 0; i < partitionList.Length; i++)  
              {  
                  if (partitionList[ i ].partition_name == "PROD")  
                      m_interfaces.ManagementPartition.set_active_partition(partitionList[ i ].partition_name);  
              }  
    
              // TEST DATA  
              //string[] vsName = new string[1];  
              //vsName[0] = "vs_products_http";  
              //string[] localDefaultPools = m_interfaces.LocalLBVirtualServer.get_default_pool_name(vsName);  
              // END TEST DATA  
    
              //Get all virtual servers  
              string[] localVirtualServers = m_interfaces.LocalLBVirtualServer.get_list();  
              //Get all the active default pools  
              string[] localDefaultPools = m_interfaces.LocalLBVirtualServer.get_default_pool_name(localVirtualServers);  
    
              //Get the pool stats  
              LocalLBPoolPoolStatistics poolStats = m_interfaces.LocalLBPool.get_statistics(localDefaultPools);  
              //Get the pool stat entries  
              LocalLBPoolPoolStatisticEntry[] poolStatEntries = poolStats.statistics;  
    
              //Loop through each pool and print out the server side current connection stats  
              for (int i = 0; i < poolStatEntries.Length; i++)   
              {  
                  CommonStatistic[] commonStats = poolStatEntries[ i ].statistics;  
    
                  for (int x = 0; x < commonStats.Length; x++)  
                  {  
                      if (commonStats[x].type == CommonStatisticType.STATISTIC_SERVER_SIDE_CURRENT_CONNECTIONS && poolStatEntries[ i ].pool_name != "")  
                      {  
                          CommonULong64 commonValue = commonStats[x].value;  
                          Console.WriteLine("Pool name: {0}  Current connections: {1}", poolStatEntries[ i ].pool_name, commonValue.low.ToString());   
                      }  
                        
                  }  
              }  
    
          }

7 Replies

  • BTW -- its timing out here: LocalLBPoolPoolStatistics poolStats = m_interfaces.LocalLBPool.get_statistics(localDefaultPools);
  • I figured out a way to get the data by breaking it out into batches, although, it's still gawd awfully slow!!!

    Here is the snippet if anyone wants:

      using System;   
       using System.Collections.Generic;   
       using System.Text;   
       using iControl;   
          
       namespace iControlInterface   
       {   
           class Program   
           {   
               static void Main(string[] args)   
               {   
                   Interfaces m_interfaces = new Interfaces();   
                   string ipAddress = "*******";   
                   string user = "*********";   
                   string pass = "**************";   
                   //Initalize the f5 interface   
                   m_interfaces.initialize(ipAddress, user, pass);   
                      
                   //Check if inialized   
                   if (!m_interfaces.initialized)   
                       Environment.Exit(Environment.ExitCode);   
          
                   //Get the partition list to drop into PROD   
                   ManagementPartitionAuthZPartition[] partitionList = m_interfaces.ManagementPartition.get_partition_list();   
                   for (int i = 0; i < partitionList.Length; i++)   
                   {   
                       if (partitionList[ i ].partition_name == "PROD")   
                           m_interfaces.ManagementPartition.set_active_partition(partitionList[ i ].partition_name);   
                   }   
          
                   // TEST DATA   
                   //string[] vsName = new string[1];   
                   //vsName[0] = "vs_products_http";   
                   //string[] localDefaultPools = m_interfaces.LocalLBVirtualServer.get_default_pool_name(vsName);   
                   // END TEST DATA   
          
                   //Get all virtual servers   
                   string[] localVirtualServers = m_interfaces.LocalLBVirtualServer.get_list();   
                   //PrintInfo(localVirtualServers);   
                   //Get all the active default pools   
                   string[] localDefaultPools = m_interfaces.LocalLBVirtualServer.get_default_pool_name(localVirtualServers);   
                   //PrintInfo(localDefaultPools);   
          
                   //Get the pool stats   
                   LocalLBPoolPoolStatistics poolStats = new LocalLBPoolPoolStatistics();   
                   //Get the pool stat entries   
                   LocalLBPoolPoolStatisticEntry[] poolStatEntries;   
          
                   List localDefaultPoolsList = new List(localDefaultPools);   
                   string[] poolBatch;   
                   while(localDefaultPoolsList.Count > 0)   
                   {   
                       if(localDefaultPoolsList.Count >= 10)   
                       {   
                           poolBatch = localDefaultPoolsList.GetRange(localDefaultPoolsList.Count - 10, 10).ToArray();   
                           localDefaultPoolsList.RemoveRange(localDefaultPoolsList.Count - 10, 10);   
                           poolStats = m_interfaces.LocalLBPool.get_statistics(poolBatch);   
                           poolStatEntries = poolStats.statistics;   
                           PrintStatistics(poolStatEntries);   
                       }   
                       else   
                       {   
                           poolBatch = localDefaultPoolsList.ToArray();   
                           localDefaultPoolsList.Clear();   
                           poolStats = m_interfaces.LocalLBPool.get_statistics(poolBatch);   
                           poolStatEntries = poolStats.statistics;   
                           PrintStatistics(poolStatEntries);   
                       }   
          
                   }   
          
               }   
          
               public static void PrintInfo(string[] info)    
               {   
                   for (int i = 0; i > info.Length; i++)    
                   {   
                       Console.WriteLine(info[ i ].ToString());   
                   }   
               }   
               public static void PrintStatistics(LocalLBPoolPoolStatisticEntry[] poolStatEntries)    
               {   
                   //Loop through each pool and print out the server side current connection stats   
                   for (int i = 0; i < poolStatEntries.Length; i++)   
                   {   
                       CommonStatistic[] commonStats = poolStatEntries[ i ].statistics;   
          
                       for (int x = 0; x < commonStats.Length; x++)   
                       {   
                           if (commonStats[x].type == CommonStatisticType.STATISTIC_SERVER_SIDE_CURRENT_CONNECTIONS && poolStatEntries[ i ].pool_name != "")   
                           {   
                               CommonULong64 commonValue = commonStats[x].value;   
                               Console.WriteLine("Pool name: {0}  Current connections: {1}", poolStatEntries[ i ].pool_name, commonValue.low.ToString());   
                           }   
          
                       }   
                   }   
               }   
           }   
       }   
  • Okay, now I've really have the problem knocked out. Apparently I was getting back no name pools (I believe for virtul servers with no default pool assigned) and when trying to fetch stats for and empty object the iControl method for get_statistics doesn't play nice. Anyway, I added in a quick method to prune out empty objects from the .get_default_pool_name method, and presto, the stats collection went from just over 3 minutes down to just under 2 seconds! Massive improvement!

     

     

    Here is the final test (not super elegant, and still has some debugging info and a timer) code if anyone wants:

     

     

     

     using System;  
      using System.Collections.Generic;  
      using System.Text;  
      using iControl;  
      using System.Diagnostics;  
        
      namespace iControlInterface  
      {  
          class Program  
          {  
              static void Main(string[] args)  
              {  
                  Interfaces m_interfaces = new Interfaces();  
                  string ipAddress = "*******";  
                  string user = "*******";  
                  string pass = "*******";  
                  //Initalize the f5 interface  
                  m_interfaces.initialize(ipAddress, user, pass);  
                    
                  //Check if inialized  
                  if (!m_interfaces.initialized)  
                      Environment.Exit(Environment.ExitCode);  
        
                  //Get the partition list to drop into PROD  
                  ManagementPartitionAuthZPartition[] partitionList = m_interfaces.ManagementPartition.get_partition_list();  
                  for (int i = 0; i < partitionList.Length; i++)  
                  {  
                      if (partitionList[ i ].partition_name == "PROD")  
                          m_interfaces.ManagementPartition.set_active_partition(partitionList[ i ].partition_name);  
                  }  
        
                  // TEST DATA  
                  //string[] vsName = new string[1];  
                  //vsName[0] = "vs_products_http";  
                  //string[] localDefaultPools = m_interfaces.LocalLBVirtualServer.get_default_pool_name(vsName);  
                  // END TEST DATA  
                    
                  //Get all virtual servers  
                  string[] localVirtualServers = m_interfaces.LocalLBVirtualServer.get_list();  
                  //PrintInfo(localVirtualServers);  
                  //Get all the active default pools  
                  string[] localDefaultPools = m_interfaces.LocalLBVirtualServer.get_default_pool_name(localVirtualServers);  
                  //PrintInfo(localDefaultPools);  
        
                  //Get the pool stats  
                  LocalLBPoolPoolStatistics poolStats = new LocalLBPoolPoolStatistics();  
                  //Get the pool stat entries  
                  LocalLBPoolPoolStatisticEntry[] poolStatEntries;  
        
                  //Load the localDefaultPools into a List object to prune and perform batch get_statistics with  
                  List localDefaultPoolsList = RemoveNoNamePools(localDefaultPools);  
                  string[] poolBatch;  
                  int batchCount = 1;  
                  Stopwatch timer = new Stopwatch();  
                  timer.Start();  
        
                  //Break the statistics rquests into batchs of XX  
                  while(localDefaultPoolsList.Count > 0)  
                  {  
                      if(localDefaultPoolsList.Count >= 10)  
                      {  
                          poolBatch = localDefaultPoolsList.GetRange(localDefaultPoolsList.Count - 10, 10).ToArray();  
                          localDefaultPoolsList.RemoveRange(localDefaultPoolsList.Count - 10, 10);  
                          poolStats = m_interfaces.LocalLBPool.get_statistics(poolBatch);  
                          poolStatEntries = poolStats.statistics;  
                          PrintStatistics(poolStatEntries);  
                      }  
                      else  
                      {  
                          poolBatch = localDefaultPoolsList.ToArray();  
                          localDefaultPoolsList.Clear();  
                          poolStats = m_interfaces.LocalLBPool.get_statistics(poolBatch);  
                          poolStatEntries = poolStats.statistics;  
                          PrintStatistics(poolStatEntries);  
                      }  
                      Console.WriteLine("Batch {0} time elapsed: {1}", batchCount.ToString(), timer.Elapsed.ToString());  
                      batchCount++;  
                  }  
                  Console.WriteLine("Total time elapsed: {1}", batchCount.ToString(), timer.Elapsed.ToString());  
                  timer.Stop();  
        
              }  
        
              private static List RemoveNoNamePools(string[] localDefaultPools)  
              {  
                  List localDefaultPoolsList = new List();  
                  foreach (string poolName in localDefaultPools)  
                  {  
                      if (poolName != "")  
                          localDefaultPoolsList.Add(poolName);  
                  }  
                  return localDefaultPoolsList;  
              }  
        
              public static void PrintInfo(string[] info)   
              {  
                  for (int i = 0; i > info.Length; i++)   
                  {  
                      Console.WriteLine(info[ i ].ToString());  
                  }  
              }  
              public static void PrintStatistics(LocalLBPoolPoolStatisticEntry[] poolStatEntries)   
              {  
                  //Loop through each pool and print out the server side current connection stats  
                  for (int i = 0; i < poolStatEntries.Length; i++)  
                  {  
                      CommonStatistic[] commonStats = poolStatEntries[ i ].statistics;  
        
                      for (int x = 0; x < commonStats.Length; x++)  
                      {  
                          if (commonStats[x].type == CommonStatisticType.STATISTIC_SERVER_SIDE_CURRENT_CONNECTIONS && poolStatEntries[ i ].pool_name != "")  
                          {  
                              CommonULong64 commonValue = commonStats[x].value;  
                              Console.WriteLine("Pool name: {0}  Current connections: {1}", poolStatEntries[ i ].pool_name, commonValue.low.ToString());  
                          }  
        
                      }  
                  }  
              }  
          }  
      }

     

  • Steffen, sorry I didn't get back to you on your first post but I'm glad you got things worked out. Let me be the first to say Thank You for continuing on with your thread and posting your solution. It's always easier to just continue on with your work but the time it took for you to post your working solution will undoubtedly help someone else down the road. If you don't mind, I'm going to put this as a CodeShare entry in the iControl CodeShare.

     

     

    Thanks again for following up on your initial post and please don't hesitate to ask more questions down the road.

     

     

    -Joe
  • Oh, one more thing after looking at your code. You might want to convert that Common::ULong64 value into a native 64 bit number. You might be safe with the Current Connections stat on the lower 32 bits but for total counts, it will likely go past the 2^32 barrier quite often.

     

     

    -Joe
  • Thanks Joe. Instead of storing the CommonStat value in an object, I'm just converting the .low value directly to a string and outputting that.

    Here's a more polished version:

     using System;  
      using System.Collections.Generic;  
      using System.Text;  
      using iControl;  
      using System.Diagnostics;  
        
      namespace PRVD.iControl.GetAllPoolCurrentConnections  
      {  
          class Program  
          {  
              static void Main(string[] args)  
              {  
                  if (args.Length < 3 || string.IsNullOrEmpty(args[0]) || string.IsNullOrEmpty(args[1]) || string.IsNullOrEmpty(args[2]))  
                  {  
                      Console.WriteLine(  
                        "Usage: f5GetAllPoolCurrentConnections [username] [password] [hostname or ip address] optional [partition]");  
                      return;  
                  }  
                  string user = args[0];  
                  string pass = args[1];  
                  string ipAddress = args[2];  
                  string partition = "";  
                  if (args.Length > 3 && !string.IsNullOrEmpty(args[3]))  
                      partition = args[3];  
        
                  Interfaces m_interfaces = new Interfaces();  
                  m_interfaces.initialize(ipAddress, user, pass);  
                    
                  //Check if inialized  
                  if (!m_interfaces.initialized)  
                      Environment.Exit(Environment.ExitCode);  
        
                  //Get the partition list to drop into PROD  
                  bool partitionFound = false;  
                  ManagementPartitionAuthZPartition[] partitionList = m_interfaces.ManagementPartition.get_partition_list();  
                  for (int i = 0; i < partitionList.Length; i++)  
                  {  
                      Console.WriteLine("Partition found: {0}", partitionList[ i ].partition_name);  
        
                      if (partitionList[ i ].partition_name == partition)  
                      {  
                          m_interfaces.ManagementPartition.set_active_partition(partitionList[ i ].partition_name);  
                          partitionFound = true;  
                      }  
                  }  
                  if (!partitionFound)  
                  {  
                      Console.WriteLine("The partition '{0}' specified was not found.", partition);  
                      return;  
                  }  
                    
                  //Get all virtual servers  
                  string[] localVirtualServers = m_interfaces.LocalLBVirtualServer.get_list();  
                  //PrintInfo(localVirtualServers);  
                  //Get all the active default pools  
                  string[] localDefaultPools = m_interfaces.LocalLBVirtualServer.get_default_pool_name(localVirtualServers);  
                  //PrintInfo(localDefaultPools);  
        
                  //Get the pool stats  
                  LocalLBPoolPoolStatistics poolStats = new LocalLBPoolPoolStatistics();  
                  //Get the pool stat entries  
                  LocalLBPoolPoolStatisticEntry[] poolStatEntries;  
        
                  //Load the localDefaultPools into a List object to prune and perform batch get_statistics with  
                  List localDefaultPoolsList = RemoveNoNamePools(localDefaultPools);  
                  string[] poolBatch;  
                  int batchCount = 1;  
                  Stopwatch timer = new Stopwatch();  
                  timer.Start();  
        
                  //Break the statistics rquests into batchs of XX  
                  while(localDefaultPoolsList.Count > 0)  
                  {  
                      if(localDefaultPoolsList.Count >= 10)  
                      {  
                          poolBatch = localDefaultPoolsList.GetRange(localDefaultPoolsList.Count - 10, 10).ToArray();  
                          localDefaultPoolsList.RemoveRange(localDefaultPoolsList.Count - 10, 10);  
                          poolStats = m_interfaces.LocalLBPool.get_statistics(poolBatch);  
                          poolStatEntries = poolStats.statistics;  
                          PrintStatistics(poolStatEntries);  
                      }  
                      else  
                      {  
                          poolBatch = localDefaultPoolsList.ToArray();  
                          localDefaultPoolsList.Clear();  
                          poolStats = m_interfaces.LocalLBPool.get_statistics(poolBatch);  
                          poolStatEntries = poolStats.statistics;  
                          PrintStatistics(poolStatEntries);  
                      }  
                      //Console.WriteLine("Batch {0} time elapsed: {1}", batchCount.ToString(), timer.Elapsed.ToString());  
                      batchCount++;  
                  }  
                  Console.WriteLine("Total time elapsed: {1}", batchCount.ToString(), timer.Elapsed.ToString());  
                  timer.Stop();  
        
              }  
        
              private static List RemoveNoNamePools(string[] localDefaultPools)  
              {  
                  List localDefaultPoolsList = new List();  
                  foreach (string poolName in localDefaultPools)  
                  {  
                      if (poolName != "")  
                          localDefaultPoolsList.Add(poolName);  
                  }  
                  return localDefaultPoolsList;  
              }  
        
              public static void PrintInfo(string[] info)   
              {  
                  for (int i = 0; i > info.Length; i++)   
                  {  
                      Console.WriteLine(info[ i ].ToString());  
                  }  
              }  
              public static void PrintStatistics(LocalLBPoolPoolStatisticEntry[] poolStatEntries)   
              {  
                  //Loop through each pool and print out the server side current connection stats  
                  for (int i = 0; i < poolStatEntries.Length; i++)  
                  {  
                      CommonStatistic[] commonStats = poolStatEntries[ i ].statistics;  
        
                      for (int x = 0; x < commonStats.Length; x++)  
                      {  
                          if (commonStats[x].type == CommonStatisticType.STATISTIC_SERVER_SIDE_CURRENT_CONNECTIONS && poolStatEntries[ i ].pool_name != "")  
                          {  
                              Console.WriteLine("Pool name: {0}  Current connections: {1}", poolStatEntries[ i ].pool_name, commonStats[x].value.low.ToString());  
                          }  
        
                      }  
                  }  
              }  
          }  
      }

  • Thanks Steffan. I've created a CodeShare entry with this app in the iControl CodeShare wiki

     

     

    http://devcentral.f5.com/wiki/default.aspx/iControl/CSharpPollingDefaultPoolConnections.html

     

    Click here

     

     

     

    Feel free to update it there if you make additional changes.

     

     

    -Joe