Getting Started with iControl: Working with the System
So far throughout this Getting Started with iControl series, we’ve worked our way through history, libraries, configuration object, and statistics. In this final article in the series, we’ll tackle a few system functions, namely system ntp and dns settings, and then generating a qkview. Some of the system functions are new functionality to iControl with the rest portal, as system calls are not available via soap. The rule of thumb here is if the system call is available in tmsh via the util branch, it’s available in the rest portal. System DNS Settings When setting up the BIG-IP, some functions require an active DNS configuration to perform lookups. In the GUI, this is configured by navigating to System->Configuration->Device->DNS. In tmsh, it’s configured by modifying the settings in /sys/dns. Note that there is not a create function here, as the DNS objects themselves already exist, whether or not you have configured them. Because of this, when updating these settings via iControl rest, you’ll use the PUT method. An example tmsh configuration for the DNS settings is below: [root@ltm3:Active:Standalone] config # tmsh list sys dns sys dns { name-servers { 10.10.10.2 10.10.10.3 } search { test.local } } This formatted in json looks like this: {"nameServers": ["10.10.10.2", "10.10.10.3"], "search": ["test.local"]} with a PUT to https://hostname/mgmt/tm/sys/dns. Powershell Python Powershell DNS Settings #---------------------------------------------------------------------------- function Get-systemDNS() # # Description # This function retrieves the system DNS configuration # #---------------------------------------------------------------------------- { $uri = "/mgmt/tm/sys/dns"; $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 Write-Host "`nName Servers"; Write-Host "------------"; $items = $obj.nameServers; for($i=0; $i -lt $items.length; $i++) { $name = $items[$i]; Write-Host "`t$name"; } Write-Host "`nSearch Domains"; $items = $obj.search; for($i=0; $i -lt $items.length; $i++) { $name = $items[$i]; Write-Host "`t$name"; } Write-Host "`n" } #---------------------------------------------------------------------------- function Set-systemDNS() # # Description # This function sets the system DNS configuration # #---------------------------------------------------------------------------- { param( [array]$Servers, [array]$SearchDomains ); $uri = "/mgmt/tm/sys/dns"; $link = "https://$Bigip$uri"; $headers = @{}; $headers.Add("ServerHost", $Bigip); $headers.Add("Content-Type", "application/json"); $obj = @{ nameServers = $Servers search = $SearchDomains }; $body = $obj | ConvertTo-Json $secpasswd = ConvertTo-SecureString $Pass -AsPlainText -Force $mycreds = New-Object System.Management.Automation.PSCredential ($User, $secpasswd) $obj = Invoke-RestMethod -Method PUT -Uri $link -Headers $headers -Credential $mycreds -Body $body; Get-systemDNS; } Python DNS Settings def get_sys_dns(bigip, url): try: dns = bigip.get('%s/sys/dns' % url).json() print "\n\n\tName Servers:" for server in dns['nameServers']: print "\t\t%s" % server print "\n\tSearch Domains:" for domain in dns['search']: print "\t\t%s\n\n" %domain except Exception, e: print e def set_sys_dns(bigip, url, servers, search): servers = [x.strip() for x in servers.split(',')] search = [x.strip() for x in search.split(',')] payload = {} payload['nameServers'] = servers payload['search'] = search try: bigip.put('%s/sys/dns' % url, json.dumps(payload)) get_sys_dns(bigip, url) except Exception, e: print e System NTP Settings NTP is another service that some functions like APM access require. In the GUI, the settings for NTP are configured in two places. First, the servers are configured in System->Configuration->Device->NTP. The timezone is configured on the System->Platform page. In tmsh, the settings are configured in /sys/ntp. Like DNS, these objects already exist, so the iControl rest method will be a PUT instead of a POST. An example tmsh configuration for the NTP settings is below: [root@ltm3:Active:Standalone] config # tmsh list sys ntp sys ntp { servers { 10.10.10.1 } timezone America/Chicago } This formatted in json looks like this: {"servers": ["10.10.10.1"], "timezone": "America/Chicago"} with a PUT to https://hostname/mgmt/tm/sys/ntp. Powershell Python Powershell NTP Settings #---------------------------------------------------------------------------- function Get-systemNTP() # # Description # This function retrieves the system NTP configuration # #---------------------------------------------------------------------------- { $uri = "/mgmt/tm/sys/ntp"; $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 Write-Host "`nNTP Servers"; Write-Host "------------"; $items = $obj.servers; for($i=0; $i -lt $items.length; $i++) { $name = $items[$i]; Write-Host "`t$name"; } Write-Host "`nTimezone"; $item = $obj.timezone; Write-Host "`t$item`n"; } #---------------------------------------------------------------------------- function Set-systemNTP() # # Description # This function sets the system NTP configuration # #---------------------------------------------------------------------------- { param( [array]$Servers, [string]$TimeZone ); $uri = "/mgmt/tm/sys/ntp"; $link = "https://$Bigip$uri"; $headers = @{}; $headers.Add("ServerHost", $Bigip); $headers.Add("Content-Type", "application/json"); $obj = @{ servers = $Servers timezone = $TimeZone }; $body = $obj | ConvertTo-Json $secpasswd = ConvertTo-SecureString $Pass -AsPlainText -Force $mycreds = New-Object System.Management.Automation.PSCredential ($User, $secpasswd) $obj = Invoke-RestMethod -Method PUT -Uri $link -Headers $headers -Credential $mycreds -Body $body; Get-systemNTP; } Python NTP Settings def get_sys_ntp(bigip, url): try: ntp = bigip.get('%s/sys/ntp' % url).json() print "\n\n\tNTP Servers:" for server in ntp['servers']: print "\t\t%s" % server print "\n\tTimezone: \n\t\t%s" % ntp['timezone'] except Exception, e: print e def set_sys_ntp(bigip, url, servers, tz): servers = [x.strip() for x in servers.split(',')] payload = {} payload['servers'] = servers payload['timezone'] = tz try: bigip.put('%s/sys/ntp' % url, json.dumps(payload)) get_sys_ntp(bigip, url) except Exception, e: print e Generating a qkview Moving on from configuring some system settings to running a system task, we’ll now focus on a common operational task: running a qkview. In the GUI, this is done via System->Support. This is easy enough at the command line as well with the simple qkview command at the bash prompt, or via tmsh as show below: [root@ltm3:Active:Standalone] config # tmsh run util qkview Gathering System Diagnostics: Please wait ... Diagnostic information has been saved in: /var/tmp/ltm3.test.local.qkview Please send this file to F5 support. This formatted in json looks like this: {"command": "run"} with a POST to https://hostname/mgmt/tm/util/qkview. Powershell Python Powershell - Run a qkview #---------------------------------------------------------------------------- function Gen-QKView() # # Description # This function generates a qkview on the system # #---------------------------------------------------------------------------- { $uri = "/mgmt/tm/util/qkview"; $link = "https://$Bigip$uri"; $headers = @{}; $headers.Add("serverHost", $Bigip); $headers.Add("Content-Type", "application/json"); $secpasswd = ConvertTo-SecureString $Pass -AsPlainText -Force $mycreds = New-Object System.Management.Automation.PSCredential ($User, $secpasswd) $obj = @{ command='run' }; $body = $obj | ConvertTo-Json Write-Host ("Running qkview...standby") $obj = Invoke-RestMethod -Method POST -Uri $link -Headers $headers -Credential $mycreds -Body $body; Write-Host ("qkview is complete and is available in /var/tmp.") } Python - Run a qkview def gen_qkview(bigip, url): payload = {} payload['command'] = 'run' try: print "\n\tRunning qkview...standby" qv = bigip.post('%s/util/qkview' % url, json.dumps(payload)).text if 'saved' in qv: print '\tqkview is complete and available in /var/tmp.' except Exception, e: print e Going Further Now that you have a couple basic tasks in your arsenal, what else might you add? Here are a couple exercises for you to build your toolbox: Configure sshd, creating a custom security banner. Configure snmp Extend the qkview function to support downloading the qkview file generated. Resources All the scripts for this article are available in the codeshare under "Getting Started with iControl Code Samples."2.2KViews0likes2CommentsGetting Started with iControl: Interface Taxonomy & Language Libraries
In part two of this article in the Getting Started with iControl series, we will cover the taxonomy for both the SOAP and REST iControl interfaces, that were discussed in theprevious article on iControl History, as well as introduce the language libraries we unofficially support on DevCentral. Note: This article published by Jason Rahm but co-authored with Joe Pruitt. Taxonomy The two interfaces were designed in different ways and with different goals. iControl SOAP was developed as a functional API behaving very similar to a Java or C++ class where you have interfaces and methods included within those interfaces. Interfaces roughly related to product features and for organization, those interfaces were then grouped into larger namespaces, or product modules. To get a little more specific, iControl SOAP implements a RPC (Remote Procedure Call) type model. To make use of the APIs, one just instantiated an instance of the interface and then used functional (create(), get_list(), ...) and accessor (get_*(), set_*(), ...) methods with it. iControl REST on the other hand, is based on a document model where documents are passed back and forth containing all the relevant information of the given object(s) in question. This interface makes use of the HTTP protocol's VERBs (GET, POST, PUT, PATCH, DELETE) for action and URIs (ie. /mgmt/tm/ltm/pool) for the objects you are manipulating. Instead using methods to perform actions, you use the associated HTTP VERB to trigger the action, the URI to denote the object, and the POST data or Query Parameters to indicated the objects details. iControl REST is currently modeled off of the tmsh shell which uses similar modules and interfaces from SOAP (with different abbreviations) in it's URI path. Module Interface Method iControl SOAP LocalLB Pool get_list() create() add_member() Networking SelfIP get_netmask() set_vlan() Module Sub-Module Component iControl REST ltm pool <pool name> net self <self name> Now for some simple examples using the SOAP and REST interfaces using python libraries. Don't worry if python's not your thing,we’ll cover some perl, powershell, java, and Node.jsas well in the following articles in this series. ### ignore SSL cert warnings >>> import ssl >>> ssl._create_default_https_context = ssl._create_unverified_context ### SOAP >>> import bigsuds >>> b_soap = bigsuds.BIGIP('172.16.44.15', 'admin', 'admin') >>> b_soap.LocalLB.Pool.get_list() ['/Common/myNewPool2'] ### REST >>> from f5.bigip import BigIP as f5 >>> b_rest = f5('172.16.44.15', 'admin', 'admin') >>> b_rest.ltm.pools.get_collection()[0].name u'myNewPool2' Note there really isn't much difference between the two iControl interfaces when it comes to the client code. This is the beauty of libraries, they mask the hard work! We'll dig into the code more in the next article. so don't worry too much about the code above just yet. Interfaces & Objects In the above section, we briefly outlined the high level taxonomy between module and interface. There are literally thousands of defined methods for the SOAP interface, all defined and published in theiControl wikion DevCentral. For the REST interface, you will want toreference the iControl REST wikias well, but you will really want to become intimate with thetmsh reference guide, as a solid understanding of how tmsh works will make a more favorable going. Language Libraries Raw SOAP payload is out of the question unless you enjoy pain, so a library is a given if using the SOAP interface. But even with REST, as easy as it is to interact with interface directly, the fact is if you aren’t using a library, you still have to create all your JSON documents and investigate and prepare all the payload requirements when manipulating or creating objects. When selecting SOAP or REST, the reality is if you’re going to build applications that utilize iControl, you’re definitely going to want to use a library. What is a Library? Depending on the language or languages you might use, a library can be called many things, such as a package, a wrapper, a module, a class, etc. But at it’s most basic meaning, a library is simply a collection of tools and/or functionality that abstracts some of the work required in understanding the underlying protocols and assists in getting things done faster. For example, if you are formatting an office document and changing the font size, color, family, and bolding it on select words throughout the doc, it could be painful to do this if you have to do it more than a few times. You could record a macro of these steps, and then for each subsequent task, replay that work via the macro so you only have to do it once. In a roundabout way, this is what libraries do for you as well. The common repetitive work is done for you, so you can focus on the business logic in your applications. What Libraries Are Available? There are several libraries available, some languages having more than one to support both iControl SOAP and iControl REST, and at least one (like python) that has several libraries for just the SOAP interface. To each his own, we like to expand the horizons around here and equip everyone for whatever language suits best for your needs and abilities. We could list out all the libraries here, but they are already documented on theprogrammability page in the wiki, at least the ones we are directly responsible for. Joe does a lot with PowerShell and Perl, and I with python, and we do have quite a few community members well versed in the .Net languages and python too, though there is some support for java and ruby as well. On Github I've seen aniControl wrapper for the Go language, and I seem to recall some love for PHP a while back. Now that we'rethrough the historyand some foundational information on the SOAP and REST interfaces, we can move on to actually building something! In the next article in this series, we'll jump into some code samples to highlight the pros and cons of working with SOAP and REST, as well as show you the differences among a few languages for each interface in accomplishing the same tasks.2.1KViews1like1CommentGetting Started with iControl: History
tl;dr - iControl provides access to BIG-IP management plane services through SOAP and REST API interfaces. The Early Days iControl started back in early 2000. F5 had 2 main products: BIG-IP and 3-DNS (later GTM, now BIG-IP DNS). BIG-IP managed the local datacenter's traffic, while 3-DNS was the DNS orchestrator for all the BIG-IP's in numerous data centers. The two products needed a way to communicate with each other to ensure they were making the right traffic management decisions respective to all of the products in the system. At the time, the development team was focused on developing the fastest running code possible and that idea found it's way into the cross product communication feature that was developed. The technology the team chose to use was the Common Object Request Broker Architecture (CORBA) as standardized by the Object Management Group (OMG). Coming hot off the heels of F5's first management product SEE-IT (which was another one of my babies), the dev team coined this internal feature as "LINK-IT" since it "linked" the two products together. With the development of our management, monitoring, and visualization product SEE-IT, we needed a way to get the data off of the BIG-IP. SEE-IT was written for Windows Server and we really didn't want to go down the route of integrating into the CORBA interface due to several factors. So, we wrote a custom XML provider on BIG-IP and 3-DNS to allow for configuration and statistic data to be retrieved and consumed by SEE-IT. It was becoming clear to me that automation and customization of our products would be beneficial to our customers who had been previously relying on our SNMP offerings. We now had 2 interfaces for managing and monitoring our devices: one purely internal (LINK-IT) and the other partially (XML provider). The XML provider was very specific to our SEE-IT products use case and we didn't see a benefit of trying to morph that so we looked back at LINK-IT to see what we could to do make that a publicly supported interface. We began work on documenting and packaging it into F5's first public SDK. About that time, a new standard was emerging for exchanging structured information. The Simple Object Access Protocol (SOAP), which allows for structured information exchange, was being developed but not fully ratified until version 1.2 in 2003. I had to choose to roll our own XML implementation or make use of this new proposed specification. There was risk as the specification was not a standard yet but I made the choice to go the SOAP route as I felt that picking a standard format would give us the best 3rd party software compatibility down the road. Our CORBA interface was built on a nice class model which I used as a basis for an SOAP/XML wrapper on top of that code. I even had a great code name for the interface: X-LINK-IT! For those who were around when I gave my "XML at F5" presentation to F5 Product Development, you may remember the snide comments going around afterwards about how XML was not a great technology and a big mistake supporting. Good thing I didn't listen to them... At this point in mid-2001, the LINK-IT SDK was ready to go and development of X-LINK-IT was well underway. Well, let's just say that Marketing didn't agree with our ingenious product naming and jumped in to VETO our internal code names for our public release. I'll give our Chief Marketer Jeff Pancottine credit for coining the term "iControl" which was explained to me as "Internet Control". This was the start of F5's whole Internet Controlled Architecture messaging by the way. So, LINK-IT and X-LINK-IT were dead and iControl CORBA and iControl SOAP were born. The Death of CORBA, All Hail SOAP The first version of the iControl SDK for CORBA was released on 5/25/2001 with the SOAP version trailing it by a few months. This was around the BIG-IP version 3 time frame. We chugged along for a few years through the BIG-IP version 4 life and then a big event occurred that was the demise for CORBA - well, it's actually 2 events. The first event was the full rewrite of the BIG-IP data plane when TMOS was introduced in BIG-IP, version 9 (we skipped from version 4 to version 9 for some reason that slips my mind). Since virtually the entire product was rewritten, the interfaces that were tied to the product features, would have to change drastically. We used this as an opportunity to look at the next evolution of iControl. Until this point, iControl SOAP was just a shim on top of CORBA and it had some performance issues so we worked at splitting them apart and having SOAP talk directly to our configuration engine. Now we had 2 interface stacks side by side. The second event was learning we only had 1 confirmed customer using the CORBA interface compared to the 100's using SOAP. Given that knowledge and now that BIG-IP and 3-DNS no longer used iControl CORBA to talk to each other, the decision was made to End of Life iControl CORBA with Version 9.0. But, iControl SOAP still used the CORBA IDL files for it's API definitions and documentation so fun trivia note: the same CORBA tools are still in place in today's iControl SOAP build tools that were there in version 3 of BIG-IP. I'm fairly sure that is the longest running component in our build system. The Birth of DevCentral I can't speak about iControl without mentioning DevCentral. We had our iControl SDK out but no where to directly support the developers using it. At that time, F5 was a "hardware" company and product support wasn't ready to support application developers. Not many know that DevCentral was created due to the popularity of iControl with our customer base and was born on a PC under my desk in 2003. I continued to help with DevCentral part time for a few years but in 2007 I decided to work full time on building our community and focusing 100% on DevCentral. It was about this time that we were pushing the idea of merging the application and infrastructure teams together - or at least getting them to talk more frequently. This was a precursor to the whole DevOps mentality so I'd like to think we were a bit ahead of the curve on that. Enter iControl REST In 2013, iControl was reaching it's teenage years and starting to show it's age a bit. While SOAP is still supported by all the major tool vendors, application development was shifting to richer browser-based apps. And with that, Representational State Transfer (REST) was gaining steam. REST defined a usage pattern for using browser based mechanisms with HTTP to access objects across the network with a JavaScript Object Notation (JSON) format for the content. To keep up with current technologies, the PD team at F5 developed the first REST/JSON interface in BIG-IP version 11.5 as Early Access and was made Generally Available in version 11.6. With the REST interface, more modern web paradigms could be supported and you could actually code to iControl directly from a browser! There were also additional interface based tools for developing and debugging built directly into the service to give service listings and schema definitions. At the time of this writing, F5 supports both of the main iControl interfaces (SOAP and REST) but are focusing all new energy on our REST platform for the future. For those who have developed SOAP integrations, have no fear as that interface is not going away. It will just likely not get all the new feature support that will be added into the REST interfaces over time. SDKs, Toolkits, and Libraries Through the years, I've developed several client libraries (.Net, PowerShell, Java) for iControl SOAP to assist with ramp-up time for initial development. There have also been numerous other language based libraries for languages like Ruby, PHP, and Python developed by the community and other development teams. Most recently, F5 has published the iControl library for Python which is available as part of our OpenStack integration. DevCentral is your place to for our API documentation where we update our wiki with API changes on each major release. And as time rolls on, we are adding REST support for new and existing products such as iWorkflow, BIG-IQ, and other products yet to be released that will include SDKs and other reference material. F5 has a strong commitment to our end users and their automation and integration projects with F5 technologies. Coming full circle, my current role is overseeing APIs and SDKs for standards, consistency, and completeness in our Programmability and Orchestration (P&O) team so keep a look out for future articles from me on our efforts on that front. And REST assured, we will continue to do all we can to help our customers move to new architectures and deployment models with programmability and automation with F5 technologies.3KViews1like1CommentGetting Started with iControl: Working with Statistics
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 Bocchinoreleased 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 Powershell Python 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 Powershell Python 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."4.5KViews0likes3CommentsGetting Started with iControl: Working with Configuration Objects
In the previous articles in the Getting Started with iControl series, we covered the history of iControl, taxonomy, and introduced the languages most common to our community. In this article, we'll start looking at some code samples. With each task we cover, we'll show an example for a few languages for both the soap and rest interface. The sheer amount of space in this article required to show full samples of each language for both soap and rest would be overwhelming in line, so for the sample code, we have pulled out the appropriate sections of the code for you to study. Understand that based on the libraries used, if at all, some of the work may be abstracted. Full scripts for each language and portal preference are linked at the bottom of this article. If you click each of the iControl REST and iControl SOAP tabs, you'll see a language listing for the sample code provided. Joe Pruitt is the super hero providing everything not python here, so give him a shout out next time you see him in these parts. Note: This article published by Jason Rahm but co-authored with Joe Pruitt. With all the disclaimers out of the way, let's get down to business! The first thing you have to do to talk to the BIG-IP is know where to connect. Some prerequisites: BIG-IP Hostname Username Password Portal URL rest: https://host/mgmt/<tm> (most calls are for /tm, though there are others) soap: https://host/iControl/iControlPortal.cgi There are dozens of articles and codeshare samples on DevCentral on creating/modifying objects both in rest and soap, so we're not going to cover all the details ad nauseam here. But we do want to introduce the basic concepts of retrieving, creating, and modifying a couple simple objects in this article. The first one we'll tackle is one of the most important building blocks of the BIG-IP configuration: the pool. Working with the Pool Object The pool has many attributes that can be set, but not all are required. In this exercise however, we're going to simply demonstrate the creation of a pool and a pool listing. The pool object can be created via rest with a simple name, and with soap with the name, the lb method, and the members attribute (it can be empty, but the attribute needs to be present.) iControl SOAP Java Perl Powershell Python iControl REST Node.js Perl Powershell Python Java SOAP Example public void displayAllPools() throws Exception { String [] pool_list = m_interfaces.getLocalLBPool().get_list(); System.out.println("Pools\n"); System.out.println("-----------\n"); for(int i=0; i<pool_list.length; i++) { System.out.println(pool_list[i]); } } public void createPool(String poolname, String partition) throws Exception { String [] pool_list = new String[] {"/" + partition + "/" + poolname}; iControl.LocalLBLBMethod [] lb_methods = new iControl.LocalLBLBMethod[] { iControl.LocalLBLBMethod.LB_METHOD_ROUND_ROBIN }; iControl.CommonIPPortDefinition[][] membersAofA = new iControl.CommonIPPortDefinition[1][]; membersAofA[0] = new iControl.CommonIPPortDefinition[1]; membersAofA[0][0] = new iControl.CommonIPPortDefinition(); membersAofA[0][0].setAddress("10.10.10.10"); membersAofA[0][0].setPort(80); m_interfaces.getLocalLBPool().create( pool_list, lb_methods, membersAofA ); System.out.println("Pool " + poolname + " created in partition " + partition); } Perl SOAP Example #---------------------------------------------------------------------------- # getPoolList #---------------------------------------------------------------------------- sub getPoolList() { $soapResponse = $Pool->get_list(); &checkResponse($soapResponse); my @pool_list = @{$soapResponse->result}; print "POOL LIST\n"; print "---------\n"; foreach $pool (@pool_list) { print " ${pool}\n"; } } #---------------------------------------------------------------------------- # createPool() #---------------------------------------------------------------------------- sub createPool() { my ($partition, $pool) = @_; print "CREATING POOL $pool\n"; my @pool_names = [$pool]; my @lb_methods = ["LB_METHOD_ROUND_ROBIN"]; $member = { address => "10.10.10.10", port => 80 }; # memberA is the 1st dimension of the array, we need one for each pool push @memberA, $member; # memberAofA is the 2nd dimension. push pool members for each pool here. push @memberAofA, [@memberA]; $soapResponse = $Pool->create( SOAP::Data->name( pool_names => ["/$partition/$pool"]), SOAP::Data->name( lb_methods => ["LB_METHOD_ROUND_ROBIN"]), SOAP::Data->name(members => [@memberAofA]) ); &checkResponse($soapResponse); print "POOL ${pool} created in partition ${partition}...\n"; } Powershell SOAP Example #---------------------------------------------------------------------------- function Get-PoolList() #---------------------------------------------------------------------------- { $pool_list = $(Get-F5.iControl).LocalLBPool.get_list(); Write-Host "POOL LIST"; Write-Host "---------"; foreach($pool in $pool_list) { Write-Host " $pool"; } } #---------------------------------------------------------------------------- function Create-Pool() # # Description: # This function creates a new pool if the given pool name doesn't # already exist. # # Parameters: # Name - The Name of the pool you wish to create. # MemberList - A string list of pool member addresses. # MemberPort - The port the pool members will be configured with. #---------------------------------------------------------------------------- { param( [string]$Name, [string[]]$MemberList, [int]$MemberPort ); $IPPortDefList = New-Object -TypeName iControl.CommonIPPortDefinition[] $MemberList.Length; for($i=0; $i-lt$MemberList.Length; $i++) { $IPPortDefList[$i] = New-Object -TypeName iControl.CommonIPPortDefinition; $IPPortDefList[$i].address = $MemberList[$i]; $IPPortDefList[$i].port = $MemberPort; } Write-Host "Creating Pool $Name"; $(Get-F5.iControl).LocalLBPool.create( (,$Name), (,"LB_METHOD_ROUND_ROBIN"), (,$IPPortDefList) ); Write-Host "Pool '$Name' Successfully Created"; } Python SOAP Example def get_pool_list(bigip): try: poollist = bigip.LocalLB.Pool.get_list() print "POOL LIST" print " ---------" for pool in poollist: print " %s" % pool except Exception, e: print e def create_pool(bigip, pool): try: bigip.LocalLB.Pool.create_v2([pool], ['LB_METHOD_ROUND_ROBIN'], [[]]) except Exception, e: print e Node.js REST Example //-------------------------------------------------------- function handleVERB(verb, resource, body, callback) { //-------------------------------------------------------- getAuthToken( function(token) { httpRequest(verb, resource, body, null, null, token, function(json) { callback(json); }); }); } //-------------------------------------------------------- function handleGetPoolList(callback) { //-------------------------------------------------------- var uri = "/mgmt/tm/ltm/pool"; handleVERB("GET", uri, null, function(json) { callback(json); }); } //-------------------------------------------------------- function handleCreatePool(pool, partition, callback) { //-------------------------------------------------------- var uri = "/mgmt/tm/ltm/pool"; var body = '{"name":"' + pool + '", "partition":"' + partition + '"}'; console.log("BODY: " + body); handleVERB("POST", uri, body, function(json) { callback(json); }); } Perl REST Example #--------------------------------------------------- sub getPoolList() { #--------------------------------------------------- $resp_json = &handleGET("/mgmt/tm/ltm/pool"); $resp = decode_json($resp_json); @items = @{$resp->{"items"}}; print "POOL NAMES\n"; print "----------\n"; foreach $item (@items) { $fullPath = $item->{"fullPath"}; print " $fullPath\n"; print Dumper($item); } } #--------------------------------------------------- sub handleGET() { #--------------------------------------------------- my ($resource) = @_; $url = &buildURL($resource); $resp = &getHttpRequest($url); return $resp; } #--------------------------------------------------- sub createPool() { #--------------------------------------------------- my ($pool, $partition) = @_; my $poolObj; $poolObj->{"name"} = $pool; $poolObj->{"partition"} = $partition; my $json = encode_json($poolObj); print "JSON: $json\n"; exit(); $resp = &handlePOST("/mgmt/tm/ltm/pool", $json); print Dumper($resp); } #--------------------------------------------------- sub handlePOST() { #--------------------------------------------------- my ($resource, $body) = @_; if ( $body eq "" ) { &usage(); } $url = &buildURL($resource); $resp = &postHttpRequest($url, $body); return $resp; } Powershell REST Example #---------------------------------------------------------------------------- function Get-PoolList() #---------------------------------------------------------------------------- { $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 Create-Pool() # # Description: # This function creates a new pool if the given pool name doesn't # already exist. # # Parameters: # Name - The Name of the pool you wish to create. # Partition - The name of the partition to place the pool in. #---------------------------------------------------------------------------- { param( [string]$Name, [string]$Partition ); $uri = "/mgmt/tm/ltm/pool"; $link = "https://$Bigip$uri"; $headers = @{}; $headers.Add("ServerHost", $Bigip); $headers.Add("Content-Type", "application/json"); $obj = @{ name=$Name partition=$Partition }; $body = $obj | ConvertTo-Json $secpasswd = ConvertTo-SecureString $Pass -AsPlainText -Force $mycreds = New-Object System.Management.Automation.PSCredential ($User, $secpasswd) $obj = Invoke-RestMethod -Method POST -Uri $link -Headers $headers -Credential $mycreds -Body $body; Write-Host "Pool ${Name} created in partition ${Partition}" } Python REST Example 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 create_pool(bigip, url, pool, part=None): try: payload = {} payload['name'] = pool if part is not None: payload['partition'] = part pool_config = bigip.post("%s/ltm/pool" % url, json.dumps(payload)).json() print pool_config except Exception, e: print e Working with the Data-Group Object Data-groups are interesting objects with which to work, primarily because you can have internal data-groups, which are wholly contained within bigip.conf, and external data-groups, which has it's definition in bigip.conf but the records are kept in an external file. Also interesting is that this particular object has different attributes via soap than it does in rest. In soap, all the records can be individually managed, so you can add, find, and delete on a record by record basis. Not so with rest. In rest we have the concepts of collections and a subcollections. A collection is like a list of pools. A sub-collection would be the members of a pool. Ideally, a data-group itself would be part of a collection, and it's records would be a subcollection, but that is not the case today. This has major implications if you want to update a data-group. Say you have a data-group with 10,000 records. You want add a record, so you issue an HTTP PUT method with your payload of one record. Instead of the desired outcome of a modified data-group with 10,001 records, you now have a much smaller data-group of exactly one record. Not good! So make sure if you are using the rest interface with data-groups, you store all the existing records, make all your adds/changes/deletes, then PUT the entire record collection. But where the soap interface has the upper hand with being able to update individual records, rest punches back with the ability to list out all data-group at once, whereas with soap, you have to query each type of data-group among string, integer, and address. So there are some pros and cons to both approaches to weigh as you dig in. iControl SOAP Java Perl Powershell Python iControl REST Node.js Perl Powershell Python Java SOAP Example public void createDataGroup(String datagroup) throws Exception { // Create String Class iControl.LocalLBClassStringClass [] StringClassA = new iControl.LocalLBClassStringClass[1]; StringClassA[0] = new iControl.LocalLBClassStringClass(); StringClassA[0].setName(datagroup); StringClassA[0].setMembers(new String [] { "a", "b", "c" }); m_interfaces.getLocalLBClass().create_string_class(StringClassA); // Set Values String [][] valuesAofA = new String[1][]; valuesAofA[0] = new String[] { "data 1", "data 2", "data 3" }; m_interfaces.getLocalLBClass().set_string_class_member_data_value( StringClassA, valuesAofA ); getDataGroup(datagroup); } public void removeFromDataGroup(String datagroup) throws Exception { String [] names = new String[] {"c"}; iControl.LocalLBClassStringClass [] StringClassA = new iControl.LocalLBClassStringClass[1]; StringClassA[0] = new iControl.LocalLBClassStringClass(); StringClassA[0].setName(datagroup); StringClassA[0].setMembers(new String [] { "c" }); m_interfaces.getLocalLBClass().delete_string_class_member(StringClassA); getDataGroup(datagroup); } //-------------------------------------------------------------------------- // //-------------------------------------------------------------------------- public void addToDataGroup(String datagroup) throws Exception { // Create String Class iControl.LocalLBClassStringClass [] StringClassA = new iControl.LocalLBClassStringClass[1]; StringClassA[0] = new iControl.LocalLBClassStringClass(); StringClassA[0].setName(datagroup); StringClassA[0].setMembers(new String [] { "d", "e" }); m_interfaces.getLocalLBClass().add_string_class_member(StringClassA); // Set Values String [][] valuesAofA = new String[1][]; valuesAofA[0] = new String[] { "data 4", "data 5" }; m_interfaces.getLocalLBClass().set_string_class_member_data_value( StringClassA, valuesAofA ); getDataGroup(datagroup); } Perl SOAP Example #---------------------------------------------------------------------------- sub createDataGroup() #---------------------------------------------------------------------------- { my ($datagroup) = @_; my @names = ("a", "b", "c"); my $StringClass = { name => $datagroup, members => [@names] }; # Create Data group with names $soapResponse = $Class->create_string_class( SOAP::Data->name(classes => [$StringClass]) ); &checkResponse($soapResponse); # Set values # Build Values 2-D Array for values parameter my @valuesA = ("data 1", "data 2", "data 3"); my @valuesAofA; push @valuesAofA, [@valuesA]; $soapResponse = $Class->set_string_class_member_data_value ( SOAP::Data->name(class_members => [$StringClass]), SOAP::Data->name(values => [@valuesAofA]) ); &checkResponse($soapResponse); &getDataGroup($datagroup); } #---------------------------------------------------------------------------- sub removeFromDataGroup() #---------------------------------------------------------------------------- { my ($datagroup) = @_; my @names = ("c"); my $StringClass = { name => $datagroup, members => [@names] }; # Create Data group with names $soapResponse = $Class->delete_string_class_member( SOAP::Data->name(class_members => [$StringClass]) ); &checkResponse($soapResponse); &getDataGroup($datagroup); } #---------------------------------------------------------------------------- sub addToDataGroup() #---------------------------------------------------------------------------- { my ($datagroup) = @_; my @names = ("d", "e"); my $StringClass = { name => $datagroup, members => [@names] }; # Create Data group with names $soapResponse = $Class->add_string_class_member( SOAP::Data->name(class_members => [$StringClass]) ); &checkResponse($soapResponse); # Set values # Build Values 2-D Array for values parameter my @valuesA = ("data 4", "data 5"); my @valuesAofA; push @valuesAofA, [@valuesA]; $soapResponse = $Class->set_string_class_member_data_value ( SOAP::Data->name(class_members => [$StringClass]), SOAP::Data->name(values => [@valuesAofA]) ); &checkResponse($soapResponse); &getDataGroup($datagroup); } Powershell SOAP Example #------------------------------------------------------------------------- function Create-DataGroup() #------------------------------------------------------------------------- { param( [string]$Name ); $StringClassA = New-Object -TypeName iControl.LocalLBClassStringClass[] 1; $StringClassA[0] = New-Object -TypeName iControl.LocalLBClassStringClass; $StringClassA[0].name = $Name; $StringClassA[0].members = ("a", "b", "c"); $(Get-F5.iControl).LocalLBClass.create_string_class( $StringClassA ); $DataValueA = ("data 1", "data 2", "data 3"); $DataValuesAofA = $(Get-F5.iControl).LocalLBClass.set_string_class_member_data_value( $StringClassA, (, $DataValueA) ) Get-DataGroup -Name $Name; } #------------------------------------------------------------------------- function RemoveFrom-DataGroup() #------------------------------------------------------------------------- { param( [string]$Name ); $StringClassA = New-Object -TypeName iControl.LocalLBClassStringClass[] 1; $StringClassA[0] = New-Object -TypeName iControl.LocalLBClassStringClass; $StringClassA[0].name = $Name; $StringClassA[0].members = ("c"); $(Get-F5.iControl).LocalLBClass.delete_string_class_member( $StringClassA ); Get-DataGroup -Name $Name; } #------------------------------------------------------------------------- function AddTo-DataGroup() #------------------------------------------------------------------------- { param( [string]$Name ); $StringClassA = New-Object -TypeName iControl.LocalLBClassStringClass[] 1; $StringClassA[0] = New-Object -TypeName iControl.LocalLBClassStringClass; $StringClassA[0].name = $Name; $StringClassA[0].members = ("d", "e"); $(Get-F5.iControl).LocalLBClass.add_string_class_member( $StringClassA ); $DataValueA = ("data 4", "data 5"); $DataValuesAofA = $(Get-F5.iControl).LocalLBClass.set_string_class_member_data_value( $StringClassA, (, $DataValueA) ) Get-DataGroup -Name $Name; } Python SOAP Example def get_dg_list(bigip): try: dg_str_list = bigip.LocalLB.Class.get_string_class_list() dg_str_names = bigip.LocalLB.Class.get_string_class(dg_str_list) for dg in dg_str_names: print dg print ' Data Group: %s' % dg['name'] for x in dg['members']: print ' %s' % x except Exception, e: print e def extend_dg(bigip, dgname, keys, values): try: bigip.LocalLB.Class.add_string_class_member([{'name': dgname, 'members': keys}]) bigip.LocalLB.Class.set_string_class_member_data_value([{'name': dgname, 'members': keys}], [[values]]) except Exception, e: print e def contract_dg(bigip, dgname, keys): try: bigip.LocalLB.Class.delete_string_class_member([{'name': dgname, 'members': keys}]) except Exception, e: print e def create_dg(bigip, dgname, keys, values): try: bigip.LocalLB.Class.create_string_class([{'name': dgname, 'members': keys}]) bigip.LocalLB.Class.set_string_class_member_data_value([{'name': dgname, 'members': keys}], [[values]]) except Exception, e: print e Node.js REST Example //-------------------------------------------------------- function createDataGroup(datagroup) { //-------------------------------------------------------- datagroup = datagroup.replace(/\//g, "~"); var uri = "/mgmt/tm/ltm/data-group/internal"; var dgObj = {}; dgObj.name = datagroup; dgObj.type = "string"; dgObj.records = [ {name: "a", data: "data 1"}, {name: "b", data: "data 2"}, {name: "c", data: "data 3"}, ]; var body = JSON.stringify(dgObj); handleVERB("POST", uri, body, function(json) { console.log(json); }); } //-------------------------------------------------------- function removeFromDataGroup(datagroup) { //-------------------------------------------------------- dg_uri = datagroup.replace(/\//g, "~"); var uri = "/mgmt/tm/ltm/data-group/internal/" + dg_uri; var dgObj = {}; dgObj.name = datagroup; dgObj.records = [ {name: "a", data: "data 1"}, {name: "b", data: "data 2"} ]; var body = JSON.stringify(dgObj); handleVERB("PATCH", uri, body, function(json) { console.log(json); }); } //-------------------------------------------------------- function addToDataGroup(datagroup) { //-------------------------------------------------------- dg_uri = datagroup.replace(/\//g, "~"); var uri = "/mgmt/tm/ltm/data-group/internal/" + dg_uri; var dgObj = {}; dgObj.name = datagroup; dgObj.records = [ {name: "a", data: "data 1"}, {name: "b", data: "data 2"}, {name: "d", data: "data 4"}, {name: "e", data: "data 5"} ]; var body = JSON.stringify(dgObj); handleVERB("PATCH", uri, body, function(json) { console.log(json); }); } Perl REST Example #---------------------------------------------------------------------------- sub createDataGroup() #---------------------------------------------------------------------------- { my ($datagroup) = @_; my @names = ("a", "b", "c"); my $StringClass = { name => $datagroup, members => [@names] }; # Create Data group with names $soapResponse = $Class->create_string_class( SOAP::Data->name(classes => [$StringClass]) ); &checkResponse($soapResponse); # Set values # Build Values 2-D Array for values parameter my @valuesA = ("data 1", "data 2", "data 3"); my @valuesAofA; push @valuesAofA, [@valuesA]; $soapResponse = $Class->set_string_class_member_data_value ( SOAP::Data->name(class_members => [$StringClass]), SOAP::Data->name(values => [@valuesAofA]) ); &checkResponse($soapResponse); &getDataGroup($datagroup); } #---------------------------------------------------------------------------- sub removeFromDataGroup() #---------------------------------------------------------------------------- { my ($datagroup) = @_; my @names = ("c"); my $StringClass = { name => $datagroup, members => [@names] }; # Create Data group with names $soapResponse = $Class->delete_string_class_member( SOAP::Data->name(class_members => [$StringClass]) ); &checkResponse($soapResponse); &getDataGroup($datagroup); } #---------------------------------------------------------------------------- sub addToDataGroup() #---------------------------------------------------------------------------- { my ($datagroup) = @_; my @names = ("d", "e"); my $StringClass = { name => $datagroup, members => [@names] }; # Create Data group with names $soapResponse = $Class->add_string_class_member( SOAP::Data->name(class_members => [$StringClass]) ); &checkResponse($soapResponse); # Set values # Build Values 2-D Array for values parameter my @valuesA = ("data 4", "data 5"); my @valuesAofA; push @valuesAofA, [@valuesA]; $soapResponse = $Class->set_string_class_member_data_value ( SOAP::Data->name(class_members => [$StringClass]), SOAP::Data->name(values => [@valuesAofA]) ); &checkResponse($soapResponse); &getDataGroup($datagroup); } Powershell REST Example #---------------------------------------------------------------------------- function Create-DataGroup() # # Description: # This function creates a new internal data group # # Parameters: # Name - The Name of the data group #---------------------------------------------------------------------------- { param( [string]$Name ); $uri = "/mgmt/tm/ltm/data-group/internal"; $link = "https://$Bigip$uri"; $headers = @{}; $headers.Add("ServerHost", $Bigip); $headers.Add("Content-Type", "application/json"); $obj = @{ name=$Name type="string" records= ( @{ name="a" data="data 1" }, @{ name="b" data="data 2" }, @{ name="c" data="data 3" } ) }; $body = $obj | ConvertTo-Json $secpasswd = ConvertTo-SecureString $Pass -AsPlainText -Force $mycreds = New-Object System.Management.Automation.PSCredential ($User, $secpasswd) $obj = Invoke-RestMethod -Method POST -Uri $link -Headers $headers -Credential $mycreds -Body $body; Write-Host "Pool ${Name} created in partition ${Partition}" } #---------------------------------------------------------------------------- function RemoveFrom-DataGroup() # # Description: # This function removes an entry from a data group # # Parameters: # Name - The Name of the data group #---------------------------------------------------------------------------- { param( [string]$Name ); $uri = "/mgmt/tm/ltm/data-group/internal/${Name}"; $link = "https://$Bigip$uri"; $headers = @{}; $headers.Add("ServerHost", $Bigip); $headers.Add("Content-Type", "application/json"); $obj = @{ name=$Name records= ( @{ name="a" data="data 1" }, @{ name="b" data="data 2" } ) }; $body = $obj | ConvertTo-Json $secpasswd = ConvertTo-SecureString $Pass -AsPlainText -Force $mycreds = New-Object System.Management.Automation.PSCredential ($User, $secpasswd) $obj = Invoke-RestMethod -Method PATCH -Uri $link -Headers $headers -Credential $mycreds -Body $body; $obj | Format-List } #---------------------------------------------------------------------------- function AddTo-DataGroup() # # Description: # This function adds records to an existing data group # # Parameters: # Name - The Name of the data group #---------------------------------------------------------------------------- { param( [string]$Name ); $uri = "/mgmt/tm/ltm/data-group/internal/${Name}"; $link = "https://$Bigip$uri"; $headers = @{}; $headers.Add("ServerHost", $Bigip); $headers.Add("Content-Type", "application/json"); $obj = @{ name=$Name records= ( @{ name="a" data="data 1" }, @{ name="b" data="data 2" }, @{ name="d" data="data 4" }, @{ name="e" data="data 5" } ) }; $body = $obj | ConvertTo-Json $secpasswd = ConvertTo-SecureString $Pass -AsPlainText -Force $mycreds = New-Object System.Management.Automation.PSCredential ($User, $secpasswd) $obj = Invoke-RestMethod -Method PATCH -Uri $link -Headers $headers -Credential $mycreds -Body $body; $obj | Format-List } Python REST Example def get_dg_list(rq, url): try: dg_list = rq.get('%s/ltm/data-group/internal' % url).json() for dg in dg_list['items']: print dg print ' Data Group: %s' % dg['name'] print ' --------------' if 'records' in dg: for record in dg['records']: if 'data' in record: print ' %s: %s' % (record['name'], record['data']) else: print ' %s' % record['name'] except Exception, e: print e def extend_dg(rq, url, dgname, additional_records): dg = rq.get('%s/ltm/data-group/internal/%s' % (url, dgname)).json() current_records = dg['records'] new_records = [] for record in current_records: if 'data' in record: nr = [{'name': record['name'], 'data': record['data']}] else: nr = [{'name': record['name']}] new_records.extend(nr) for record in additional_records: if 'data' in record: nr = [{'name': record['name'], 'data': record['data']}] else: nr = [{'name': record['name']}] new_records.extend(nr) payload = {} payload['records'] = new_records rq.put('%s/ltm/data-group/internal/%s' % (url, dgname), json.dumps(payload)) def contract_dg(rq, url, dgname, removal_records): dg = rq.get('%s/ltm/data-group/internal/%s' % (url, dgname)).json() current_records = dg['records'] new_records = [] for record in removal_records: if 'data' in record: nr = [{'name': record['name'], 'data': record['data']}] else: nr = [{'name': record['name']}] new_records.extend(nr) new_records = [x for x in current_records if x not in new_records] payload = {} payload['records'] = new_records rq.put('%s/ltm/data-group/internal/%s' % (url, dgname), json.dumps(payload)) def create_dg(rq, url, dgname, records): new_records = [] for record in records: if 'data' in record: nr = [{'name': record['name'], 'data': record['data']}] else: nr = [{'name': record['name']}] new_records.extend(nr) payload = {} payload['type'] = 'string' payload['name'] = dgname payload['records'] = new_records try: rq.post('%s/ltm/data-group/internal' % url, json.dumps(payload)) except Exception, e: print e Resources All the scripts for this article are available in the codeshare under "Getting Started with iControl Code Samples."3.7KViews0likes0Comments