Skip to content

Instantly share code, notes, and snippets.

@nmische
Created June 7, 2012 16:07
Show Gist options
  • Save nmische/2889692 to your computer and use it in GitHub Desktop.
Save nmische/2889692 to your computer and use it in GitHub Desktop.
iRule to allow clients to select a pool member based on a parameter set in the HTTP query string.
# irule_select_pool_member
#
# v1.0 - 7 June 2012 - nmische
# Rewritten from v0.2 at: https://devcentral.f5.com/wiki/irules.Select_pool_member_based_on_HTTP_query_string_parameter.ashx
#
# Purpose:
# Allows wharton wired clients to select a pool member based on a parameter set in the HTTP query string.
#
# Notes:
# Only works with pools with up to 9 members. Uses event command to disable all events so may not be safe to use with other iRules.
#
when RULE_INIT {
# Log debug info? 0 = no, 1 = yes
set static::log_debug 0
# 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?ltm_pool_member=2
# where member is the parameter name
set static::member_param "ltm_pool_member"
}
when CLIENT_ACCEPTED priority 750 {
if { not [class match [IP::client_addr] equals datagroup_wharton_wired_subnets] }{
event disable all
}
}
when HTTP_REQUEST {
# Track whether a member has been found in the query string or cookie
set member_num ""
# Check if query string contains "ltm_pool_member=" before evaluating the value
# Also check for a previously set cookie indicating a manually selected pool member.
if { [HTTP::query] contains $static::member_param or [HTTP::cookie $static::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 cookie_val [HTTP::cookie $static::member_param]
set member_num $cookie_val
if { $static::log_debug }{ log local0. "[IP::client_addr]:[TCP::client_port]: Parsed member number from cookie: \$member_num: $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/Default.aspx?tabid=53&forumid=5&tpage=1&view=topic&postid=1168257#1145270
set query_val [URI::query "?&[HTTP::query]" "&${static::member_param}"]
if {$query_val ne ""}{
set member_num $query_val
if { $static::log_debug }{ log local0. "[IP::client_addr]:[TCP::client_port]: Using member number from URI: \$member_num: $member_num" }
}
# If we have a member number then try to select a pool member
if { $member_num ne "" }{
# Check member number value
switch -glob $member_num {
"0" {
# Exact match for 0 (client wants to clear cookie)
# Load balance the request and delete the cookie in the response
if { $static::log_debug }{ log local0. "[IP::client_addr]:[TCP::client_port]: Member 0 specified, will remove cookie in response." }
return
}
"[1-9]" {
# The parameter had a value between 1 and 9
# 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 { $static::log_debug }{ log local0. "[IP::client_addr]:[TCP::client_port]: Error parsing pool member from $member_num" }
} elseif { $result == 2 }{
if { $static::log_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 { $static::log_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
if { $static::log_debug }{ log local0. "[IP::client_addr]:[TCP::client_port]: No/invalid member_num parsed, load balancing request" }
event disable all
}
}
when HTTP_RESPONSE {
# Check if $member_num has a value
if { $member_num ne "" }{
if { $member_num eq 0 } {
# Expire the cookie by inserting the name/value with an expire time in the past
HTTP::cookie insert name $static::member_param value $cookie_val path "/"
HTTP::cookie expires $static::member_param 0 absolute
if { $static::log_debug }{ log local0. "[IP::client_addr]:[TCP::client_port]: Expiring cookie $static::member_param=$cookie_val" }
} elseif { $cookie_val ne $member_num }{
# Set a cookie with the member_num value
HTTP::cookie insert name $static::member_param value $member_num path "/"
if { $static::log_debug }{ log local0. "[IP::client_addr]:[TCP::client_port]: Inserting cookie $static::member_param=$member_num" }
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment