For more information regarding the security incident at F5, the actions we are taking to address it, and our ongoing efforts to protect our customers, click here.

FQDN pool members, Least Connections Member SLB with Cookie Persistence in an iCall

Problem this snippet solves:

A lightweight SLB solution without LTM

Code :

sys icall script matt_dns {
    app-service none
    definition {
        #
# Get the DNS records from the server and place them in a sorted list
        set dns_raw [exec bash -c {dig +short matt.f5demo.com @10.128.20.252 A}]
        #set dns_raw "10.128.20.51\n10.128.20.52\n10.128.20.53\n10.128.20.54"
        set dns [split $dns_raw "\n"]
        set dns_sorted [lsort $dns]

# Read the prevous DNS result from file, sorted to ensure formating etc
        if { [catch { set dns_previous_file [open "/config/ifile/current_icall_dns" r] } err] } {
            set dns_old ""
        } else {
            set dns_old [read $dns_previous_file]
            close $dns_previous_file
            set dns_sorted_old [lsort $dns_old]
        }

# Writing the current DNS to a file
        set dns_previous_file [open "/config/ifile/current_icall_dns" w+]
        puts $dns_previous_file $dns_raw
        close $dns_previous_file

        set count 0
        foreach a $dns_sorted {
            set pn "pool_$a"
            if { [catch { tmsh::show /ltm pool $pn } err] } {

# Create Pools if they don't exist
                tmsh::log "Creating new pool for $a"
                tmsh::create /ltm pool $pn \{ members add \{ $a:80 \}\}
            } else {

# Enable the Pool member if it does exist (quicker to just enable it than to check if it is disabled) 
                tmsh::modify /ltm pool $pn \{ members modify \{ all \{ session user-enabled \} \} \}
            }
            lappend pn_list $pn
            incr count
        }

# Disable Pools no longer in use
        foreach b $dns_sorted_old {
            if {[lsearch -exact $dns_sorted $b]==-1} {
                tmsh::log "not in current dns list, disabling $b"
                set old_pn "pool_$b"
                tmsh::modify /ltm pool $old_pn \{ members modify \{ $b:http \{ session user-disabled \} \} \}
                # Not too keen to just staight delete these, my thoughts are to have another iCall running every say 20min looking for user-disabled pools (or could use something a particular monitor) and confirming no active sessions and then deleting
                #tmsh::delete /ltm pool $old_pn
            }
        }

# If just one pool create a persistence cookie but no SLB (need a persistence record for the case when another member is dynamically added)
        if { $count eq 1 } {
            set persist [lindex $pn_list 0]
            set Ratio_SLB "set selected $persist"
        } else {

# If more than one pool

# Work out Total Connections
            set total 0
            foreach a $pn_list {
                foreach obj [tmsh::get_status /ltm pool $a detail] {
                    set cc [tmsh::get_field_value $obj cur-sessions]
                    #set cc [expr double( { 100 * rand() })]
                    set total [expr double( { $total + $cc })]
                    lappend cc_list $cc
                }
            }

# Work out cumulative ratios for each pool
            set percentage 0
            incr count -1
            set j 0
            foreach a $pn_list {
                if { $total != 0.0 } { 
                    set percentage [expr double( { $percentage + ( $total - [lindex $cc_list $j] ) / ( $total * $count ) } ) ]
                } else {
                    set percentage [expr double( {$percentage + ( 1.0 / ( $count + 1.0 ) ) } ) ]
                }
                tmsh::log "$percentage $total $count [lindex $cc_list $j]"

# Construct the Dynamic Ratio SLB and persistence components of the iRule

# First Pool
                if { $j eq 0 } {
                    set Ratio_SLB "set Random_num \[expr \{ rand() \}\]
            if \{ \$Random_num < $percentage \} \{ 
                set selected $a
            \}"
                    set persist [concat $a " -" \n]

# Middle Pools, if there are any
                } elseif { $j < $count } {
                    set Ratio_tmp " elseif \{ \$Random_num < $percentage \} \{ 
                set selected $a 
            \}"
                    set Ratio_SLB [concat $Ratio_SLB $Ratio_tmp]
                    set persist [concat $persist $a " -" \n]

# Last Pool
                } else {
                    set Ratio_tmp " else \{ 
                set selected $a
            \}"
                    set Ratio_SLB [concat $Ratio_SLB $Ratio_tmp]
                    set persist [concat $persist $a]
                }
                incr j
            }
            #tmsh::log "Test $Ratio_SLB"
            #tmsh::log "$persist"

        }

# Construct the whole iRule

set iRuleName "iCall_CookiePersist_RatioSLB"
set iRuleCode "
when HTTP_REQUEST timing on \{

    switch \[HTTP::cookie pool_cookie\] \{
        $persist \{
        \#Persistence Record Exists
            pool \[HTTP::cookie pool_cookie\]
            set selected \"\"
        \}
        default \{
        \#Load balancing decision required
            $Ratio_SLB
            pool \$selected
        \}
    \}
\}


when HTTP_RESPONSE timing on \{

    if \{\$selected ne \"\"\} \{
    \#set a persistence cookie
        HTTP::cookie insert name pool_cookie value \$selected path \"/\"
    \}
\}
"

# Create the iRule

#    tmsh::log "$iRuleCode"

    if { [catch { tmsh::modify ltm rule $iRuleName $iRuleCode } err] } {
        tmsh::create ltm rule $iRuleName $iRuleCode
    }
}
    description none
    events none
}
Published Jun 22, 2016
Version 1.0

1 Comment

  • Hey team I have a slight non functional issue with iCalls in general. Interested in advice. So we deploy our f5s in Sync Groups that we manually sync (I don;t think this is unusual). With iCalls running on each device we end up with Changes Pending status in Red all the time (both devices changed), which is really painful - not keen to move to Auto Sync, advise pls