Did I lose you at “Documenting?”  Documentation is rarely at the top of the list of things we make time for, yet the practice is critical.  You never know when you’re going to see that iRule again, and if it’s six months later after an upgrade and you have to stare at code for a while just to figure out what its purpose is, well, that’s a problem.  With iRules, comments are not compiled into byte-code, so there is no performance hit taken with their presence.    Some things you could consider for the documentation header:

  • Overall concept for the rule or some simple workflow
  • Date/Time the header was created
  • Revision information
  • External Requirements (profiles, classes, dns configurations)
  • LTM Platform
  • TMOS Version and Hotfix level the iRule was developed on
  • Known versions the iRule does NOT work on (and why if known)
  • CMP Compatibility flag
  • iRule performance data

This in comment format would look something like this (not all the above list included):

## GRACEFUL SSL SECURITY STRENGTH ENFORCEMENT
##
## Sends users with browsers not capable of 256-bit encryption to
## error page, forces capable browsers to higher security profile
##
## CMP compatible:  Yes
##
## This rule requires:
##  1. Default clientssl profile allowing 128-bit encryption: DEFAULT:!ADH:!EXPORT40:!EXP:!LOW
##  2. Additional clientssl profile set to higher security: DEFAULT:!ADH:!EXPORT40:!EXP:!LOW:!MEDIUM
##  3. HTTP profile
##  4. Datagroup (accepted_ciphers in this example) with hex strings of 256-bit accepted ciphers:
##  class accepted_ciphers {
##     "0035"
##     "c014" 
##  }
##  # NOTE that the "" should not be added when submitting the strings in the GUI
##  
## This rule developed on:
##  TMOS v9.4.8 355.0
##  LTM1500
##
##  Note: Confirmed NOT WORKING on TMOS vBIG-IP Version 10.2.0 1707.0
##   (due to failure of eval of SSL::profile in CLIENT_DATA event)
##
## Sizing Data:
## RULE clientssl_issue
## +-> CLIENT_ACCEPTED   200 total   0 fail   0 abort
## |   |     Cycles (min, avg, max) = (0, 0, 0)
## +-> CLIENT_DATA   200 total   0 fail   0 abort
## |   |     Cycles (min, avg, max) = (439752, 491878, 3673016)
## +-> HTTP_REQUEST   200 total   0 fail   0 abort
##     |     Cycles (min, avg, max) = (0, 0, 0)
##
##  NOTES: Test case: httperf --server=x.x.x.x --port=443 --uri=/ --ssl --ssl-no-reuse --num-conn=200 --rate=5

That’s incredibly useful going into a scenario where you have to troubleshoot or validate upgrades or patches to infrastructure or applications.  Whereas some of that information would have to be manually generated, some of it can be auto-generated. 

Automating iRule Header Information via pyControl

Looking back to the pyControl iRule Timing script I started in 2x5 Minute iRules on Timing, what if I turned the timing on/off from the script, and removed the old header (if present) and inserted a new one after testing?  The first changes to the script is to add a section of code to download the iRule (via the query_rule iControl method) from the LTM and pull out any existing header information.  I also am using a counter for the number of performance trials.  After yanking this information from the iRule, I add timing and then re-bundle the iRule and send it back to the LTM (via the modify_rule method.)

rule_contents = rl.query_rule(rule_names = [irule])
brl = rule_contents[0].rule_definition.split('\n')
rl_ver = 1
for x in brl:
    if x[:18] == '#hdr_info: Trials:':
        rl_ver = int(x.split(' ')[2].encode('ascii')) + 1
brl = filter (lambda a: a[:10] != '#hdr_info:', brl)
brl.insert(0, u'timing on')
brl = '\n'.join(brl)
rule_contents[0].rule_definition = brl
rl.modify_rule(rules = rule_contents)

Note the " u'timing on' " insert.  The list items in the iRule object are all unicode.  Rather than converting to ascii to work with them, I just left the objects as unicode, so each insert utilizes the unicode designator in python.  Load is then sent as before in the script and then after the stats have been calculated and displayed to console, I package the header information in a list that I then add to the existing iRule after removing timing.

rule_contents = rl.query_rule(rule_names = [irule])
brl = rule_contents[0].rule_definition.split('\n')
brl.remove(u'timing on')
irules_header = []
irules_header.append(u'#hdr_info: ### END ###')
irules_header.append(u'#hdr_info: CPU Util / request: %s' % (z*100.0/tmm_cpu_cycles))
irules_header.append(u'#hdr_info: Total Avg Cycles: %s' % z)
irules_header.append(u'#hdr_info: Target: %s' % url)
irules_header.append(u'#hdr_info: Cores: %s, Speed: %s' % (cpu_cores, cpu_num))
irules_header.append(u'#hdr_info: Platform: %s' % platform_ID)
irules_header.append(u'#hdr_info: Time: %s' % time.asctime(time.localtime(time.time())))
irules_header.append(u'#hdr_info: Trials: %s' % rl_ver)
irules_header.append(u'#hdr_info: Name: %s' % irule)
irules_header.append(u'#hdr_info: ### BEGIN ###')
for x in irules_header:
    brl.insert(0, x)
brl = '\n'.join(brl)
rule_contents[0].rule_definition = brl
rl.modify_rule(rules = rule_contents)

Finally, I pull the complete rule back down from the LTM and display to console:

rule_contents = rl.query_rule(rule_names = [irule])
print "\nFinal iRule:\n"
print rule_contents[0].rule_definition

The result of this with the full script is:

jrahm@ubuntu:~/scripts$ python2.6 is2.py 172.16.99.5 admin w3c http://172.16.100.99 200

Please enter your password:
Password:

Resetting statistics for the w3c iRule

Enabling timing and removing header information

Making 200 requests to host http://172.16.100.99

### iRule w3c Performance Results
        BIG-IP LTM Z100 has 2454400000 availabe CPU Cycles (1 cores, 3068.000 MHz)
        Average Cycles/sec:     665391
        CPU Utilization/req:    0.0271101287484 percent
        Max Requests/sec:       3688

Inserting Header in iRule and disabling timing

Final iRule:

#hdr_info: ### BEGIN ###
#hdr_info: Name: w3c
#hdr_info: Trials: 4
#hdr_info: Time: Wed Mar  9 09:16:21 2011
#hdr_info: Platform: Z100
#hdr_info: Cores: 1, Speed: 3068.000
#hdr_info: Target:
http://172.16.100.99
#hdr_info: Total Avg Cycles: 665391
#hdr_info: CPU Util / request: 0.0271101287484
#hdr_info: ### END ###
when HTTP_REQUEST {
   # < CONDENSED FOR CLARITY
}

Parting Thoughts

If you wanted to incorporate some of the ideas from above that wouldn’t be fit for auto-generation (like rule concept and workflow), you could place that information immediately before your first event with a different pattern than “#hdr_info:” since the script will remove any line matching that pattern.  The full script is in the iControl codeshare under pycontrol irule timing and comment header insert.

What other ideas do you have for rule documentation?  Post them in the comments section below.