Select pool member based on HTTP query string parameter
Problem this snippet solves:
The iRule allows clients to select a pool member based on a parameter set in the HTTP query string. The manual selection can be specified on any URI by appending member=1 to the query string. On responses, a session cookie is set to ensure the client requests are manually persisted to the same server as long as the browser is kept open. To have LTM clear the cookie, the member number can be set in the URI to 0.
For versions lower than 10.0, you could use a shell script run nightly to create a datagroup containing all of the pool members, regardless of state. This could be done on any LTM version. Then replace [active_members -list ..] with [lindex [lsort $::my_pool_members_class] $member_num] for 9.x or [lindex [lsort [class -get my_pool_members_class]] $member_num] for 10.x.
Code :
# # Select pool member based on HTTP query string parameter # v0.1 - 2010-03-15 - Aaron Hooley - hooleylists at gmail dot com # v0.2 - 2010-12-29 - Aaron Hooley - hooleylists at gmail dot com - used 'members' command added in 10.0 instead of active_members # # - Allow a client to select the pool member based on a parameter set in the query string # - Uses a digit to select the 0th, 1st, 2nd, etc pool member from the active members of a pool # # See below for an alternative method for statically mapping 1 to the first pool membrer regardless of state. # - Sets a session cookie so that the client will be persisted to the same pool member for the duration # that the browser is kept open. # - If the parameter is set to 0, then remove the cookie and load balance the request # # Requires 10.0+ to use members -list commnad # http://devcentral.f5.com/s/wiki/default.aspx/iRules/members # # For versions lower than 10.0 you could use a shell script run nightly to create a datagroup containing # all of the pool members. This could be done on any LTM version. # Then replace [members -list] with [lindex [lsort $::my_pool_members_class] $member_num] for 9.x # or [lindex [lsort [class -get my_pool_members_class]] $member_num] for 10.x # when HTTP_REQUEST { # Log debug to /var/log/ltm? 1=yes, 0=no. set member_debug 1 # Name of the URI parameter name used to manually select a specific pool member # Clients can append the member parameter to a query string in the format of: # www.example.com/index.html?member=2 # where member is the parameter name set member_param "member" # Track whether a member has been found in the query string or cookie set member_num "" # Debug logging if {$member_debug}{ log local0. "[IP::client_addr]:[TCP::client_port]: Debug enabled on [HTTP::method] request for [HTTP::host][HTTP::uri]" log local0. "[IP::client_addr]:[TCP::client_port]: Members for pool [LB::server pool] (sorted):\ [lsort [members -list [LB::server pool]]]" } # Check if query string contains "member=" before evaluating the value # Could replace this with a check of the URI that is only used when # manually selecting a specific pool member. # Also check for a previously set cookie indicating a manually selected pool member. if {[HTTP::query] contains $member_param or [HTTP::cookie $member_param] ne ""}{ # Have the query string parameter take precedence over the cookie. # So set the member_num based on the cookie and then overwrite it if the param value is set. set member_num [HTTP::cookie $member_param] if {$member_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Query contained the member parameter or member cookie was present.\ Parsed member cookie value: $member_num"} # Parse the value of the parameter to get the pool member number being selected # Use a workaround to handle a bug with URI::query, described in: # CR137465: http://devcentral.f5.com/s/Default.aspx?tabid=53&forumid=5&tpage=1&view=topic&postid=1168257#1145270 set query_member_num [URI::query "?&[HTTP::query]" "&${member_param}"] if {$query_member_num ne ""}{ set member_num $query_member_num if {$member_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Using member number from URI: \$member_num: $member_num"} } # Check member number value switch -glob $member_num { "0" { # Exact match for 0 (client wants to clear cookie) # Delete the cookie in the response # Save the cookie value so we can delete it in the response set cookie_val [HTTP::cookie $member_param] if {$member_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Member 0 specified, will remove cookie in response."} return } "[0-9]*" { # The parameter had a value starting with a digit, # Use lindex to get the nth -1 pool member "IP port" (lindex is 0 based) # Use scan to parse the IP and port to separate variables (the pool command doesn't seem to handle them together) # Use catch to handle any errors trying to parse the pool member if {[catch {scan [lindex [lsort [members -list [LB::server pool]]] [expr {$member_num - 1}]] {%[^ ] %d} ip port} result]}{ if {$member_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Error parsing pool member from $member_num"} } elseif {$result == 2}{ if {$member_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Parsed IP port: $ip $port"} # Use catch to handle any errors trying to select the pool member if {not [catch {pool [LB::server pool] member $ip $port}]}{ # Selecting pool member succeeded so exit this event of this iRule if {$member_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Successfully selected: $ip $port"} return } } } } # If we're still in the iRule there was no match or there were errors trying to select the pool member # so load balance the request pool [LB::server pool] unset member_num if {$member_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: No/invalid member_num parsed, load balancing request"} } } when HTTP_RESPONSE { # Check if $member_num is set and has a value if {[info exists member_num] and $member_num ne ""}{ switch $member_num { 0 { if {$cookie_val ne ""}{ # Expire the cookie by inserting the name/value with an expire time in the past if {$member_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Expiring cookie $member_param=$cookie_val"} HTTP::cookie insert name $member_param value $cookie_val path "/" HTTP::cookie expires $member_param 0 absolute } } default { if {$member_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Inserting cookie $member_param=$member_num"} HTTP::cookie insert name $member_param value $member_num path "/" } } } } # Debug events which can be removed once testing is complete when LB_SELECTED { if {$member_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Selected IP:port: [LB::server]"} } when SERVER_CONNECTED { if {$member_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Server IP:port: [IP::server_addr]:[TCP::server_port]"} }
- r_dynamo_79563NimbostratusHi Hoolio, what will the same iRule look like with debugging turned off?