Working with Tables

What’s a Table?

In an iApp’s presentation section, tables let you define a set of several widgets as a row and let the user add/remove rows as needed. For example, to build a pool of web servers, a table can define a row with two columns: the IP address for a member and the port that it’s listening on. That would look like this:
section pool {
    table www_servers {
        string address validator "IPAddress"
        string port validator "PortNumber"
    }
}

That’ll let the user provide as many ip/port combinations as they want. However, once the user submits the form, the implementation script needs to process it.

Processing a Table in TCL

Let’s say the user enters 3 pool members: 10.0.0.1:80, 10.0.0.2:80, and 10.0.0.3:80. When the user submits that form, it’ll become available to the implementation script as a variable: $::pool__www_servers that’ll have this for a value:
{{
    address 10.0.0.1
    port 80
}} {{
    address 10.0.0.2
    port 80
}} {{
    address 10.0.0.3
    port 80
}}

While not terribly intimidating, it doesn’t look too easy to parse either - but it’s really not that bad. The key is to notice that TCL is going to treat this as a series of nested lists due to how it handles curly braces. The next thing to notice is that the columns and data are in “array set” notation already (arrays are what most languages call “hashes” or “content addressable arrays”). Combining this, the general pattern to get to the values is:
set members {}
foreach row $::pool__www_servers {
    array set columns [lindex $row 0]
    puts "IP Address: $columns(address), Port: $columns(port)"
    lappend members "$columns(address):$columns(port)"
}
tmsh::create ltm pool ${tmsh::app_name}-pool \
    members add \{ [join $members] \}

What that does is loop over each row of the table, assigning that to the “row” variable. That actually contains a one-element list which is in “array set” form already, so we create the “columns” array using the first element of the row list. Once you have that, the hard work is done and you can get to any of the fields by using the name of the field as the argument to columns. In this example, I print out the variables, and then build up a list called “members” that I then use to create a pool.

Things to Consider

The pattern that I describe above of looping over the rows, assigning each to an array, and then operating that data works well for many cases, and doesn’t require any specialized toolkits or procedures to parse. That’s pretty nice - but it’s not perfect.
One shortcoming is that the data has to be in “array set” form - and if the user submits an empty value, or a value containing white space, it’ll no longer be in the correct form. I avoid that in my case by providing a validator in the APL to guarantee that my data is in the expected form, but that might not always be possible. Basically, if you can’t guarantee that your data will be consumable by “array set” then this method won’t work. In such cases, a toolkit of procedures to more properly parse it would be appropriate (and perhaps we’ll post one here on DC…).

The BIG-IP API Reference documentation contains community-contributed content. F5 does not monitor or control community code contributions. We make no guarantees or warranties regarding the available code, and it may contain errors, defects, bugs, inaccuracies, or security vulnerabilities. Your access to and use of any code available in the BIG-IP API reference guides is solely at your own risk.