Rapid iRule Removal via Tmsh Script

The BIG-IP GUI is pretty slick and there have been massive improvements in function and efficiency since my first exposure in version 4.2.  As good as it is, however, some tasks are just better suited to the CLI.  Take maintenance, for example.  How long will it take you to remove an iRule from a virtual server?  A quick attempt from login to removal was about 14 seconds.  While already logged in, it took me about nine seconds.  So assuming you know exactly what virtuals an iRule needs to be removed from, and that you can stay on top of which you’ve pulled from without duplicating effort, you can see how efficiency flies right out the window if you have 20, or 30, or maybe 300 virtuals to touch, right?

Enter scripting.  This scenario is why scripts exist.  It takes a mundane manual task and automates it, taking not only less time, but removing the human error element as well (assuming you don’t design the errors into your script!)  In this tech tip, I’m going to look at using tmsh scripting to evaluate all the virtual servers on the BIG-IP and if an iRule called “x” is present, remove it. 

Why remove iRules?  They Rock, right?

Well, yes, they do!  But, there are differently purposed iRules deployed.  Some are critical to site functionality—remove them, and the site is down hard.  Other rules are present for logging, d

ebugging, injecting content, etc.  These aren’t necessary, and during times of troubleshooting or resource concerns, could be removed.  As indicated above, this is trivial from a standpoint of procedure, but it’s the time involved that counts.  So scripting it is.  The problem now is what kind of script to go with?  The options are a shell script, a perl script, a tmsh script, or an iControl script.  The first two are certainly possible, but must utilize raw BIG-IP configuration data and then parse it into objects for manipulation.  For the latter two, the data is already presented as objects, so the complexity of the script is reduced dramatically.  iControl gets the stage all the time, so I chose tmsh for this job.

 

A Little Planning Goes a Long Way

Sometimes I get ahead of myself and start scripting immediately.  This is more fun, but always results in rewriting something as I’ve certainly made logic errors and assumptions that don’t pan out.  So let’s start with the high level approach, and then work our way into code.

Consider this virtual server configuration:

Virtual Server Configuration

 

ltm virtual tmshtest10 {
    destination 10.10.20.159:http
    ip-protocol tcp
    mask 255.255.255.255
    pool att_rtr
    profiles {
        http { }
        stream { }
        tcp { }
    }
    rules {
        filler1
        filler3
        filler4
        filler5
    }
}

 

Each of these keywords and values can be accessed in tmsh as an object.  I don’t really care about any of the keywords in a virtual except rules, since it’s only a specific iRule I want to remove.  So that’s the first test in the script.  After collecting all the vips into a variable, I want to loop through that list and test for the presence of that keyword.  If it is not defined, I can move on to the next virtual.  Once I’ve verified the keyword is present, however, I next need to see if the iRule I want to remove is defined within the rules keyword.  If it is, then I want to take action on this virtual server.  Since there is no remove action associated with iRules, I need to test how many iRules are currently applied.  If the iRule I want to remove is the only one, I can just modify the virtual server with the rules none attributes.  However, if there are more than one, I need to “pop” the specific rule from the list, then re-apply the iRules that will remain.  Finally, I’ll wrap all the looping and modification into a transaction, so if there are any failures in the script the desired actions will not be committed.  If you are a visual learner, the approach I’ve just described is presented in a workflow diagram in Figure 1.

The Code

Every tmsh script requires the script::run procedure.  I start with a conditional to make sure the rule name has been supplied as an argument (the only argument).  I then store the rule name and the virtual server configurations in a couple variables, and set a couple other variables for use in the loop.  The first two conditionals in the for loop test for presence of rules and then the specific rule.  If either conditional fails, that instance of the loop is terminated and the loop moves on to the next virtual.  I originally had a little more “logic” in there on both conditionals, but a second set of eyes from Mark Crosland, the author of tmsh, cleaned it up significantly, taking advantage of the zero return from tmsh::get_field_value in the absence of the keyword.  The third conditional evaluates the number of rules present.  If only one, the script utilizes the none attribute for rules.  If more than that, the rule must be removed from the list, which is done with lreplace.  The lappend action is just for printing the actions the script took on the active configuration.

 

tmsh script

 

proc script::run {} {
    if { $tmsh::argc != 2 } {
        puts "A single rule name must be provided"
        exit
    }
    set rulename [lindex $tmsh::argv 1]
    set rules ""
    set vips [tmsh::get_config /ltm virtual]
    set vips_in_play ""

    tmsh::begin_transaction

    foreach vip $vips {
        if { [tmsh::get_field_value $vip "rules" rules] == 0 } {
            continue
        }
        if { [lsearch -exact $rules $rulename] == -1 } {
            continue
        }
        if { [llength $rules] < 2 } {
            tmsh::modify /ltm virtual [tmsh::get_name $vip] rules none
            lappend vips_in_play $vip
        } else {
            set id [lsearch -exact $rules $rulename]
            set keepers [lreplace $rules $id $id]
            tmsh::modify /ltm virtual [tmsh::get_name $vip] rules "{ $keepers }"
            lappend vips_in_play $vip
        }
    }

    tmsh::commit_transaction

    puts "The $rulename iRule was removed from the following virtuals: "
    foreach x $vips_in_play {
        puts "\t[tmsh::get_name $x]"
    }
}

 

The Demo

For a quick walkthrough of the process involved in removing an iRule, and a demonstration of the script, check out the video below, which is also viewable in the Monitoring & Management section of the Tutorials Page.  The script above is posted in the tmsh wiki.

Published May 14, 2010
Version 1.0
  • Ariel_Santa_Cr1's avatar
    Ariel_Santa_Cr1
    Historic F5 Account
    Hi Jason,

     

    I was trying to modify this iRule to achive the same objective but in every partition. I've been with this script the whole day and get to the approach below. The only problem I'm having is that the lsearch is not matching the rule name with the rules list, may be due to the complete name is with the partition name. Also tried to pass the complete path as /Common/rule_name, and other test to remove the -exact option to the lsearch command, non of this worked.

     

     

    What am I missing?

     

     

    Commented iRule.

     

     

     

     

    proc script::init {} {

     

    }

     

     

    proc script::run {} {

     

    if { $tmsh::argc != 2 } {

     

    puts "A single rule name must be provided"

     

    exit

     

    }

     

    set rulename [lindex $tmsh::argv 1]

     

    set rules ""

     

     

    read the partitions list and store it in a variable

     

    set partitions [tmsh::get_config /auth partition]

     

     

    set vips_in_play ""

     

     

    foreach partition $partitions {

     

    changing the active partition

     

    tmsh::cd "/[tmsh::get_name $partition]"

     

     

    reading the virtual servers list for the active partition

     

    set vips [tmsh::get_config /ltm virtual]

     

     

    begining the transaction here as the get_config function can't be invoked within a transaction

     

     

    tmsh::begin_transaction

     

     

    foreach vip $vips {

     

    if { [tmsh::get_field_value $vip "rules" rules] == 0 } {

     

    continue

     

    }

     

     

    here's the problem I found, this lsearch returns always -1 even when the rulename is in the rules list. To test it I removed the -exact option but still not working.

     

    if { [lsearch $rules $rulename] == -1 } {

     

    continue

     

    }

     

    puts "Pase por aqui: $vip"

     

    if { [llength $rules] < 2 } {

     

    tmsh::modify /ltm virtual [tmsh::get_name $vip] rules none

     

    lappend vips_in_play $vip

     

    } else {

     

    set id [lsearch -exact $rules $rulename]

     

    set keepers [lreplace $rules $id $id]

     

    tmsh::modify /ltm virtual [tmsh::get_name $vip] rules "{ $keepers }"

     

    lappend vips_in_play $vip

     

    }

     

    }

     

    Commiting the transaction within this foreach to be able to run the get_config for the next partition.

     

    tmsh::commit_transaction

     

    }

     

    puts "The $rulename iRule was removed from the following virtuals: "

     

    foreach x $vips_in_play {

     

    puts "\t[tmsh::get_name $x]"

     

    }

     

    }

     

     

    Any help will be much appreciated.

     

     

    Ariel
  • add logging of both what you are passing as an argument, and the list ($rules) before the condition. The stdout should be logged to /var/tmp/scriptd.out.
  • Ariel_Santa_Cr1's avatar
    Ariel_Santa_Cr1
    Historic F5 Account
    Hi Jason,

     

    Thanks for the quick answer.

     

    I did some troubleshooting on this but I'm not much of developer so I might be missing something very basic for you.

     

    All the logging I did was through the PUTS command so I can see it right in the screen after the script execution.

     

    Added many logs as flags to understand where the flow was going through. Logged the variables $rules and $rulename before the IF. Even logged the complete function [lsearch -exact $rules $rulename] to know the value the function returns. It always returns a -1.

     

     

    These are the values of the parameters before the IF and LSEARCH execution:

     

    $rules: {rules /Common/_sys_https_redirect}

     

    $rulname: /Common/_sys_https_redirect

     

     

    Do you see any reason of why the LSEARCH function will not find the string within the rules? Note that I have also tried removing the -exact option to the LSEARCH.

     

     

    Thanks!

     

    Ariel
  • i would think it would not treat the list parameters as text, but you can try to see if taking the rule name itself from the list contents helps:

     

     

    lsearch [string trimright [lindex [split $rules " "] 1] "}"] $rulename

     

  • Ariel_Santa_Cr1's avatar
    Ariel_Santa_Cr1
    Historic F5 Account
    Should I escape the last "}" ?

     

    It's closing the IF open brackets eventhough it's between double quotes.

     

     

    And one question arise, why does it work in your script? I think I'm doing the same thing here with that variable. The only difference I see is that in your example the rules didn't include the partition name (full path).

     

  • shoot me the script in its entirety in email at j dot rahm at f5 dot com and I'll take a look on a test box with partitions. I haven't dealt with them much so I'm not sure where the gotchas are.
  • This is great for removal however how about adding? Would someone have an example script to add a new irule to all VIP's. We run into the issue where as we dont have or use http profiles on all our VIP's thus the irule we are adding would / should skip those VIP's if it needs additional profiles. I tried the below and get the error this VIP needs http profile. tmsh modify /ltm virtual all rules { add-all-irule-2 } 01070394:3: HTTP_REQUEST event in rule (/Common/add-all-irule-2) requires an associated HTTP or FASTHTTP profile on the virtual server (/Common/TestMGMT)
  • Hi,

     

    perfect article. It works very well. But it affects all VIP's. Is possible to implement it only on some VIP's (for example 10 from 100)?

     

    Thank you