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

Was this article helpful?

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