Forum Discussion

Chris_Phillips's avatar
Chris_Phillips
Icon for Nimbostratus rankNimbostratus
Aug 03, 2006

iRule as stored function or similar

Hi,

 

 

I have an iRule with a dozen lines or so and amd really hoping that there is some way to use this one single iRule as a stored function for instances of it. I mean, i will be using seperate instances of this iRule maybe 20 times, and would really like to be able to call the function with different parameters for each instance rather than having to copy all the code each time, e.g. "call my_iRule(param1, param2)".

 

 

I'm not that hopeful, but hoping to be amazed!

 

 

Thanks

 

 

Chris

10 Replies

  • Colin_Walker_12's avatar
    Colin_Walker_12
    Historic F5 Account
    There isn't currently a mechanism for calling iRules in a function-like manner as you described. The closest I've seen people come is to make the iRule read from a class to fill in the info specific to the Virtual it's assigned to, then create just that class of info for each Virtual.

     

     

    You'd still be copying and pasting the iRule around, but at least you wouldn't have a seperate version of the rule for each Virtual, just a seperate class.

     

     

    Colin
  • unRuleY_95363's avatar
    unRuleY_95363
    Historic F5 Account
    Actually, we've discussed this quite a bit in designing iRules.

     

     

    The work-around is to use variables to pass arguments into an iRule and get returned results and then use priority assignment and multiple iRule combinations to chain the rule snippets together on various virtual servers. I've yet to encounter a situation where this doesn't provide the ability to write one rule that does common functionality and use it in various ways.

     

     

    The decision to not have procedures was largely due to the impact on performance that making a procedure call has in Tcl.

     

    This may someday be worked around...

     

  • these sound very interesting, but do you have any examples? i generally undestand the class principle, presumably you'd need something identifiable like the virtual server address and/or port which would let you identify a single instance. As far as second suggestion, that's really gone way over my head... intersting sounding words but really wouldn't know where to start!

     

     

    Cheers

     

     

    Chris
  • Check out this post

     

     

    http://devcentral.f5.com/Default.aspx?tabid=28&view=topic&forumid=5&postid=8796 Click here
  • Deb_Allen_18's avatar
    Deb_Allen_18
    Historic F5 Account
    To re-use the same rule with different variable values for different virtuals without creating multiple instances, I've used this approach with good results:

     

     

    First set up a class. Each row will begin with the configured name of each virtual server to which the rule will be applied, and will also contain the value of the variables required by each:
    class cl_VSVars {
       vs_VirtualServer1 valueA valueB valueC
       vs_VirtualServer2 valueA valueB valueC
       vs_VirtualServer3 valueA valueB valueC
    }
    Then you can include this code in your rule to extract the variable values based on the virtual upon which the traffic is processed (use in any event but RULE_INIT):
    set myVSVars [findclass [virtual name] cl_VSVars]
    set myVar1 [getfield $myVSVars " " 1]
    set myVar2 [getfield $myVSVars " " 2]
    set myVar3 [getfield $myVSVars " " 3]
    If you want to store the class name in a variable, you can do so like this:
    when RULE_INIT {
      set ::variableClass ::VSVars
    }
    when CLIENT_ACCEPTED {
      set myVSVars [findclass [virtual name] [set $::variableClass]]
    ...
    }
    HTH

     

    /deb
  • Thanks guys, this all looks great.

     

     

    I'm left thinking about what impact this kind of complexity could have in terms of efficiency though. i guess i could write variables into a class on each connect event or suhc, but that would presumably be much less efficient that just writing directly to a local variable. I guess this is deviating from what i originally asked though, so i'll have a bash at the suggestions here.

     

     

    Thanks

     

     

    Chris
  • Hi,

    thanks for everything everyone's said here. I've updated my one single iRule to store data in an array using the virtual name as the array key:

      when RULE_INIT {
         number of times an ip address can be used between lookups
        set ::max_usage_count 3
      
         array of resolved ip addresses
        array set ::server_ipaddr { }
         array of resolved address uses
        array set ::usage_count { }
        log local0. "Completed RULE_INIT on lookup"
      }
      when CLIENT_ACCEPTED {
         initialize usage count if not yet done for this VS
        if { [ info exists ::usage_count([virtual name]) ] == 0 } {
          set ::usage_count([virtual name]) 0
        }
        
         use server_ipaddr if it exists, use dummy localhost if not
        if { [ info exists ::server_ipaddr([virtual name]) ] == 1 } {
          node $::server_ipaddr([virtual name]) [getfield [virtual name] "_" 3]
        } else {
           this WILL fail, but only occurs on very first usage when no IP address is known yet.
          node 127.0.0.1
        }
        
         decrement usage_count for this virtual server
        incr ::usage_count([virtual name]) -1
         call lookup if maximum usages has happened and reset usage counter
        if { $::usage_count([virtual name]) <= 0 } {
          set ::usage_count([virtual name]) $::max_usage_count
          NAME::lookup [getfield [virtual name] "_" 2]
        }
      }
      when NAME_RESOLVED {
         assign resolved name to server_ipaddr array
        if {[scan [NAME::response] "%d.%d.%d.%d" a b c d] == 4 } {
          set ::server_ipaddr([virtual name]) [NAME::response]
        }
      } 

    I realised that as i already wanted to put the destination server and port number in the virtual server name, i could just split on an underscore (e.g. vs_example.com_443) to get all the configuration details i needed. I can then store additional dunamic data in two global arrays as i go along.

    I'm very happy with the logic here, but wonder about the optimization of it all. I've read tips that advise against intermediate local variable names (otherwise i would have been tempted to copy [virtual name] to a local variable in the CLIENT_ACCEPTED event) but if anyone has other pointers to tweak the efficieny, every little helps. i'm doing a lot of repeated array lookups and getfields... i'm lead to believe that's the best approach if anything though.

    Cheers

    Chris
  • Remember too that you can test all your variations against the timing command to find the most efficient solution specific to your environment:

    Details on how to configure it:

    Posted By unRuleY on 3/22/2005 4:19 PM

    That is a very good point. You have obviously thought about this. Of course, it will all really depend on just how often you expect to match. If it does not match often, then you are completely correct. If it matches regularly, then you would likely want to save the result in a variable. Another factor to weigh is the number of elements in the class/datagroup.

    For those that are interested and paying attention, I'm now going to mention a YASF (yet another stealth feature):

    You can enable timing statistics in a rule which will allow you to see just how many cycles are spent evaluating a given rule event. The way you do this is with the "timing on" statement.

    An example that enables timing for all subsequent events in a rule is:

       
     rule my_fast_rule {   
        timing on   
        when HTTP_REQUEST {   
            Do some stuff   
        }   
     }   
     

    An example of only timing a specific event is:

       
     rule my_slow_rule {   
        when HTTP_REQUEST timing on {   
            Do some other stuff   
        }   
     }   
     

    This will then collect timing information each time the rule is evaluated and can be viewed with "b rule show all". You'll likely only want to look at the average and min numbers as max is often way, way out there due to the optimizations being performed on the first run of the rule. Additionally, enabling timing does have some overhead, though it should be negligible.

    Details on how to make sense of the numbers:

    Click here
  • Chris,

     

     

    I notice that you're setting the node to 127.0.0.1 if server_ipaddr doesn't exist. TMM actually listens on several ports for this loopback address (including the management GUI).

     

     

    You might want to pick another IP address that isn't used.

     

     

    Aaron
  • Yeah that would be fairly obvioous now wouldn't it? i only used that as that's what was added to my rule when it was converted to a wiki entry.

     

     

    cheers

     

     

    chris