Generic per Object Metadata Library

Problem this snippet solves:

This library provides the methods for creating, updating, reading, and deleting per object metadata. It utilized the description field to store this data. It places the values in the form of: your description data Metadata {{ key1 value1} {key2 value2} md5_of_metadata}

note that all the metadata between the outer curly braces is base64 encoded to help keep hands off

How to use this snippet:

Example

root@sea-b-earnhart(Active)(/Common)(tmos)# list sys application service f5net.app/f5net description
sys application service f5net.app/f5net {
    description " Metadata {e25vbmUgZm9vfSB7ZG5zX3Rlc3QuYXBwL2Ruc190ZXN0ICJ0bXNoOjptb2RpZnkgc3lzIGFwcGxpY2F0aW9uIHNlcnZpY2UgZG5zX3Rlc3QuYXBwL2Ruc190ZXN0IGV4ZWN1dGUtYWN0aW9uIGRlZmluaXRpb24ifSB7cmVuYXRhLmFwcC9yZW5hdGEgInRtc2g6Om1vZGlmeSBzeXMgYXBwbGljYXRpb24gc2VydmljZSByZW5hdGEuYXBwL3JlbmF0YSBleGVjdXRlLWFjdGlvbiBkZWZpbml0aW9uIn0gYWQyMThjMjI0NzU4ZTBkYjRmNTVmODVkMmY1MGI0NDk=}"
}

if you pipe that through base64 -d you will see:

{none foo} {dns_test.app/dns_test "tmsh::modify sys application service dns_test.app/dns_test execute-action definition"} {renata.app/renata "tmsh::modify sys application service renata.app/renata execute-action definition"} ad218c224758e0db4f55f85d2f50b449

notice that it is a list of lists (the later of which are of the form {key value}), with the last element being an md5 hash of the entire list to help detect manual edits

Utility Procs

proc get_description { object }
proc md5sum { text }
proc b64en { str }
proc b64de { str }

Wrapper Procs for iApp service objects

proc set_service_description_meta { service key value }
proc delete_service_meta { service key }
proc get_service_description_meta { service } 
proc get_service_description { service }
proc update_service_meta { service key value }
proc get_service_meta_value { service key }

Primary Procs

proc get_meta_value { object key }
proc get_description_meta { object }
proc get_meta_index { object key }
proc update_meta { object key value }
proc delete_meta { object key }
proc set_description_meta { object key value}

Contributed by: m.earnhart

Code :

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}
    }
}

}
Published Mar 11, 2015
Version 1.0

Was this article helpful?

No CommentsBe the first to comment