Getting Started with the f5-common-python SDK
If you have dabbled with python and iControl over the years, you might be familiar with some of my other “Getting Stared with …” articles on python libraries. I started my last, on Bigsuds, this way:
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?
It’s past time to update those numbers as the forth library in our python support evolution, the f5-common-python SDK, has been available since March of last year! I still love Bigsuds, but it only supports the iControl SOAP interface. The f5-common-python SDK is under continuous development in support of the iControl REST interface, and like Bigsuds, does a lot of the API heavy lifting for you so you can just focus on the logic of bending BIG-IP configuration to your will. Not all endpoints are supported yet, but please feel free to open an issue on the GitHub repo if there’s something missing you need for your project. In this article, I’ll cover the basics of installing the SDK and how to utilize the core functionality.
Installing the SDK
This section is going to be really short, as the SDK is uploaded to PyPI after reach release, though you can clone the GitHub project and run the development branch with latest features if you so desire. I'd recommend installing in a virtual environment to keep your system python uncluttered, but YMMV.
pip install f5-sdk
A simple one-liner and we're done! Moving on...
Instantiating BIG-IP
The first thing you’ll want to do with your shiny new toy is authenticate to the BIG-IP. You can use basic or token authentication to do so.
I disable the certificate security warnings on my test boxes, but the first two lines in the sample code below are not necessary if you are using valid certificates
>>> import requests >>> requests.packages.urllib3.disable_warnings() >>> from f5.bigip import ManagementRoot >>> # Basic Authentication >>> b = ManagementRoot('ltm3.test.local', 'admin', 'admin') >>> # Token Authentication >>> b = ManagementRoot('ltm3.test.local', 'admin', 'admin', token=True) >>> b.tmos_version u'12.1.0'
The
b
object has credentials attached and various other attributes as well, such as the tmos_version
attribute shown above. This is the root object you’ll use (of course you don’t have to call it b, you can call it plutoWillAlwaysBeAPlanetToMe if you want to, but that’s a lot more typing) for all the modules you might interact with on the system.
Nomenclature
The method mappings are tied to the tmsh and REST URL ids. Consider the tmsh command
tmsh list /ltm pool
. In the URL, this would be https://ip/mgmt/tm/ltm/pool. For the SDK, at the collection level the command would be b.tm.ltm.pools
. It's plural here because we are signifying the collection.
If there is a collection already ending in an s, like the subcollection of a pool in members, it would be addressed as members_s. This will be more clear as we work through examples in later articles, but I wanted to provide a little guidance before moving on.
Working with Collections
There are two types of collections (well three if you include subcollections, but we’ll cover those in a later article,) organizing collections and collections. An organizing collection is a superset of other collections. For example, the ltm or net module listing would be an organizing collection, whereas ltm/pool or net/vlan would be collections. To retrieve either type, you use the
get_collection
method as shown below, with abbreviated output.
# The LTM Organizing Collection >>> for x in b.tm.ltm.get_collection(): ... print x ... {u'reference': {u'link': u'https://localhost/mgmt/tm/ltm/auth?ver=12.1.0'}} {u'reference': {u'link': u'https://localhost/mgmt/tm/ltm/data-group?ver=12.1.0'}} {u'reference': {u'link': u'https://localhost/mgmt/tm/ltm/dns?ver=12.1.0'}} # The Net/Vlan Collection: >>> vlans = b.tm.net.vlans.get_collection() >>> for vlan in vlans: ... print vlan.name ... vlan10 vlan102 vlan103
Working with Named Resources
A named resource, like a pool, vip, or vlan, is a fully configurable object for which the CURDLE methods are supported. These methods are:
- create()
- update()
- refresh()
- delete()
- load()
- exists()
Let’s work through all these methods with a pool object.
>>> b.tm.ltm.pools.pool.exists(name='mypool2017', partition='Common') False >>> p1 = b.tm.ltm.pools.pool.create(name='mypool2017', partition='Common') >>> p2 = b.tm.ltm.pools.pool.load(name='mypool2017', partition='Common') >>> p1.loadBalancingMode = 'least-connections-member' >>> p1.update() >>> assert p1.loadBalancingMode == p2.loadBalancingMode Traceback (most recent call last): File "", line 1, in AssertionError >>> p2.refresh() >>> assert p1.loadBalancingMode == p2.loadBalancingMode >>> p1.delete() >>> b.tm.ltm.pools.pool.exists(name='mypool2017', partition='Common') False
Notice in line 1, I am looking to see if the pool called mypool2017 exists, to which I get a return value of False. So I can go ahead and create that pool as shown in line 3. In line 4, I load the same pool so I have two local python objects (p1, p2) that reference the same BIG-IP pool (mypool2017.) In line 5, I update the load balancing algorithm from the default of round robin to least connections member. But at this point, only the local python object has been updated. To update the BIG-IP, in line 6 I apply that method to the object. Now if I assert the LB algorithm between the local p1 and p2 python objects as shown in line 7, it fails, because we have updated p1, but p2 is still as it was when I initially loaded it. Refreshing p2 as shown in line 11 will update it (the local python object, not the BIG-IP pool.) Now I assert again in line 12, and it does not fail. As this was just an exercise, I delete the new pool (could be done on p1 or p2 since they reference the same BIG-IP object) in line 13, and a quick check to see if it exists in line 14 returns false.
The great thing is that even though the endpoints change from pool to virtual to rule and so on, the methods used for them do not.
Next Steps
This is just the tip of the iceberg! There is much more to cover, so come back for the next installment, where we’ll cover unnamed resources and commands. If you can't wait, feel free to dig into the SDK documentation.
yes Jason.
With partition='Common' parameter, .exists() call works as expected.
Based on my test, I didn't need to specify partition='xxx' parameter for exists() and it worked. For example, most of protocol profile resources; http, tcp, udp, http and persistence profile resources; source, destination, hash persistence )
One exception so far I found was the exists() of Local traffic policy profile resource which requires two additional parameter (partition='xxx' and subPath='xxx') as well as name='xxx'.
Do you know any easy way of figuring out required parameters of each methods call (Create(), Refresh(), Update(), Delete(), Load(), Exists())?
- JRahmAdmin
there are some inconsistencies we'll need to clean up. Ideally, you should never need to supply the partition, we should assume 'Common' if not supplied. Some parameters are required by the REST interface, not the sdk.
- Tariq_Iqbal_924Nimbostratus
Jason,
What nomenclature should I use to trigger F5 to generate the UCS?
Your help in this regard wil be greatly appreciated.
Thanks
- JRahmAdmin
Hi Tariq, give this a try:
from f5.bigip import ManagementRoot b = ManagementRoot('192.168.1.245', 'admin', 'admin') b.tm.sys.ucs.exec_cmd('save', name='bigip1.ucs')
- Tariq_Iqbal_924Nimbostratus
Thanks Jason for a prompt response. Is there a function that I can instantiate to check if the UCS is complete? I see the exist method and I don't know if that method can be used to check if the UCS creation is completed prior to running commands to copy the UCS to a remote server. Also, can I use the exec_cmd for copying the file using cp? Thanks Jason for all your help.
- JRahmAdmin
no, there isn't a status since this is just a wrapper for a cli command. you could use bash to check for the existence of the file you are creating with the ucs call and then assert:
result = b.tm.util.bash.exec_cmd('run', utilCmdArgs='-c "ls /var/local/ucs"') assert 'bigip1.ucs' in result.commandResult
- KernelPanicNimbostratus
Hi Jason, great work you're doing here!
I'm trying to update user parameters from the SDK. Having trouble accessing subcollection partitionAccess and user roll. Could you show an example of how to change a user roll from guest to resource admin, without resorting to exec_cmd.
u = b.tm.auth.users.user.load(name='myuser') pp(u.raw) 'uri': u'https://mybox.com:443/mgmt/tm/auth/user/,myuser/'}, u'description': u'myuser', u'encryptedPassword': u'!!', u'fullPath': u'myuser', u'generation': 1, u'kind': u'tm:auth:user:userstate', u'name': u'myuser', u'partitionAccess': [{u'name': u'all-partitions', u'nameReference': {u'link': u'https://localhost/mgmt/tm/auth/partition/all-partitions?ver=12.1.2'}, u'role': u'guest'}], u'selfLink': u'https://localhost/mgmt/tm/auth/user/myuser?ver=12.1.2', u'shell': u'bash'}
- JRahmAdmin
The partitionAccess field is a list of attributes on the primary user object, but not a subcollection. A true subcollection, like pool members, is addressed like this:
>>pool = b.tm.pools.pool.load(name='testpool') >>members = pool.members_s.get_collection() >>for member in members: ... print member.name 10.10.10.100:80 192.168.103.20:80
To get to your attributes and change them, you just need to pull the dictionary indexed at list item 0:
>>> u = b.tm.auth.users.user.load(name='jason') >>> u.partitionAccess[0]['role'] u'irule-manager' >>> u.partitionAccess[0]['role'] = 'guest' >>> u.update()
- Joel_42834Nimbostratus
F5_Digger has an excellent point that I don't believe has been answered:
Many of the objects in the SDK take keyword parameters that don't appear to be documented.
For example, the ManagementRoot object takes a "token" parameter. I don't see where in the SDK documentation this is listed.
In addition, many objects have functions that are similarly not in the class documentation for that object.
I'd like to be using this SDK for some serious work but am finding it very frustrating due to incomplete documentation.
- JRahmAdmin
Hi @Joel, I'm working on documentation updates this quarter, and hope to have the first updated version released the first week of January.