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.0Michael_Earnhar
Historic F5 Account
Joined October 30, 2008
Michael_Earnhar
Historic F5 Account
Joined October 30, 2008
No CommentsBe the first to comment