Getting Started with Bigsuds–a New Python Library for iControl

I imagine the progression for you, the reader, will be something like this in the first six- or seven-hundred milliseconds after reading the title: Oh cool! Wait, what? Don’t we already have like two libraries for python? Really, a third library for python? Yes. An emphatic yes. The first iteration of pycontrol (pc1) was based on the zsi library, which hasn’t been updated in years and was abandoned with the development of the second iteration, pycontrol v2 (pc2), which switched to the active and well-maintained suds library. Bigsuds, like pycontrol v2, is also based on the suds library. So why bigsuds? There are several advantages to using the bigsuds library.

No need to specify which WSDLs to download

In pycontrol v2, any iControl interface you wish to work with must be specified when you instantiate the BIG-IP, as well as specifying the local directory or loading from URL for the WSDLs. In bigsuds, just specify the host, username, and password (username and password optional if using test box defaults of admin/admin) and you’re good to go. Currently in pycontrol v2:

>>> import pycontrol.pycontrol as pc 
>>> b = pc.BIGIP( 
...     hostname = '192.168.6.11', 
...     username = 'admin', 
...     password = 'admin', 
...     fromurl = True, 
...     wsdls = ['LocalLB.Pool']) 
>>> b.LocalLB.Pool.get_list() 
[/Common/p1, /Common/p2, /Common/p3, /Common/p5] 

And here in bigsuds:

>>> import bigsuds 
>>> b = bigsuds.BIGIP(hostname = '192.168.6.11') 
>>> b.LocalLB.Pool.get_list() 
['/Common/p1', '/Common/p2', '/Common/p3', '/Common/p5'] 
>>> b.GlobalLB.Pool.get_list() 
['/Common/p2', '/Common/p1']

No need to define the typefactory for write operations.

This was the most challenging aspect of pycontrol v2 for me personally. I would get them correct sometimes. Often I’d bang my head against the wall wondering what little thing I missed to prevent success. The cool thing with bigsuds is you are just passing lists for sequences and lists of dictionaries for structures. No object creation necessary before making the iControl calls. It’s a thing of beauty.

Creating a two member pool in pycontrol v2:

lbmeth = b.LocalLB.Pool.typefactory.create('LocalLB.LBMethod')

# This is basically a stub holder of member items that we need to wrap up.
mem_sequence = b.LocalLB.Pool.typefactory.create('Common.IPPortDefinitionSequence')

# Now we'll create some pool members.
mem1 = b.LocalLB.Pool.typefactory.create('Common.IPPortDefinition')
mem2 = b.LocalLB.Pool.typefactory.create('Common.IPPortDefinition')

# Note how this is 'pythonic' now. We set attributes agains the objects, then
# pass them in.
mem1.address = '1.2.3.4'
mem1.port = 80

mem2.address = '1.2.3.4'
mem2.port = 81

# Create a 'sequence' of pool members.
mem_sequence.item = [mem1, mem2]

# Let's create our pool.
name = 'PC2' + str(int(time.time()))

b.LocalLB.Pool.create(pool_names = [name], lb_methods = \
        [lbmeth.LB_METHOD_ROUND_ROBIN], members = [mem_sequence])

In contrast, here is a two member pool in bigsuds.

>>> b.LocalLB.Pool.create_v2(['/Common/Pool1'],['LB_METHOD_ROUND_ROBIN'],[[{'port':80, 'address':'1.2.3.4'},{'port':81, 
'address':'1.2.3.4'}]])

Notice above that I did not use the method parameters. They are not required in bigsuds, though you can certainly include them. This could be written in the long form as:

>>> b.LocalLB.Pool.create_v2(pool_names = ['/Common/Pool1'],lb_methods = ['LB_METHOD_ROUND_ROBIN'], members = [[{'port':80, 'address':'1.2.3.4'},{'port':81, 
'address':'1.2.3.4'}]])

Standard python data types are returned

There’s no more dealing with data returned like this:

>>> p2.LocalLB.Pool.get_statistics(pool_names=['/Common/p2']) 
(LocalLB.Pool.PoolStatistics){ 
   statistics[] = 
      (LocalLB.Pool.PoolStatisticEntry){ 
         pool_name = "/Common/p2" 
         statistics[] = 
            (Common.Statistic){ 
               type = "STATISTIC_SERVER_SIDE_BYTES_IN" 
               value = 
                  (Common.ULong64){ 
                     high = 0 
                     low = 0 
                  } 
               time_stamp = 0 
            }, 
            (Common.Statistic){ 
               type = "STATISTIC_SERVER_SIDE_BYTES_OUT" 
               value = 
                  (Common.ULong64){ 
                     high = 0 
                     low = 0 
                  } 
               time_stamp = 0 
            },

Data is standard python types: strings, lists, dictionaries. That same data returned by bigsuds:

>>> b.LocalLB.Pool.get_statistics(['/Common/p1']) 
{'statistics': [{'pool_name': '/Common/p1', 'statistics': [{'time_stamp': 0, 'type': 'STATISTIC_SERVER_SIDE_BYTES_IN', 'value': {'high': 0, 'low': 0}}, {'time_stamp': 0, 'type': 'STATISTIC_SERVER_SIDE_BYTES_OUT', 'value': {'high': 0, 'low': 0}}

Perhaps not as readable in this form as with pycontrol v2, but far easier to work programmatically.

Better session and transaction support

George covered the benefits of sessions in his v11 iControl: Sessions article in fine detail, so I’ll leave that to the reader. Regarding implementations, bigsuds handles sessions with a built-in utility called with_session_id. Example code:

>>> bigip2 = b.with_session_id() 
>>> bigip2.System.Session.set_transaction_timeout(99) 
>>> print b.System.Session.get_transaction_timeout() 
5 
>>> print bigip2.System.Session.get_transaction_timeout() 
99

Also, with transactions, bigsuds has built-in transaction utilities as well. In the below sample code, creating a new pool that is dependent on a non-existent pool being deleted results in an error as expected, but also prevents the pool from the previous step from being created as show in the get_list method call.

>>> try: 
...     with bigsuds.Transaction(bigip2): 
...             bigip2.LocalLB.Pool.create_v2(['mypool'],['LB_METHOD_ROUND_ROBIN'],[[]]) 
...             bigip2.LocalLB.Pool.delete_pool(['nonexistent']) 
... except bigsuds.OperationFailed, e: 
...     print e 
... 
Server raised fault: 'Exception caught in System::urn:iControl:System/Session::submit_transaction() 
Exception: Common::OperationFailed 
        primary_error_code   : 16908342 (0x01020036) 
        secondary_error_code : 0 
        error_string         : 01020036:3: The requested pool (/Common/nonexistent) was not found.' 
>>> bigip2.LocalLB.Pool.get_list() 
['/Common/Pool1', '/Common/p1', '/Common/p2', '/Common/p3', '/Common/p5', '/Common/Pool3', '/Common/Pool2']

F5 maintained

Community member L4L7, the author of the pycontrol v2 library, is no longer with F5 and just doesn’t have the cycles to maintain the library going forward. Bigsuds author Garron Moore, however, works in house and will fix bugs and enhance as time allows. Note that all iControl libraries are considered experimental and are not officially supported by F5 Networks. Library maintainers for all the languages will do their best to fix bugs and introduce features as time allows. Source is provided though, and bugs can and are encouraged to be fixed by the community!

Installing bigsuds

Make sure you have suds installed and grab a copy of bigsuds (you’ll need to log in) and extract the contents. You can use the easy setup tools to install it to python’s site-packages library like this:

jrahm@jrahm-dev:/var/tmp$ tar xvfz bigsuds-1.0.tar.gz 
bigsuds-1.0/ 
bigsuds-1.0/setup.py 
bigsuds-1.0/bigsuds.egg-info/ 
bigsuds-1.0/bigsuds.egg-info/top_level.txt 
bigsuds-1.0/bigsuds.egg-info/requires.txt 
bigsuds-1.0/bigsuds.egg-info/SOURCES.txt 
bigsuds-1.0/bigsuds.egg-info/dependency_links.txt 
bigsuds-1.0/bigsuds.egg-info/PKG-INFO 
bigsuds-1.0/setup.cfg 
bigsuds-1.0/bigsuds.py 
bigsuds-1.0/MANIFEST.in 
bigsuds-1.0/PKG-INFO 
jrahm@jrahm-dev:/var/tmp$ cd bigsuds-1.0/ 
jrahm@jrahm-dev:/var/tmp/bigsuds-1.0$ python setup.py install

Doing it that way, you can just enter the python shell (or run your script) with a simple ‘import bigsuds’ command. If you don’t want to install it that way, you can just extract the bigsuds.py from the download and drop it in a directory of your choice and make a path reference in the shell or script:

>>> import bigsuds 
Traceback (most recent call last): 
  File "<stdin>", line 1, in <module> 
ImportError: No module named bigsuds 
>>> import sys 
>>> sys.path.append(r'/home/jrahm/dev/bigsuds-1.0') 
>>> import bigsuds 
>>>

Conclusion

Garron Moore's bigsuds contribution is a great new library for python users. There is work to be done to convert your pycontrol v2 samples, but the flexibility and clarity in the new library makes it worth it in this guy’s humble opinion. A new page in the iControl wiki has been created for bigsuds developments. Please check it out, community! For now, I’ve converted a few scripts to bigsuds, linked in the aforementioned page as well as directly below:

Published Nov 07, 2012
Version 1.0
  • i've no idea about this --------------------------------------------------------------------------- ConnectionError Traceback (most recent call last) in () ----> 1 a.LocalLB.Pool.get_list() /home/vagrant/py279_f5/lib/python2.7/site-packages/bigsuds-1.0.1-py2.7.egg/bigsuds.pyc in __getattr__(self, attr) 311 if attr.startswith('__'): 312 return getattr(super(_Namespace, self), attr) --> 313 client = self._client_creator('%s.%s' % (self._name, attr)) 314 setattr(self, attr, client) 315 return client /home/vagrant/py279_f5/lib/python2.7/site-packages/bigsuds-1.0.1-py2.7.egg/bigsuds.pyc in _create_client(self, wsdl_name) 140 One situation that raises TransportError is when credentials are bad. 141 except (urllib2.URLError, TransportError), e: --> 142 raise ConnectionError(str(e)) 143 return self._create_client_wrapper(client, wsdl_name) 144 ConnectionError:
  • The LTM pool status script is great! How can I have it poll pools in all the partitions?
  • BigSuds has now been published to pypi, the Python software repository. Installing it is now as simple as "pip install bigsuds"