Getting started with the python SDK part 3: working with statistics

In the previous article in this series, we looked at how to work with unnamed resources and commands. In this article, we’ll focus on working with statistics.

Statistics abound throughout the BIG-IP, but we’ll narrow in on virtual and pool stats here.

Virtual Server

{
    "kind": "tm:ltm:virtual:virtualstats",
    "generation": 56,
    "selfLink": "https://localhost/mgmt/tm/ltm/virtual/testvip/stats?ver=13.1.0.5",
    "entries": {
        "https://localhost/mgmt/tm/ltm/virtual/testvip/~Common~testvip/stats": {
            "nestedStats": {
                "kind": "tm:ltm:virtual:virtualstats",
                "selfLink": "https://localhost/mgmt/tm/ltm/virtual/testvip/~Common~testvip/stats?ver=13.1.0.5",
                "entries": {
                    "clientside.bitsIn": {
                        "value": 0
                    },

Pool

{
    "kind": "tm:ltm:pool:poolstats",
    "generation": 1,
    "selfLink": "https://localhost/mgmt/tm/ltm/pool/testpool/stats?ver=13.1.0.5",
    "entries": {
        "https://localhost/mgmt/tm/ltm/pool/testpool/~Common~testpool/stats": {
            "nestedStats": {
                "kind": "tm:ltm:pool:poolstats",
                "selfLink": "https://localhost/mgmt/tm/ltm/pool/testpool/~Common~testpool/stats?ver=13.1.0.5",
                "entries": {
                    "activeMemberCnt": {
                        "value": 1
                    },

You can see structurally they are very similar, with a kind including the stats keyword, and a series of entries and nested stats fields. The pool member, however, goes another nested level deeper (see details toward the bottom of this article). Even starting with the virtual server and the pool, trying to access these statistics manually, well, that is not for the faint of heart. Consider the following code:

# Virtual Server
>>> vip = b.tm.ltm.virtuals.virtual.load(name='testvip')
>>> vipstats = vip.stats.load()
>>> for k, v in vipstats.entries['https://localhost/mgmt/tm/ltm/virtual/testvip/~Common~testvip/stats']['nestedStats']['entries'].iteritems():
...     if v.get('description') != None:
...         print '%s: %s' % (k, v.get('description'))
...     elif v.get('value') != None:
...         print '%s: %s' % (k, v.get('value'))
...
syncookie.accepts: 0
ephemeral.bitsOut: 0
clientside.bitsOut: 5002552
fiveMinAvgUsageRatio: 0
tmName: /Common/testvip
csMeanConnDur: 192
syncookie.swsyncookieInstance: 0
status.enabledState: enabled
...remaining fields removed

# Pool
>>> pool = b.tm.ltm.pools.pool.load(name='testpool')
>>> poolstats = pool.stats.load()
>>> for k, v in poolstats.entries['https://localhost/mgmt/tm/ltm/pool/testpool/~Common~testpool/stats']['nestedStats']['entries'].iteritems():
...     if v.get('description') != None:
...         print '%s: %s' % (k, v.get('description'))
...     elif v.get('value') != None:
...         print '%s: %s' % (k, v.get('value'))
...
connqAll.ageMax: 0
serverside.curConns: 0
minActiveMembers: 0
serverside.bitsIn: 90474272
connqAll.serviced: 0
serverside.pktsIn: 181780
memberCnt: 1
serverside.maxConns: 50
connq.depth: 0
connqAll.depth: 0
connqAll.ageHead: 0
curSessions: 0
status.availabilityState: available

A couple things to note here. You might think you could use the selfLink attribute instead of manually typing the entry ID, but the selfLink needs a little help. You can programmatically insert the ~Common~testpool in there and use that, but either way, to get to that list of items to iterate over is a chore! Another item of interest is that each stats field is a dictionary with a key of value or description, so that needs to be managed while pulling the values for storage or printing. At this point you must be thinking "there's got to be a better way!" Thankfully, there is.

Using the Stats Utility

The Stats utility is buried down deep in the utils directory tree in the handlers module. Once imported, you just wrap any of your stats method calls with that class. Focusing in on just the pool this time, that significantly cuts down the work necessary in your for loop:

>>> from f5.utils.responses.handlers import Stats
>>> pool = b.tm.ltm.pools.pool.load(name='testpool')
>>> pool stats = Stats(pool.stats.load())
>>> for k, v in poolstats.stat.iteritems():
...     if v.get('description') != None:
...         print '%s: %s' % (k, v.get('description'))
...     elif v.get('value') != None:
...         print '%s: %s' % (k, v.get('value'))
...

And voila, piece of cake!

One final note before closing up shop: this doesn't quite work with pool members. The resulting json payload from a pool member stats query to the API are pretty buried. Using the sdk to grab the stats (without the Stats module):

>>> pm = pool.members_s.members.load(name='192.168.103.20:80', partition='Common')
>>> pmstats = pm.stats.load()

You can see from this variable data in my IDE that the depth is well...very deep!

The development team still has some work to do to iron out the Stats class, particularly for subcollections like pool members, but for top level objects it’s a time/life-saver! Keep an eye on issue 1440 on GitHub for enhancements. 

Published Jun 07, 2018
Version 1.0
  • Hi Jason, nice one. +1 and thanks!

    You can also add the query parameter of '

    options=detail
    ' to reveal the associated node details:

    https://localhost/mgmt/tm/ltm/pool/~~/stats?options=detail

    Cheers, Stephan

  • You're jumping ahead! The next article is going to cover using the some of the request parameters!

     

  • I´m curious to see as I´m using them often to limit the output or to hand over additional parameters.

     

    Cheers, Stephan

     

  • Hi @falik2, I don't think your entire comment made it?

     

  • I'm posting this here to hopefully help someone else out on how to iterate through the pool member stats. The F5 Python SDK: How to get virtual server availability article was useful in figuring this out.

     

    This executes much slower than I would like it to, but at least I can get to the data now.

     

    I was after the number of server side connections, the availability state and enabled state values of each pool member.

     

     load the pool
    p1 = mgmt.tm.ltm.pools.pool.load(name='TEST-POOL', partition='Common')
    
     get the pool members
    members = p1.members_s.get_collection()
    
     Iterate through each pool member
    for member in members:        
         load the pool member and stats for the member
        pm = p1.members_s.members.load(name=member.name, partition="Common")
        pmstats = pm.stats.load()
    
         Get the selfLink to be used later.
        for k in pmstats.entries:
            selflink = k
    
         statentries will contain a list with the correct data
        statentries = pmstats.entries.get(selflink).get('nestedStats').get('entries')
    
        for i in statentries:
             Numbers entries contain information in 'value'
             Text entries contain information in 'description'
            if i == 'serverside.curConns':
                connections = statentries[i].get('value')
    
            if i == 'status.availabilityState':
                availability = statentries[i].get('description')
    
            if i == 'status.enabledState':
                enabled = statentries[i].get('description')
    
        print("{}, {:3}, {}, {}".format(member.name, connections, availability, enabled))