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.

Statistics on BIG-IP

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.

  • SNMP - Yes, it’s old school. But it has persisted as long as it has for a reason. It’s broad and lightweight. It does, however, require client-side software to do something with all that data.
  • AVR - By provisioning the AVR module on BIG-IP, you can configure an expansive amount of metrics to measure. Combined with syslog to send the data off-box, this push approach is also pretty lightweight, though like with snmp, having a tool to consume those stats is helpful. Ken Bocchino released an analytics iApp that combines AVR and its data output with Splunk and its indexing ability to deliver quite a solution.

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.


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. 

Node.Js Pool Stats

//--------------------------------------------------------
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"));
		}
	});	
}

Powershell Pool Stats

#----------------------------------------------------------------------------
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')");
}

Python Pool Stats

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']

Virtual Server Stats

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.)

Node.Js Virtual Server Stats

//--------------------------------------------------------
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"));
		}
	});	
}

Powershell Virtual Server Stats

#----------------------------------------------------------------------------
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')");
}

Python Virtual Server Stats

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']

Going Further

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.

  • Given that you can pull back a list of pools or virtual servers by not providing one specifically, iterate through the appropriate list and print stats for all of them instead of just one.
  • Extend that even further, building a table of virtual servers and their respective stats
  • These stats are a point in time, and not terribly useful as a single entity. Create a timer where the stats are pulled back every 10s or so and take a delta to display.
  • Extend that even further by narrowing down to maybe the bits in and out, and graph them over time.

Resources

All the scripts for this article are available in the codeshare under "Getting Started with iControl Code Samples."