Welcome!

Java IoT Authors: Elizabeth White, Yeshim Deniz, Zakia Bouachraoui, Liz McMillan, Pat Romanski

Blog Feed Post

Monitor Your iRule Performance with iControl and Google-O-Meter Charts

iRule performance has long been a question our users have asked about.  We get questions all the time about how to determine what impact a specific iRule has on the normal operation of the BIG-IP’s they are running on.  The question is a hard one to answer as we rarely see two iRules that are the same and the mere fact that you can code your iRule however you want, makes it very hard to provide info on the impact they would have in a generic manner.

A while back we wrote a tech tip on optimizing iRules titled “Evaluating iRule Performance” in which we illustrate how to enable timing metrics on an iRule to help report the number of CPU cycles the iRule is using up during it’s processing.  We make these metrics available in the BIG-IP CLI and GUI as well as from the iControl API.  This makes it easy to integrate them into external applications such as the iRule Editor.

I’m always thinking of ways to visualize the metrics we can get from iControl and I got to thinking about the article I wrote a while back on Building an iControl PowerShell Monitoring Dashboard with Google Charts and looked again at the Google Charts API to see if anything new had cropped up recently.

For those that don’t know about Google Charts, Google has made available an awesome, in my opinion, API to enable the dynamic creation of charts to include with your own web applications.  At the time of this article, they offer Bar charts, Box charts, Candlestick charts, Compound charts, Dynamic icons, Formulas, GraphViz charts, Line charts, Map charts, Pie charts, QR codes, Radar charts, Scatter charts, Venn charts, and, the one I decided to use in this article, Google-O-Meter charts.

iRule Metrics

By using the “timing on” command within an iRule, several metrics can be retrieved from the LocalLB.Rule.get_statistics() method.  These values are:

  • STATISTIC_RULE_MINIMUM_CYCLES - The minimum number of clock cycles spent during an execution of the rule event.
  • STATISTIC_RULE_AVERAGE_CYCLES - The average number of clock cycles spent during an execution of the rule event.
  • STATISTIC_RULE_MAXIMUM_CYCLES - The maximum number of clock cycles spent during an execution of the rule event.
  • STATISTIC_RULE_ABORTS - The number of aborts due to TCL errors.
  • STATISTIC_RULE_FAILURES - The number of times the iRule has failed for any reason.
  • STATISTIC_RULE_TOTAL_EXECUTIONS - The number of times the iRule event has been executed.

These metrics are on an event by event basis so that means we can get detailed results for each of the events (HTTP_REQUEST, STREAM_MATCHED, etc) used within a given iRule.  From these metrics, along with the speed of the CPU on the target platform, one can calculate metrics such as

  • CPU Cycles/Request
  • Runtime (ms)/Request
  • Percent CPU Usage/Request

Google-O-Meter charts

Google-O-Meter charts are in the form of a gauge and a simple one looks like the image on the right.  It provides a minimum and maximum for the gauge along with a value for the “arms”.  You have full control over the colors and labels so I figured this would be a great way to represent the various metrics we can retrieve from iRules.

With the metrics returned from the iControl API, we could build a dashboard with these charts that illustrated in real-time how the iRule was performing.

The Code

I will follow the model in my previous dashboard example by building an engine in PowerShell that does the data collection and processing.  It will then use the automation features in Internet Explorer to dynamically create a browser instance and feed it with the output from the engine.  The engine will continue processing in a loop and feed the updated statistics to the browser allowing for a dynamically refreshing dashboard.

The script will take the following parameters

  • BIGIP – The address of your BIGIP. 
  • User – The admin user for your BIGIP.
  • Pass – The admin password for your BIGIP.
  • VirtualServer – An optional virtual server name to filter the results on.
  • iRule – An optional iRule name to filter the results on.
  • Metric – The metric you would like reported (“CYCLES”, “RUNTIME”, “CPUUSAGE”, “ALL”)
  • Debug – If set to $true, debug information is displayed to the console during the script operation.
  • Interval – The frequency in seconds between polls.  The default value is 10 (seconds)

The main logic for the engine is a while loop in which data is collected with the Get-Data function and pumped to the instance of Internet Explorer with the Refresh-Browser function.  The script then sleeps during the specified sleep interval and then repeats the procedure.

   1: function Run-Dashboard()
   2: {
   3:   while($true)
   4:   {
   5:     Write-Host "Requesting data..."
   6:     $file_data = Get-Data;
   7:     
   8:     Refresh-Browser $file_data;
   9:     Start-Sleep $script:INTERVAL;
  10:   }
  11: }

Retrieving Data

The Get-Data function is really the heart of this application.  This function requests the list of iRules assigned as resources to the specified Virtual Server(s) as well as the metrics for all the iRules on the system.  I chose to make a single call here to reduce the processing speed of the function.  I could have filtered the results and only queried the metrics or the iRules associated with Virtuals but took the easy road with the get_all_statistics() method.  I’ll leave it an exercise for the reader to make this change.

The script then loops through the virtual servers and builds a table report for each one.  The format of the table each row being an iRule and each column representing the events that are used within that iRule.  I wrote some code to calculate all of the types of events in all iRules for the given virtual server so that we could have a nice layout.

The presentation (HTML) for the dashboard is stored in the $page_data variable and returned to the calling code.

   1: function Get-Data()
   2: {
   3:   $now = [DateTime]::Now;
   4:  
   5:   $page_data = "<html><head><title>$($script:TITLE)</title>
   6:   <style type='text/css'>
   7:     body,td,th { font-family: Tahoma; font-size: 10pt; }
   8:     .datatable { background-color: #C0C0C0; }
   9:     .colheader { background-color: cyan; }
  10:     .rowheader { background-color: yellow; }
  11:   </style>
  12:   </head>
  13:   <center><h1>iRule Monitor</h1><br/><h2>$now</h2>";
  14:  
  15:   $vslist = [string[]]$(Get-VirtualServerList);
  16:   
  17:   $VirtualServerRuleAofA = (Get-F5.iControl).LocalLBVirtualServer.get_rule( $vslist );
  18:   $RuleStatistics = (Get-F5.iControl).LocalLBRule.get_all_statistics();
  19:   
  20:   # loop through all the virtual servers
  21:   for($i=0; $i -lt $VirtualServerRuleAofA.Length; $i++)
  22:   {
  23:     $vs = $vslist[$i];
  24:     
  25:     # rules for current vs
  26:     $VirtualServerRuleA = $VirtualServerRuleAofA[$i];
  27:     
  28:     # rule stats for current vs
  29:     $RuleStatisticEntryA = Filter-RuleStatistics $VirtualServerRuleA $RuleStatistics;
  30:     if ( $RuleStatisticEntryA -ne $null )
  31:     {
  32:       # Build out a column list...
  33:       $columns = @{};
  34:       foreach($RuleStatisticEntry in $RuleStatisticEntryA)
  35:       {
  36:         if ( ($columns.count -eq 0) -or ($null -eq $columns[$RuleStatisticEntry.event_name]) )
  37:         {
  38:           $columns.Add($RuleStatisticEntry.event_name, 0);
  39:         }
  40:       }
  41:       $scolumns = $columns.GetEnumerator() | Sort-Object Name;
  42:       
  43:       # Build out result graphs
  44:       
  45:       Write-DebugMessage "VIRTUAL SERVER $vs";
  46:       
  47:       $page_data += "<table border='1' class='datatable'>"
  48:       $page_data += "<tr><th colspan='$($columns.Count + 1)'>Virtual Server '$vs'</th></tr>";
  49:       
  50:       $page_data += "<tr><th>iRule</th>"
  51:       foreach($key in $scolumns)
  52:       {
  53:         $page_data += "<th class='colheader'>$($key.Name)</th>";
  54:       }
  55:       $page_data += "</tr>";
  56:  
  57:       # loop through all rules for current  virtual server
  58:       foreach($VirtualServerRule in $VirtualServerRuleA)
  59:       {
  60:         if ( ($null -eq $script:IRULE) -or ($VirtualServerRule.rule_name -eq $script:IRULE) )
  61:         {
  62:           $page_data += "<tr>";
  63:           $page_data += "<td class='rowheader'>$($VirtualServerRule.rule_name)</td>";
  64:           
  65:           foreach($key in $scolumns)
  66:           {
  67:             $aborts = $null;
  68:             $avg_cycles = $null;
  69:             $failures = $null;
  70:             $max_cycles = $null;
  71:             $min_cycles = $null;
  72:             $total_executions = $null;
  73:             
  74:             foreach($RuleStatisticEntry in $RuleStatisticEntryA)
  75:             {
  76:               if ( ($RuleStatisticEntry.rule_name -eq $VirtualServerRule.rule_name) -and
  77:                    ($RuleStatisticEntry.event_name -eq $key.Name) )
  78:               {
  79:                 Write-DebugMessage "Searching for '$($RuleStatisticEntry.rule_name), $($RuleStatisticEntry.event_name)...";
  80:                 # Found a match for the row and column
  81:                 $min_cycles = Extract-Statistic $RuleStatisticEntry.statistics "STATISTIC_RULE_MINIMUM_CYCLES";
  82:                 $avg_cycles = Extract-Statistic $RuleStatisticEntry.statistics "STATISTIC_RULE_AVERAGE_CYCLES";
  83:                 $max_cycles = Extract-Statistic $RuleStatisticEntry.statistics "STATISTIC_RULE_MAXIMUM_CYCLES";
  84:                 $aborts = Extract-Statistic $RuleStatisticEntry.statistics "STATISTIC_RULE_ABORTS";
  85:                 $failures = Extract-Statistic $RuleStatisticEntry.statistics "STATISTIC_RULE_FAILURES";
  86:                 $total_executions = Extract-Statistic $RuleStatisticEntry.statistics "STATISTIC_RULE_TOTAL_EXECUTIONS";
  87:                 break;
  88:               }
  89:             }
  90:             
  91:             $min = 0;
  92:             $data_value = $null;
  93:             $max = 5000;
  94:             
  95:             $speedMhz = [Convert]::ToDouble($script:CPUSPEED);
  96:             $speed = [Convert]::ToUInt64(1000000 * $speedMhz);
  97:  
  98:             $cycles_min = $min_cycles;
  99:             $cycles_avg = $avg_cycles;
 100:             $cycles_max = $max_cycles;
 101:  
 102:             $runtime_min = [Convert]::ToDouble($min_cycles) * (1000.0/$speed);
 103:             $runtime_avg = [Convert]::ToDouble($avg_cycles) * (1000.0/$speed);
 104:             $runtime_max = [Convert]::ToDouble($max_cycles) * (1000.0/$speed);
 105:  
 106:             $cpu_min = 0;
 107:             #$min = 100.0 * ([Convert]::ToDouble($min_cycles) / [Convert]::ToDouble($speed));
 108:             $cpu_avg = 100.0 * ([Convert]::ToDouble($avg_cycles) / [Convert]::ToDouble($speed));
 109:             #$max = 100.0 * ([Convert]::ToDouble($max_cycles) / [Convert]::ToDouble($speed));
 110:             $cpu_max = 100;
 111:             
 112:             if ( 0 -eq $data_value ) { $data_value = $null; }
 113:             if ( $null -ne $data_value ) { $data_value = $data_value.ToString("0.0000"); }
 114:             
 115:             $cycles_chart = Get-Chart $cycles_min $cycles_avg $cycles_max "CYCLES";
 116:             $runtime_chart = Get-Chart $runtime_min $runtime_avg $runtime_max "RUNTIME" "0.00000";
 117:             $cpu_chart = Get-Chart $cpu_min $cpu_avg $cpu_max "CPUUSAGE" "0.00000";
 118:             $success_chart = Get-Chart 0 ($aborts + $failures) $total_executions "ERRORS";
 119:  
 120:             Write-DebugMessage $charturl;
 121:             
 122:             $page_data += "<td>";
 123:  
 124:             switch($script:METRIC)
 125:             {
 126:               "CYCLES" {
 127:                 $page_data += "$cycles_chart";
 128:                 $page_data += $success_chart;
 129:               }
 130:               "RUNTIME" {
 131:                 $page_data += "$runtime_chart";
 132:                 $page_data += $success_chart;
 133:               }
 134:               "CPUUSAGE" {
 135:                 $page_data += "$cpu_chart";
 136:                 $page_data += $success_chart;
 137:               }
 138:               "ALL" {
 139:                 $page_data += $cycles_chart;
 140:                 $page_data += $runtime_chart;
 141:                 $page_data += $cpu_chart;
 142:                 $page_data += $success_chart;
 143:               }
 144:               default {
 145:                 $page_data += "$cycles_chart";
 146:               }
 147:             }
 148:             $page_data += "</td>";
 149:           }
 150:           $page_data += "</tr>";
 151:         }
 152:       }
 153:       $page_data += "</table><p>"
 154:     }
 155:   }
 156:   
 157:   $page_data += "</center>";
 158:   
 159:   return $page_data;
 160: }

Generating The Chart

I wrote a little helper function to generate the image reference for the Google-O-Meter chart.  It takes as input the min value for the chart, the value for the arrow, the max value for the chart, the type of chart (for the label) as well as an optional data format specifier (for those pesky small double values).

   1: function Get-Chart()
   2: {
   3:   param($min, $val, $max, $type, $fmt);
   4:   
   5:   $chart = "";
   6:   Write-DebugMessage "Querying chart for '$min', '$val', '$max', '$type'...";
   7:   
   8:   if ( ($null -ne $val) -and ($null -ne $fmt) )
   9:   {
  10:     $val = $val.ToString($fmt);
  11:   }
  12:   
  13:   $suffix = "";
  14:   switch($type)
  15:   {
  16:     "CYCLES" { $suffix = ""; }
  17:     "RUNTIME" { $suffix = " ms."; }
  18:     "CPUUSAGE" { $suffix = "%"; }
  19:   }
  20:   
  21:   $prefix = Get-ChartPrefix;
  22:   $charturl = "http://${prefix}chart.apis.google.com/chart?cht=gom";
  23:   
  24:   $charturl += "&chs=$($script:CHARTSIZE)";
  25:   $charturl += "&chco=00FF00,FF0000";
  26:   $charturl += "&chds=$min,$max";
  27:   $charturl += "&chd=t:$val";
  28:   $charturl += "&chxt=x,y";
  29:   $charturl += "&chxl=0:|$val$suffix|1:|0||$max";
  30:   $charturl += "&chf=c,lg,45,FFE7C6,0,76A4FB,0.75";
  31:   
  32:   Write-DebugMessage "returning chart: $charturl...";
  33:  
  34:   $chart += "<img src='$charturl'/>";
  35:   switch($type)
  36:   {
  37:     "CYCLES" {
  38:       $chart += "<br/><center>CPU Cycles/Request</center>";
  39:     }
  40:     "RUNTIME" {
  41:       $chart += "<br/><center>Runtime (ms)/Request</center>";
  42:     }
  43:     "CPUUSAGE" {
  44:       $chart += "<br/><center>Percent CPU Usage/Request</center>";
  45:     }
  46:     "ERRORS" {
  47:       $chart += "<br/><center>Total Errors</center>";
  48:     }
  49:   }
  50:   
  51:   $chart += "<br/>";
  52:     
  53:   return $chart;
  54: }

Feeding Results To The Browser

The Refresh-Browser function will feed the data to the browser.  It first checks to see if a browser instance has been created and, if not, creates it.  It then updates the browsers Document.DocumentElement.lastChild.InnerHTML property to dynamically update the contents of the page.  This causes the page to refresh itself.

   1: function Refresh-Browser()
   2: {
   3:   param($file_data);
   4:   
   5:   if ( $null -eq $script:BROWSER )
   6:   {
   7:     Write-DebugMessage "Creating new Browser"
   8:     $script:BROWSER = New-Object -com InternetExplorer.Application;
   9:     $script:BROWSER.Navigate2("About:blank");
  10:     $script:BROWSER.Visible = $true;
  11:     $script:BROWSER.TheaterMode = $script:THEATER;
  12:   }
  13:   $docBody = $script:BROWSER.Document.DocumentElement.lastChild;
  14:   $docBody.InnerHTML = $file_data;
  15: }

The Results

The following is how the application looks when run with the following parameters.  I’ve specified to monitor the virtual server ‘DC5’ and only the ‘dc.dc5_stream’ iRule.  It will present graphs for all the available metrics.

   1: PS> .\PsiControlDashboard.ps1 -BIGIP bigip -User user -Pass pass -VirtualServer DC5 -iRule "dc.dc5_stream" -Metric "ALL"

 

tt_irulemonitor_3

Get The Code

The full PowerShell script is available in the iControl CodeShare under PsIruleDashboard.

 

Related Articles on DevCentral

Read the original blog entry...

More Stories By Joe Pruitt

Joe Pruitt is a Principal Strategic Architect at F5 Networks working with Network and Software Architects to allow them to build network intelligence into their applications.

IoT & Smart Cities Stories
At CloudEXPO Silicon Valley, June 24-26, 2019, Digital Transformation (DX) is a major focus with expanded DevOpsSUMMIT and FinTechEXPO programs within the DXWorldEXPO agenda. Successful transformation requires a laser focus on being data-driven and on using all the tools available that enable transformation if they plan to survive over the long term. A total of 88% of Fortune 500 companies from a generation ago are now out of business. Only 12% still survive. Similar percentages are found throug...
In his general session at 19th Cloud Expo, Manish Dixit, VP of Product and Engineering at Dice, discussed how Dice leverages data insights and tools to help both tech professionals and recruiters better understand how skills relate to each other and which skills are in high demand using interactive visualizations and salary indicator tools to maximize earning potential. Manish Dixit is VP of Product and Engineering at Dice. As the leader of the Product, Engineering and Data Sciences team at D...
As you know, enterprise IT conversation over the past year have often centered upon the open-source Kubernetes container orchestration system. In fact, Kubernetes has emerged as the key technology -- and even primary platform -- of cloud migrations for a wide variety of organizations. Kubernetes is critical to forward-looking enterprises that continue to push their IT infrastructures toward maximum functionality, scalability, and flexibility. As they do so, IT professionals are also embr...
The Japan External Trade Organization (JETRO) is a non-profit organization that provides business support services to companies expanding to Japan. With the support of JETRO's dedicated staff, clients can incorporate their business; receive visa, immigration, and HR support; find dedicated office space; identify local government subsidies; get tailored market studies; and more.
At CloudEXPO Silicon Valley, June 24-26, 2019, Digital Transformation (DX) is a major focus with expanded DevOpsSUMMIT and FinTechEXPO programs within the DXWorldEXPO agenda. Successful transformation requires a laser focus on being data-driven and on using all the tools available that enable transformation if they plan to survive over the long term. A total of 88% of Fortune 500 companies from a generation ago are now out of business. Only 12% still survive. Similar percentages are found throug...
Atmosera delivers modern cloud services that maximize the advantages of cloud-based infrastructures. Offering private, hybrid, and public cloud solutions, Atmosera works closely with customers to engineer, deploy, and operate cloud architectures with advanced services that deliver strategic business outcomes. Atmosera's expertise simplifies the process of cloud transformation and our 20+ years of experience managing complex IT environments provides our customers with the confidence and trust tha...
AI and machine learning disruption for Enterprises started happening in the areas such as IT operations management (ITOPs) and Cloud management and SaaS apps. In 2019 CIOs will see disruptive solutions for Cloud & Devops, AI/ML driven IT Ops and Cloud Ops. Customers want AI-driven multi-cloud operations for monitoring, detection, prevention of disruptions. Disruptions cause revenue loss, unhappy users, impacts brand reputation etc.
As you know, enterprise IT conversation over the past year have often centered upon the open-source Kubernetes container orchestration system. In fact, Kubernetes has emerged as the key technology -- and even primary platform -- of cloud migrations for a wide variety of organizations. Kubernetes is critical to forward-looking enterprises that continue to push their IT infrastructures toward maximum functionality, scalability, and flexibility.
Today's workforce is trading their cubicles and corporate desktops in favor of an any-location, any-device work style. And as digital natives make up more and more of the modern workforce, the appetite for user-friendly, cloud-based services grows. The center of work is shifting to the user and to the cloud. But managing a proliferation of SaaS, web, and mobile apps running on any number of clouds and devices is unwieldy and increases security risks. Steve Wilson, Citrix Vice President of Cloud,...
When Enterprises started adopting Hadoop-based Big Data environments over the last ten years, they were mainly on-premise deployments. Organizations would spin up and manage large Hadoop clusters, where they would funnel exabytes or petabytes of unstructured data.However, over the last few years the economics of maintaining this enormous infrastructure compared with the elastic scalability of viable cloud options has changed this equation. The growth of cloud storage, cloud-managed big data e...