Hi AlexS,
let me give a brief demo what happens behind the scene and how to deal with variable:
Lets assume this...
when RULE_INIT {
log local0.debug "-- RULE_INIT --"
set variable_test_1 abc
log local0.debug "Exec Level: [info level]"
log local0.debug "Visible VAR: [info vars variable_test_*]"
}
when HTTP_REQUEST {
log local0.debug "-- HTTP_REQUEST (before PROC) --"
set variable_test_2 abc
log local0.debug "Exec Level: [info level]"
log local0.debug "Visible VAR: [info vars variable_test_*]"
call TESTPROC
log local0.debug "-- HTTP_REQUEST (after PROC) --"
log local0.debug "Exec Level: [info level]"
log local0.debug "Visible VAR: [info vars variable_test_*]"
}
proc TESTPROC { } {
log local0.debug "-- TEST PROC --"
set variable_test_3 abc
log local0.debug "Exec Level: [info level]"
log local0.debug "Visible VAR: [info vars variable_test_*]"
}
Directly after saving the iRule the RULE_INIT event gets triggered and creates you these log lines...
Dec 5 11:34:10 kw-f5-dev.itacs.de debug tmm1[11166]: Rule /Common/TestRule <RULE_INIT>: -- RULE_INIT --
Dec 5 11:34:10 kw-f5-dev.itacs.de debug tmm1[11166]: Rule /Common/TestRule <RULE_INIT>: Exec Level: 0
Dec 5 11:34:10 kw-f5-dev.itacs.de debug tmm1[11166]: Rule /Common/TestRule <RULE_INIT>: Visible VAR: variable_test_1
Dec 5 11:34:10 kw-f5-dev.itacs.de debug tmm[11166]: Rule /Common/TestRule <RULE_INIT>: -- RULE_INIT --
Dec 5 11:34:10 kw-f5-dev.itacs.de debug tmm[11166]: Rule /Common/TestRule <RULE_INIT>: Exec Level: 0
Dec 5 11:34:10 kw-f5-dev.itacs.de debug tmm[11166]: Rule /Common/TestRule <RULE_INIT>: Visible VAR: variable_test_1
Note: I'm using 2 CPU Cores in my Dev Unit, so each of my CPUs running their own TMM process (tmm and tmm1) which will execute the TCL script. Thats why we see 2 times the log...
When I HTTP request to the virtual server the HTTP_REQUEST will become triggered, which calls the PROC. The HTTP_REQUEST and PROC will both write additional log lines...
Dec 5 11:43:55 kw-f5-dev.itacs.de debug tmm[11166]: Rule /Common/TestRule <HTTP_REQUEST>: -- HTTP_REQUEST (before PROC) --
Dec 5 11:43:55 kw-f5-dev.itacs.de debug tmm[11166]: Rule /Common/TestRule <HTTP_REQUEST>: Exec Level: 2
Dec 5 11:43:55 kw-f5-dev.itacs.de debug tmm[11166]: Rule /Common/TestRule <HTTP_REQUEST>: Visible VAR: variable_test_2
Dec 5 11:43:55 kw-f5-dev.itacs.de debug tmm[11166]: Rule /Common/TestRule <HTTP_REQUEST>: -- TEST PROC --
Dec 5 11:43:55 kw-f5-dev.itacs.de debug tmm[11166]: Rule /Common/TestRule <HTTP_REQUEST>: Exec Level: 3
Dec 5 11:43:55 kw-f5-dev.itacs.de debug tmm[11166]: Rule /Common/TestRule <HTTP_REQUEST>: Visible VAR: variable_test_3
Dec 5 11:43:55 kw-f5-dev.itacs.de debug tmm[11166]: Rule /Common/TestRule <HTTP_REQUEST>: -- HTTP_REQUEST (after PROC) --
Dec 5 11:43:55 kw-f5-dev.itacs.de debug tmm[11166]: Rule /Common/TestRule <HTTP_REQUEST>: Exec Level: 2
Dec 5 11:43:55 kw-f5-dev.itacs.de debug tmm[11166]: Rule /Common/TestRule <HTTP_REQUEST>: Visible VAR: variable_test_2
Whats happening in the example above?
First of all try to think multidimensional!
We may have lots of CMP-enabled CPUs and each is executing an independent TCL environment. We have global stuff in each independent TCL environment. We have lots of per TCP/UDP connection related stuff going on. And then we have finally individual stuff happening within a PROC executions.
RULE_INIT scripts are getting executed in the highest TCL execution level (ID=0) and are used to define global stuff which affects all iRules and all Connections processed by your individual Virtual Servers. The purpose of this event is to define static stuff which may become readable by any child execution levels.
HTTP_REQUEST scripts are getting executed in the TCL execution level (ID=2) of the underlying TCP connection over which the request was send. Whatever you define here, is only affecting the individual TCP connection.
PROC script are getting executed in a child TCL execution level (ID=MotherLVL+1) of the calling TCL execution level. Whatever you define here, is only affecting the individual PROC itself.
How can I cross communicate between the TCL execution levels and TCL environments?
You can define some global stuff in a static::* variable during RULE_INIT. Remember that this code is getting executed once everytime you "Save" your iRule on the System.
when RULE_INIT {
set static::optionA "abc"
}
A HTTP_REQUEST event may read the static::* variable during its event execution.
when HTTP_REQUEST {
if { $static::optionA equals "abc" } then {
# Do some fancing things...
}
}
Note: Dont change those static::* varibales from events other than RULE_INIT, use them as Read-Only variables. Please simply dont do it... unless you know the impact of doing so... 😉
A HTTP_REQUEST event may call a PROC and pass arguments to it and the PROC may output a return value which can be stored in a variable.
when HTTP_REQUEST {
set my_variable "123"
set proc_return_value [call my_proc $my_variable]
if { $proc_return_value == "12345" } then {
# Do some fancing things...
}
}
proc my_proc { proc_input_variable } {
return "${proc_input_variable}45"
}
A slightly more advanced method to pass varibales between a PROC and its calling execution level would be to use [upvar] to map a varibale within a PROC to a varibale in the calling execution level.
when HTTP_REQUEST {
set x "Hello"
set y "World"
call build_string
log local0.debug $z
}
proc build_string { } {
upvar x local_x
upvar y local_y
upvar z local_z
set local_z "$local_x $local_y"
}
Note: Mapping many varibales between destinct TCL execution levels is annoying and also adds lots of CPU overhead. Use PROCs only if you get advantages by doing so and not because you are getting used to it from other coding languages. I for an example never use procs in TCL because they are almost always better (more flexible, faster, simpler, etc.) alternatives than using PROCs...
The last method I'd like to show you is how you could cross communicate between independent Connection and even TCL environments running on different CPUs by using the [table] command.
when HTTP_REQUEST {
set counter_value [table incr -mustexist "Count_[IP::client_addr]"]
if { $counter_value eq "" } then {
# Counter not available. Initialize a new table entry with timeout of 10 seconds.
set counter_value 1
table set "Count_[IP::client_addr]" $counter_value 10 indef
}
log local0.debug "Client [IP::client_addr] has requested $counter_value time in the last 10 seconds."
}
Note: The above example will work, even if the consecutive HTTP request where send acrross different TCP connection and processed by different CPUs.
Good source for the command in this POST:
Native TCL Command
F5 specific iRule Commands
HTH and Cheers, Kai