on 16-Jun-2016 19:21
In the previous article in the Getting Started with iControl series, we threw the lion's share of languages at you for both iControl portals, exposing you to many of the possible attack angles available for programmatically interacting with the BIG-IP. In this article, we're going to scale back a bit, focusing a few rest-based examples on how to gather statistics.
Note: This article published by Jason Rahm but co-authored with Joe Pruitt.
As much as you can do with iControl, it isn’t the only methodology for gathering statistics. In fact, I’d argue that there are at least two better ways to get at them, besides the on-box rrd graphs.
But maybe you don’t have those configured, or available, and you just need to do some lab testing and would like to consume those stats locally as you generate your test traffic. In these code samples, we’ll take a look at virtual server and pool stats.
At the pool level, several stats are available for consumption, primarily around bandwidth utilization and connection counts. You can also get state information as well, though I'd argue that's not really a statistic, unless I suppose you are counting how often the state is available. With snmp, the stats come in high/low form, so you have to do some bit shifting in order to arrive at the actual value. Not so with with iControl. The value comes as is. In each of these samples below, you'll see two functions. The first is the list function, where when you run the script you will get a list of pools so you can then specify which pool you want stats for. The second function will then take that pool as an argument and return the statistics.
//-------------------------------------------------------- function getPoolList(name) { //-------------------------------------------------------- var uri = "/mgmt/tm/ltm/pool"; handleVERB("GET", uri, null, function(json) { //console.log(json); var obj = JSON.parse(json); var items = obj.items; console.log("POOLS"); console.log("-----------"); for(var i=0; i<items.length; i++) { var fullPath = items[i].fullPath; console.log(" " + fullPath); } }); } //-------------------------------------------------------- function getPoolStats(name) { //-------------------------------------------------------- var uri = "/mgmt/tm/ltm/pool/" + name + "/stats"; handleVERB("GET", uri, null, function(json) { //console.log(json); var obj = JSON.parse(json); var selfLink = obj.selfLink; var entries = obj.entries; for(var n in entries) { console.log("--------------------------------------"); console.log("NAME : " + getStatsDescription(entries[n].nestedStats, "tmName")); console.log("--------------------------------------"); console.log("AVAILABILITY STATE : " + getStatsDescription(entries[n].nestedStats, "status.availabilityState")); console.log("ENABLED STATE : " + getStatsDescription(entries[n].nestedStats, "status.enabledState")); console.log("REASON : " + getStatsDescription(entries[n].nestedStats, "status.statusReason")); console.log("SERVER BITS IN : " + getStatsValue(entries[n].nestedStats, "serverside.bitsIn")); console.log("SERVER BITS OUT : " + getStatsValue(entries[n].nestedStats, "serverside.bitsOut")); console.log("SERVER PACKETS IN : " + getStatsValue(entries[n].nestedStats, "serverside.pktsIn")); console.log("SERVER PACKETS OUT : " + getStatsValue(entries[n].nestedStats, "serverside.pktsOut")); console.log("CURRENT CONNECTIONS : " + getStatsValue(entries[n].nestedStats, "serverside.curConns")); console.log("MAXIMUM CONNECTIONS : " + getStatsValue(entries[n].nestedStats, "serverside.maxConns")); console.log("TOTAL CONNECTIONS : " + getStatsValue(entries[n].nestedStats, "serverside.totConns")); console.log("TOTAL REQUESTS : " + getStatsValue(entries[n].nestedStats, "totRequests")); } }); }
#---------------------------------------------------------------------------- function Get-PoolList() # # Description: # This function returns the list of pools # # Parameters: # None #---------------------------------------------------------------------------- { $uri = "/mgmt/tm/ltm/pool"; $link = "https://$Bigip$uri"; $headers = @{}; $headers.Add("ServerHost", $Bigip); $secpasswd = ConvertTo-SecureString $Pass -AsPlainText -Force $mycreds = New-Object System.Management.Automation.PSCredential ($User, $secpasswd) $obj = Invoke-RestMethod -Method GET -Headers $headers -Uri $link -Credential $mycreds $items = $obj.items; Write-Host "POOL NAMES"; Write-Host "----------"; for($i=0; $i -lt $items.length; $i++) { $name = $items[$i].fullPath; Write-Host " $name"; } } #---------------------------------------------------------------------------- function Get-PoolStats() # # Description: # This function returns the statistics for a pool # # Parameters: # Name - The name of the pool #---------------------------------------------------------------------------- { $uri = "/mgmt/tm/ltm/pool/${Name}/stats"; $link = "https://$Bigip$uri"; $headers = @{}; $headers.Add("ServerHost", $Bigip); $secpasswd = ConvertTo-SecureString $Pass -AsPlainText -Force $mycreds = New-Object System.Management.Automation.PSCredential ($User, $secpasswd) $obj = Invoke-RestMethod -Method GET -Headers $headers -Uri $link -Credential $mycreds $entries = $obj.entries; $names = $entries | get-member -MemberType NoteProperty | select -ExpandProperty Name; $desc = $entries | Select -ExpandProperty $names $nestedStats = $desc.nestedStats; Write-Host ("--------------------------------------"); Write-Host ("NAME : $(Get-StatsDescription $nestedStats 'tmName')"); Write-Host ("--------------------------------------"); Write-Host ("AVAILABILITY STATE : $(Get-StatsDescription $nestedStats 'status.availabilityState')"); Write-Host ("ENABLED STATE : $(Get-StatsDescription $nestedStats 'status.enabledState')"); Write-Host ("REASON : $(Get-StatsDescription $nestedStats 'status.statusReason')"); Write-Host ("SERVER BITS IN : $(Get-StatsValue $nestedStats 'serverside.bitsIn')"); Write-Host ("SERVER BITS OUT : $(Get-StatsValue $nestedStats 'serverside.bitsOut')"); Write-Host ("SERVER PACKETS IN : $(Get-StatsValue $nestedStats 'serverside.pktsIn')"); Write-Host ("SERVER PACKETS OUT : $(Get-StatsValue $nestedStats 'serverside.pktsOut')"); Write-Host ("CURRENT CONNECTIONS : $(Get-StatsValue $nestedStats 'serverside.curConns')"); Write-Host ("MAXIMUM CONNECTIONS : $(Get-StatsValue $nestedStats 'serverside.maxConns')"); Write-Host ("TOTAL CONNECTIONS : $(Get-StatsValue $nestedStats 'serverside.totConns')"); Write-Host ("TOTAL REQUESTS : $(Get-StatsValue $nestedStats 'totRequests')"); }
def get_pool_list(bigip, url): try: pools = bigip.get("%s/ltm/pool" % url).json() print "POOL LIST" print " ---------" for pool in pools['items']: print " /%s/%s" % (pool['partition'], pool['name']) except Exception, e: print e def get_pool_stats(bigip, url, pool): try: pool_stats = bigip.get("%s/ltm/pool/%s/stats" % (url, pool)).json() selflink = "https://localhost/mgmt/tm/ltm/pool/%s/~Common~%s/stats" % (pool, pool) nested_stats = pool_stats['entries'][selflink]['nestedStats']['entries'] print '' print ' --------------------------------------' print ' NAME : %s' % nested_stats['tmName']['description'] print ' --------------------------------------' print ' AVAILABILITY STATE : %s' % nested_stats['status.availabilityState']['description'] print ' ENABLED STATE : %s' % nested_stats['status.enabledState']['description'] print ' REASON : %s' % nested_stats['status.statusReason']['description'] print ' SERVER BITS IN : %s' % nested_stats['serverside.bitsIn']['value'] print ' SERVER BITS OUT : %s' % nested_stats['serverside.bitsOut']['value'] print ' SERVER PACKETS IN : %s' % nested_stats['serverside.pktsIn']['value'] print ' SERVER PACKETS OUT : %s' % nested_stats['serverside.pktsOut']['value'] print ' CURRENT CONNECTIONS : %s' % nested_stats['serverside.curConns']['value'] print ' MAXIMUM CONNECTIONS : %s' % nested_stats['serverside.maxConns']['value'] print ' TOTAL CONNECTIONS : %s' % nested_stats['serverside.totConns']['value'] print ' TOTAL REQUESTS : %s' % nested_stats['totRequests']['value']
The virtual server stats are very similar in nature to the pool stats, though you are getting an aggregate view of traffic to the virtual server, whereas there might be several pools of traffic being serviced by that single virtual server. In any event, if you compare the code below to the code from the pool functions, they are nearly functionally identical. The only differences should be discernable: the method calls themselves and then the object properties that are different (clientside/serverside for example.)
//-------------------------------------------------------- function getVirtualList(name) { //-------------------------------------------------------- var uri = "/mgmt/tm/ltm/virtual"; handleVERB("GET", uri, null, function(json) { //console.log(json); var obj = JSON.parse(json); var items = obj.items; console.log("VIRTUALS"); console.log("-----------"); for(var i=0; i<items.length; i++) { var fullPath = items[i].fullPath; console.log(" " + fullPath); } }); } //-------------------------------------------------------- function getVirtualStats(name) { //-------------------------------------------------------- var uri = "/mgmt/tm/ltm/virtual/" + name + "/stats"; handleVERB("GET", uri, null, function(json) { //console.log(json); var obj = JSON.parse(json); var selfLink = obj.selfLink; var entries = obj.entries; for(var n in entries) { console.log("--------------------------------------"); console.log("NAME : " + getStatsDescription(entries[n].nestedStats, "tmName")); console.log("--------------------------------------"); console.log("DESTINATION : " + getStatsDescription(entries[n].nestedStats, "destination")); console.log("AVAILABILITY STATE : " + getStatsDescription(entries[n].nestedStats, "status.availabilityState")); console.log("ENABLED STATE : " + getStatsDescription(entries[n].nestedStats, "status.enabledState")); console.log("REASON : " + getStatsDescription(entries[n].nestedStats, "status.statusReason")); console.log("CLIENT BITS IN : " + getStatsValue(entries[n].nestedStats, "clientside.bitsIn")); console.log("CLIENT BITS OUT : " + getStatsValue(entries[n].nestedStats, "clientside.bitsOut")); console.log("CLIENT PACKETS IN : " + getStatsValue(entries[n].nestedStats, "clientside.pktsIn")); console.log("CLIENT PACKETS OUT : " + getStatsValue(entries[n].nestedStats, "clientside.pktsOut")); console.log("CURRENT CONNECTIONS : " + getStatsValue(entries[n].nestedStats, "clientside.curConns")); console.log("MAXIMUM CONNECTIONS : " + getStatsValue(entries[n].nestedStats, "clientside.maxConns")); console.log("TOTAL CONNECTIONS : " + getStatsValue(entries[n].nestedStats, "clientside.totConns")); console.log("TOTAL REQUESTS : " + getStatsValue(entries[n].nestedStats, "totRequests")); } }); }
#---------------------------------------------------------------------------- function Get-VirtualList() # # Description: # This function lists all virtual servers. # # Parameters: # None #---------------------------------------------------------------------------- { $uri = "/mgmt/tm/ltm/virtual"; $link = "https://$Bigip$uri"; $headers = @{}; $headers.Add("ServerHost", $Bigip); $secpasswd = ConvertTo-SecureString $Pass -AsPlainText -Force $mycreds = New-Object System.Management.Automation.PSCredential ($User, $secpasswd) $obj = Invoke-RestMethod -Method GET -Headers $headers -Uri $link -Credential $mycreds $items = $obj.items; Write-Host "POOL NAMES"; Write-Host "----------"; for($i=0; $i -lt $items.length; $i++) { $name = $items[$i].fullPath; Write-Host " $name"; } } #---------------------------------------------------------------------------- function Get-VirtualStats() # # Description: # This function returns the statistics for a virtual server # # Parameters: # Name - The name of the virtual server #---------------------------------------------------------------------------- { param( [string]$Name ); $uri = "/mgmt/tm/ltm/virtual/${Name}/stats"; $link = "https://$Bigip$uri"; $headers = @{}; $headers.Add("ServerHost", $Bigip); $secpasswd = ConvertTo-SecureString $Pass -AsPlainText -Force $mycreds = New-Object System.Management.Automation.PSCredential ($User, $secpasswd) $obj = Invoke-RestMethod -Method GET -Headers $headers -Uri $link -Credential $mycreds $entries = $obj.entries; $names = $entries | get-member -MemberType NoteProperty | select -ExpandProperty Name; $desc = $entries | Select -ExpandProperty $names $nestedStats = $desc.nestedStats; Write-Host ("--------------------------------------"); Write-Host ("NAME : $(Get-StatsDescription $nestedStats 'tmName')"); Write-Host ("--------------------------------------"); Write-Host ("DESTINATION : $(Get-StatsDescription $nestedStats 'destination')"); Write-Host ("AVAILABILITY STATE : $(Get-StatsDescription $nestedStats 'status.availabilityState')"); Write-Host ("ENABLED STATE : $(Get-StatsDescription $nestedStats 'status.enabledState')"); Write-Host ("REASON : $(Get-StatsDescription $nestedStats 'status.statusReason')"); Write-Host ("CLIENT BITS IN : $(Get-StatsValue $nestedStats 'clientside.bitsIn')"); Write-Host ("CLIENT BITS OUT : $(Get-StatsValue $nestedStats 'clientside.bitsOut')"); Write-Host ("CLIENT PACKETS IN : $(Get-StatsValue $nestedStats 'clientside.pktsIn')"); Write-Host ("CLIENT PACKETS OUT : $(Get-StatsValue $nestedStats 'clientside.pktsOut')"); Write-Host ("CURRENT CONNECTIONS : $(Get-StatsValue $nestedStats 'clientside.curConns')"); Write-Host ("MAXIMUM CONNECTIONS : $(Get-StatsValue $nestedStats 'clientside.maxConns')"); Write-Host ("TOTAL CONNECTIONS : $(Get-StatsValue $nestedStats 'clientside.totConns')"); Write-Host ("TOTAL REQUESTS : $(Get-StatsValue $nestedStats 'totRequests')"); }
def get_virtual_list(bigip, url): try: vips = bigip.get("%s/ltm/virtual" % url).json() print "VIRTUAL SERVER LIST" print " -------------------" for vip in vips['items']: print " /%s/%s" % (vip['partition'], vip['name']) except Exception, e: print e def get_virtual_stats(bigip, url, vip): try: vip_stats = bigip.get("%s/ltm/virtual/%s/stats" % (url, vip)).json() selflink = "https://localhost/mgmt/tm/ltm/virtual/%s/~Common~%s/stats" % (vip, vip) nested_stats = vip_stats['entries'][selflink]['nestedStats']['entries'] print '' print ' --------------------------------------' print ' NAME : %s' % nested_stats['tmName']['description'] print ' --------------------------------------' print ' AVAILABILITY STATE : %s' % nested_stats['status.availabilityState']['description'] print ' ENABLED STATE : %s' % nested_stats['status.enabledState']['description'] print ' REASON : %s' % nested_stats['status.statusReason']['description'] print ' CLIENT BITS IN : %s' % nested_stats['clientside.bitsIn']['value'] print ' CLIENT BITS OUT : %s' % nested_stats['clientside.bitsOut']['value'] print ' CLIENT PACKETS IN : %s' % nested_stats['clientside.pktsIn']['value'] print ' CLIENT PACKETS OUT : %s' % nested_stats['clientside.pktsOut']['value'] print ' CURRENT CONNECTIONS : %s' % nested_stats['clientside.curConns']['value'] print ' MAXIMUM CONNECTIONS : %s' % nested_stats['clientside.maxConns']['value'] print ' TOTAL CONNECTIONS : %s' % nested_stats['clientside.totConns']['value'] print ' TOTAL REQUESTS : %s' % nested_stats['totRequests']['value']
So now that you have scripts that will pull stats from the pools and virtual servers, what might you do to extend these? As an exercise in skill development, I’d suggest trying these tasks below. Note that there are dozens of existing articles on iControl that you can glean details from that will help you in your quest.
All the scripts for this article are available in the codeshare under "Getting Started with iControl Code Samples."
Hi dear. One question related with powershell and F5 (Audit)
How can I get history of changed states from pool members by using scripts that were run under Powershell and commands through administrator web console?
Thanks.
Sincerely JPV
Hi JPV, through our APIs, the only historical stats we keep are in the high level performance graphs. I wrote an article on those APIs in this iControl 101 article.
 
The performance graphs are just for high level statistics. For object-level stats and states, you will have to write code to pull them down at your interval of choice using the various module APIs and store them locally. BIG-IP doesn't keep historical data for all configuration changes outside of audit logs which may or may not have all the information you need. We have our AVR reporting module that may help you get closer to where you want to go, but odds are if you are looking for granular specific historical changes, you'll need to build a polling system and collect the data yourself.
 
I'll dig a little deeper to make sure I'm not missing a feature but I'm fairly sure this is the way you'll need to go.
 
-Joe
 
Hi Joe.
😕
Thanks for your cooperation and I hope to know more future information!
Kind Regards. JPV