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.

IP Matching Data Profile iApp

Problem this snippet solves:

In an of itself it is not a true iApp in that it does not configure anything on the BIG-IP but it allows for configuration reuse.

Code :

cli admin-partitions {
    update-partition Common
}
cli script f5.meta_utils {
proc md5sum { text } {
    #calcualte the md5 of text
    return [exec /bin/echo -n ${text} | /usr/bin/openssl dgst -md5]
}
proc b64en { str } {
    #Found at http://wiki.tcl.tk/775
    binary scan ${str} B* bits
    switch [expr {[string length ${bits}]%6}] {
        0 {set tail ""}
        2 {append bits 0000; set tail ==}
        4 {append bits 00; set tail =}
    }
    return [string map {
        000000 A 000001 B 000010 C 000011 D 000100 E 000101 F
        000110 G 000111 H 001000 I 001001 J 001010 K 001011 L
        001100 M 001101 N 001110 O 001111 P 010000 Q 010001 R
        010010 S 010011 T 010100 U 010101 V 010110 W 010111 X
        011000 Y 011001 Z 011010 a 011011 b 011100 c 011101 d
        011110 e 011111 f 100000 g 100001 h 100010 i 100011 j
        100100 k 100101 l 100110 m 100111 n 101000 o 101001 p
        101010 q 101011 r 101100 s 101101 t 101110 u 101111 v
        110000 w 110001 x 110010 y 110011 z 110100 0 110101 1
        110110 2 110111 3 111000 4 111001 5 111010 6 111011 7
        111100 8 111101 9 111110 + 111111 /
    } ${bits}]${tail}
}
proc b64de { str } {
   #Found at http://wiki.tcl.tk/775
   set tail [expr [string length ${str}] - [string length [string trimright ${str} =]]]
   set str [string trimright ${str} =]
   set bits [string map {
     A 000000 B 000001 C 000010 D 000011 E 000100 F 000101
     G 000110 H 000111 I 001000 J 001001 K 001010 L 001011
     M 001100 N 001101 O 001110 P 001111 Q 010000 R 010001
     S 010010 T 010011 U 010100 V 010101 W 010110 X 010111
     Y 011000 Z 011001 a 011010 b 011011 c 011100 d 011101
     e 011110 f 011111 g 100000 h 100001 i 100010 j 100011
     k 100100 l 100101 m 100110 n 100111 o 101000 p 101001
     q 101010 r 101011 s 101100 t 101101 u 101110 v 101111
     w 110000 x 110001 y 110010 z 110011 0 110100 1 110101
     2 110110 3 110111 4 111000 5 111001 6 111010 7 111011
     8 111100 9 111101 + 111110 / 111111
   } ${str}]
   set bits [string range ${bits} 0 end-[expr ${tail}*2]]
   set bytes [binary format B* ${bits}]
   return ${bytes} 
}
proc set_service_description_meta { service key value } {
    #create a key/value entry in a service's metadata, if it does exist update it - wrapper to set_description_meta
    return [set_description_meta "sys application service ${service}" ${key} ${value}]
}
proc delete_service_meta { service key } {
    #delete a services' metadata key/value based on a key - wrapper to delete_meta
    return [delete_meta "sys application service ${service}" ${key}]
}
proc get_service_description_meta { service } {
    #get service metadata - a wrapper for get_description_meta
    return [get_description_meta "sys application service ${service}"]
}
proc get_service_description { service } {
    #get service object description - just a wrapper for get_description
    tmsh::cd /Common
    return [get_description "sys application service ${service}"]
}
proc update_service_meta { service key value } {
    #update a services metadata - a wrapper for update_meta
    return [update_meta "sys application service ${service}" ${key} ${value}]
}
proc get_service_meta_value { service key } {
    return [get_meta_value "sys application service ${service}" ${key}]
}
proc get_description { object } {
    #return the description field, if there is nothing init the metadata info
    tmsh::cd /Common
    set desc [lindex [lindex [lindex [tmsh::get_config ${object} description] 0] [llength ${object}]] 1]
    if {[string match ${desc} "none"]} {
        tmsh::modify ${object} description \"Metadata \{[b64en "\{none foo\} [md5sum "\{none foo\}"]"]\}\"
        set desc "Metadata \{[b64en "\{none foo\} [md5sum "\{none foo\}"]"]\}"
    } 
    return ${desc}        
}
proc get_meta_value { object key } {
    set meta [get_description_meta ${object}]
    set index [get_meta_index ${object} ${key}]
    return [lindex ${meta} ${index}]
}
proc get_description_meta { object } {
    #get full metadata of the object returns a list of key/value pairs
    set desc [get_description ${object} ] 
    #init results
    set result "none"
    #regexp for metadata
    if {[regexp {^.*Metadata\s{(.*)}$} ${desc} all r]} {
        set result [b64de ${r}] 
        if {[string match [md5sum [lrange ${result} 0 end-1]] [lindex ${result} end]]} {
            set result [lrange ${result} 0 end-1]
            return ${result} 
        } else {
            return "failed"
        }
    }
}
proc get_meta_index { object key } {
    #get the index of a specific key in an objects metadata, used for delete/update
    set meta [get_description_meta ${object}]
    set count 0
    set found "-1"
    foreach item ${meta} {
        if {[string match [lindex ${item} 0] ${key}]} {
            set found ${count} 
        }
        incr count
    }
    return ${found}
}
proc update_meta { object key value } {
    #update a key with a value in the metatdata for an object returns the final description
    set index [get_meta_index ${object} ${key}]
    set meta [get_description_meta ${object}]
    set desc_only [lrange [get_description ${object}] 0 end-2]
    set count 0
    set buff  ""
    foreach item ${meta} {
        if {[expr ${count} != ${index}]} {
            lappend buff ${item}
        } else {
            lappend buff "${key} ${value}"
        }
        incr count
    }
    set final "${desc_only} Metadata \{[b64en "${buff} [md5sum ${buff}]"]\}"
    tmsh::modify ${object} description \"${final}\"
    return ${final} 
}
proc delete_meta { object key } {
    #delete a metadata key/value based on a key
    set index [get_meta_index ${object} ${key}]
    #desc_only excludes Metadata keyword
    set desc_only [lrange [get_description ${object}] 0 end-2]
    #init count and buff
    set count 0
    set buff  ""
    #look for each metadata item ... looking for the one to NOT append
    foreach item [get_description_meta ${object}] {
        if {[expr ${count} != ${index}]} {
            lappend buff ${item}
        }
        incr count
    }
    set final "${desc_only} Metadata \{[b64en "${buff} [md5sum ${buff}]"]\}"
    tmsh::modify ${object} description \"${final}\"
    return ${final} 
}
proc set_description_meta { object key value} {
    #create a key/value entry in an objects metadata, if it does exist update it
    set found 0
    set description [get_description ${object}]
    set metadata [get_description_meta ${object}]
    foreach s ${metadata} {
        if {[string match [lindex $s 0] ${key}]} {
            set found 1
        }
    }
    #if metadata not found in metadata
    if { ${found} eq 0 } {
        #didn't find key match, there is a description, and there is metadata in that description 
        puts "desc: ${description} [llength ${description}]"
        if {[expr [llength ${description}] > 2]} {
            #There is more than just metadata - grab none metadata
            set final [lrange ${description} 0 end-2]
            #append the key value to the end of the metadata
            lappend metadata "${key} ${value}"
            set final "${final} Metadata \{[b64en "${metadata} [md5sum ${metadata}]"]\}"
        } else {
            #there is ONLY metadata in this description
            lappend metadata "${key} ${value}"
            set final "Metadata \{[b64en "${metadata} [md5sum ${metadata}]"]\}"
        }
        tmsh::modify ${object} description \"${final}\"
        return ${final}
    } else {
        update_meta ${object} ${key} ${value}
    }
}
}
cli script f5.ntr_utils_devcentral {
proc import_service { service name } {
    tmsh::include "f5.meta_utils"
    tmsh::cd /Common
    set fullname ${tmsh::app_name}.app/${tmsh::app_name}
    set cmd "\"tmsh::modify sys application service $fullname execute-action definition\""
    set_service_description_meta ${service} ${fullname} ${cmd}
    set objs [lindex [tmsh::get_config sys application service ${service} one-line] 0]
    namespace eval ${name} {
        proc service_var { objs pre flag } {
            set name [namespace current]
            set parent_obj $pre
            set count 0
            foreach obj $objs {
                 if { ([expr ${count} % 2]) and ($flag eq 0)} {
                    variable $pre_obj $obj

                 } elseif { ([expr ${count} % 2]) and ($flag > 0)} {
                    variable ${parent_obj}__${pre_obj} $obj
                 }
                 if {([llength $obj] > 1) && ($pre_obj ne "column-names") && ($pre_obj ne "rows") } {
                    incr flag
                    ${name}::service_var $obj $pre_obj $flag
                    incr flag -1
                 }
                 set pre_obj $obj
                 incr count
            }
        }
        proc import_tables { tables } {
            set name [namespace current]
            set table_count 0
            foreach table $tables {
                set result ""
                # Only process the even elements
                if {![expr $table_count % 2]} {
                    set row_count 0
                    foreach row [set ${name}::${table}__rows] {
                        set column_count 0
                        foreach column [set ${name}::${table}__column-names] {
                            #puts "Set ${table}(${column}__${row_count}): [lindex [lindex $row 1] $column_count]"
                            lappend result ${column}__${row_count}
                            lappend result [lindex [lindex $row 1] $column_count]
                            lappend result ${column},${row_count}
                            lappend result [lindex [lindex $row 1] $column_count]
                            incr column_count
                        }
                        incr row_count
                    }
                    variable ${table}
                    array set ${table} ${result}
                }
                incr table_count
            }
        }
        proc import_variables { vars } {
            set var_count 0
            foreach var $vars {
                if {![expr $var_count % 2]} {
                    set varname $var
                } else {
                    variable ${varname} [lindex $var 1]
                }
                incr var_count
            }
        }
    }
    ${name}::service_var [lindex $objs 4] "" 0
    ${name}::import_tables [set ${name}::tables]
    ${name}::import_variables [set ${name}::variables]
}
proc service_callback { } {
    tmsh::include "f5.meta_utils"
    set name $tmsh::app_name
    set fullname ${name}.app/${name}
    tmsh::cd /Common
    catch { foreach service [get_service_description_meta $fullname] {
        tmsh::cd /Common
        eval [lindex ${service} 1]
    }
    }
}
proc import_table { table_local type } {
    set row_count 0
    array set row_result {}
    array set col_result {}
    array set rowcol_result {}
    foreach row ${table_local} {
        set element_count 0
        set names ""
        foreach element [split $row "\n"] {
            if { ($element_count > 0) && ($element_count < [expr [llength [split $row "\n"]] - 1])} {
                if { [llength $element] > 1 } {
                    set value "[lindex $element 1]"
                    set column "[lindex $element 0]"
                    lappend ary_result "${column}__${row_count}" 
                    lappend ary_result "${value}" 
                    lappend comma_result "${column},${row_count}" 
                    lappend comma_result "${value}" 
                    lappend row_result($row_count) ${value}
                    lappend col_result($column) ${value}
                    lappend rowcol_result($row_count) "[list $column ${value}]"
                    lappend names $column
                }
            }
            incr element_count
        }
        incr row_count
    }
    switch $type {
        row 
            {
                foreach row [array names row_result] {
                    lappend result $row
                    lappend result $row_result($row)
                }
                set final_result ${result}
            }
        col
            {
                foreach col [array names col_result] {
                    lappend result $col
                    lappend result $col_result($col)
                }
                set final_result ${result}
            }
        names
            {
                set final_result ${name}s
            }
        rowcol
            {
                foreach row [array names rowcol_result] {
                    lappend result $row
                    lappend result $rowcol_result($row)
                }
                set final_result ${result}
            }
        comma
            {
                set final_result $comma_result
            }
        ary
            {
                set final_result $ary_result
            }
        default
            {
                set final_result $ary_result
            }
    }
    return ${final_result}
}
proc get_items_regexp { field_name field args } {
    set results ""
    puts "args: $args"
    append args " recursive"
    set folder [tmsh::pwd]
    if { [catch {
        set objs [tmsh::get_config $args]
        foreach obj $objs {
            set name [tmsh::get_name $obj]
            puts "name: ${name}" 
            puts "obj: $objs"
            set field_value [tmsh::get_field_value $obj $field_name]
            puts "field_value: $field_value"
            puts "field: $field"
            if { [regexp "$field" $field_value] } {
                append results "$folder/"
                append results [format "%s\n" ${name}]
            }
        }

        if { $folder != "/Common" } {
            tmsh::cd "/Common"
            append objs [tmsh::get_config $args]
            tmsh::cd $folder
            foreach obj $objs {
                set name [tmsh::get_name $obj]
                set field_value [tmsh::get_field_value $obj $field_name]
                if { [regexp "$field" $field_value] } {
                    append results "/Common/"
                    append results [format "%s\n" ${name}]
                }
            }
        }
    } err] } {
        puts "Command failed: tmsh::get_config $args\n  $err"
        return ${results}
    }

    return ${results}
}
}
sys application template /Common/f5.ip_match_profile {
    actions {
        definition {
            html-help {
                
            }
            implementation {
                tmsh::include "f5.ntr_utils_devcentral"
                if { [string match $basic__callback "Yes"]} { 
                    puts "calling back!"
                    tmsh::run_proc f5.ntr_utils_devcentral:service_callback
                }
            }
            presentation {
                include "/Common/f5.apl_common"
                section basic {
                    table ips {
                        string network
                        string netmask
                        noyes invert
                    }
                    noyes callback
                    #optional ( callback == "Yes" ) {
                    #    multichoice callbacks display "xlarge" tcl { tmsh::run_proc f5.ntr_utils:get_items_meta_regexp { "sys application service $tmsh::app_name.app/$tmsh::app_name" ".*app.*" } } 
                    #}
                }
                text {
                    basic "IP Matching table"
                    basic.ips "List of IP address"
                    basic.ips.network "Network:"
                    basic.ips.netmask "Netmask:"
                    basic.ips.invert "Excetptions"
                    basic.callback "Do you want to redeploy iApps that depend on this iApp?"
                }
            }
            role-acl none
            run-as none
        }
    }
    description none
    type failover
}
Published Mar 11, 2015
Version 1.0
No CommentsBe the first to comment