Sort objects by busy-ness
Problem this snippet solves:
Here's a request we've had for a long time: sort objects by busy-ness. Since there was never a consensus of what was meant by busy or how that information was to be presented, product development gave us an API to create our own custom views. So here's a cool way to watch objects (vips/snats/nats) sorted by busiest. You can sort on each of the parameters (ex. CPS, max conns, tot throughput, etc.). It requires you to also include the "misc_procedures" script as well.
NOTE: This was created to help quickly find the busiest objects on the box. At one point, we were troubleshooting why TMM CPU was so busy and bigtop only showed VIPS. Turned out traffic was traversing a snat instead. Sorting/grepping/sed/awking through bigpipe/tmsh output for all the objects was cumbersome.
ex.
y=`b virtual show`; for i in `echo "$y" | egrep '^ \| \(cur' | awk '{print $8}' | egrep -o '0-9+' | sort -rn | head`; do echo "$y" | egrep -B 2 "^ \| \(cur, max, limit, tot\) = \(0-9+, $i"; done
y=`b snat show`; for i in `echo "$y" | egrep '^\| \(cur' | awk '{print $8}' | egrep -o '0-9+' | sort -rn | head`; do echo "$y" | egrep -B 1 "^\| \(cur, max, limit, tot\) = \(0-9+, $i"; done
Yuck!
DEFAULTS (without any arguments):
- Shows all object types (vips/snats/nats)
- Sorts by Current Connections
- Samples every 10s (as there can be lots of objects which can put a bit of load on mcpd).
- Auto Formats Output
- Shows all records (simply put number on the end for how many objects you would like to see)
See "Usage example below"
RFEs:
I know, I know. There is always room for improvement. Some have already thought about are:
- Be able to only show field specified (instead of ALL the fields) to prevent wrap around on narrow terminals. Will require complete overhaul but need time to noodle on it.
- Add object sub-type (ex. tcp-vip vs. fastL4 vip)
This is version 1.0 so have a few things would like to clean up but basic functionality is there. Getting it to Devcentral asap to leverage the incredible brain trust on here:)
Sample Output:
root@bigip(Active)(tmos.cli.script)# run watch_by_busy ? usage: watch_by_busy (virtual|snat|nat|all) default = all (object-name|object-type|cps|cur-conns|max-conns|tot-conns| bits-in|bits-out|tot-bits-in|tot-bits-out| pkts-in|pkts-out|tot-pkts-in|tot-pkts-out) default = cur-conns (gig|meg|kil|raw|auto) default = auto number of records to display default = all records ex. watch_by_busy watch_by_busy virtual max-conns auto 10 root@bigip(Active)(tmos.cli.script)# run watch_by_busy all bits-out gig max-conns object-name pkts-out tot-bits-in tot-pkts-in auto cps help meg object-type raw tot-bits-out tot-pkts-out bits-in cur-conns kil nat pkts-in snat tot-conns virtual root@bigip(Active)(tmos.cli.script)# run watch_by_busy virtual max-conns 15 Gathering stats over interval 10 secs. Please be patient. Object Oject Conns Conns Conns Conns Bits Bits Tot-Bits Tot-Bits Pkts Pkts Tot-Pkts Tot-Pkts Name Type CPS Cur Max Tot In Out In Out In Out In Out ------------------------- -------- ------- ------- ------- ------- ------- ------- ------- -------- ------- ------- ------- ------- out virtual 2 5 128 23.47M 0 0 65.11G 2455.13G 0 0 103.49M 208.55M ps_http virtual 0 0 115 36.28K 0 0 8.57G 666.21G 0 0 26.57M 55.64M ps_tftp virtual 0 0 90 4.38K 0 0 562.10M 23.19G 0 0 2.19M 2.19M vpn-fwd-test virtual 0 2 29 6.14K 0 0 410.00M 1.35G 0 0 341.96K 381.49K b1600-1.es.f5net.com virtual 0 0 26 46.36K 0 0 587.07M 6.17G 0 0 548.34K 738.84K b3400-1.es.f5net.com virtual 0 0 26 43.09K 0 0 632.98M 6.21G 0 0 648.91K 782.99K b8800-2.es.f5net.com virtual 0 0 26 42.73K 0 0 625.30M 6.18G 0 0 641.43K 778.51K em500-2.es.f5net.com virtual 0 0 26 51.93K 0 0 762.37M 7.02G 0 0 779.38K 916.95K b3400-2.es.f5net.com virtual 0 0 25 43.46K 0 0 636.59M 6.21G 0 0 652.51K 785.42K b3400-3.es.f5net.com virtual 0 0 25 41.33K 0 0 755.48M 6.21G 0 0 607.76K 736.14K b3600-1.es.f5net.com virtual 0 0 25 53.14K 0 0 669.71M 6.45G 0 0 632.91K 826.27K b6900-1.es.f5net.com virtual 0 0 25 48.43K 0 0 612.60M 6.25G 0 0 570.71K 763.19K b8800-1.es.f5net.com virtual 0 1 25 237.84K 2.73K 3.18K 3.26G 9.30G 1 1 2.35M 1.92M b8900-3.es.f5net.com virtual 0 0 25 58.93K 0 0 860.26M 6.77G 0 0 740.68K 908.49K em500-1.es.f5net.com virtual 0 0 25 49.26K 0 0 910.83M 6.13G 0 0 777.24K 1.01M es_ldap virtual 3 0 15 3.03M 2.90K 1.79K 2.91G 1.79G 6 5 6.02M 4.99M
Code :
cli script watch_by_busy { proc script::init {} { #Version 1.0. Have a few things would like to clean up but basic functionality is there. set ::usage_string "usage: [lindex $tmsh::argv 0] \n \ \t\t\t\t (virtual|snat|nat|all) \n \ \t\t\t\t\t default = all \n\n \ \t\t \t (object-name|object-type|cps|cur-conns|max-conns|tot-conns| \n \ \t\t\t\t\t bits-in|bits-out|tot-bits-in|tot-bits-out| \n \ \t\t\t\t\t pkts-in|pkts-out|tot-pkts-in|tot-pkts-out) \n \ \t\t\t\t\t default = cur-conns \n\n \ \t\t \t\t (gig|meg|kil|raw|auto) \n \ \t\t\t\t\t default = auto \n\n \ \t\t \t number of records to display \n \ \t\t\t\t\t default = all records \n\n \ \t\t ex. [lindex $tmsh::argv 0] \n \ \t\t [lindex $tmsh::argv 0] virtual max-conns auto 10" set ::object_types { virtual snat nat all } set ::fields { object-name object-type cps cur-conns max-conns tot-conns\ bits-in bits-out tot-bits-in tot-bits-out\ pkts-in pkts-out tot-pkts-in tot-pkts-out } set ::formats { gig meg kil raw auto } set ::padding 2 set ::interval 10 } proc usage { } { puts $::usage_string exit } proc process_cmd_args_p { args_default } { upvar $args_default my_args foreach { arg } $tmsh::argv { if { [ lsearch -exact $::object_types $arg ] >= 0 } { set my_args(object_type) $arg } elseif { [ lsearch -exact $::fields $arg ] >= 0 } { set my_args(field) $arg set my_args(field_index) [ lsearch -exact $::fields $arg ] } elseif { [ lsearch -exact $::formats $arg ] >= 0 } { set my_args(format) $arg } elseif { [ regexp {\d+} $arg ] } { set my_args(num_display) $arg } elseif { $arg equals "help" } { usage } } } proc get_stats_p { results_l object_type } { upvar $results_l results if { $object_type equals "all" } { set object_types { virtual snat nat } } else { set object_types $object_type } foreach obj_type $object_types { set idx 0 set objs [tmsh::get_status / ltm $obj_type raw ] set count [llength $objs] while { $idx < $count } { set object_l {} set obj [lindex $objs $idx] if { $obj_type equals "virtual" or $obj_type equals "snat" } { lappend object_l [tmsh::get_name $obj] \ $obj_type \ [tmsh::get_field_value $obj "clientside.tot-conns"] \ [tmsh::get_field_value $obj "clientside.cur-conns"] \ [tmsh::get_field_value $obj "clientside.max-conns"] \ [tmsh::get_field_value $obj "clientside.tot-conns"] \ [tmsh::get_field_value $obj "clientside.bits-in"] \ [tmsh::get_field_value $obj "clientside.bits-out"] \ [tmsh::get_field_value $obj "clientside.bits-in"] \ [tmsh::get_field_value $obj "clientside.bits-out"] \ [tmsh::get_field_value $obj "clientside.pkts-in"] \ [tmsh::get_field_value $obj "clientside.pkts-out"] \ [tmsh::get_field_value $obj "clientside.pkts-in"] \ [tmsh::get_field_value $obj "clientside.pkts-out"] \ } elseif { $obj_type equals "nat" } { lappend object_l [tmsh::get_name $obj] \ $obj_type \ [tmsh::get_field_value $obj "serverside.tot-conns"] \ [tmsh::get_field_value $obj "serverside.cur-conns"] \ [tmsh::get_field_value $obj "serverside.max-conns"] \ [tmsh::get_field_value $obj "serverside.tot-conns"] \ [tmsh::get_field_value $obj "serverside.bits-in"] \ [tmsh::get_field_value $obj "serverside.bits-out"] \ [tmsh::get_field_value $obj "serverside.bits-in"] \ [tmsh::get_field_value $obj "serverside.bits-out"] \ [tmsh::get_field_value $obj "serverside.pkts-in"] \ [tmsh::get_field_value $obj "serverside.pkts-out"] \ [tmsh::get_field_value $obj "serverside.pkts-in"] \ [tmsh::get_field_value $obj "serverside.pkts-out"] \ } lappend results $object_l incr idx } } } proc script::run {} { tmsh::include misc_procedures #set defaults. Show all object types, sort by cur-conns = field_index 3, auto format and essentially display unlimited objects array set args { object_type all field cur-conns field_index 3 format auto num_display 10000 } if { $tmsh::argc > 5 } { usage } process_cmd_args_p args set fields1 { Object Oject Conns Conns Conns Conns Bits Bits Tot-Bits Tot-Bits Pkts Pkts Tot-Pkts Tot-Pkts } set fields2 { Name Type CPS Cur Max Tot In Out In Out In Out In Out } set field_lengths { 20 8 7 7 7 7 7 7 7 7 7 7 7 7 } set rate_fields { 2 6 7 10 11 } # result set tables set results_1_l {} set results_2_l {} set delay [expr $::interval * 1000] #first poll get_stats_p results_1_l $args(object_type) puts "Gathering stats over interval $::interval secs. Please be patient." while { true } { after $delay # second poll get_stats_p results_2_l $args(object_type) # list for deltas between results_1_l and results_2_l. using tcl lists as much more conducive to sorting set delta_l [ calculate_delta_list_p $results_1_l $results_2_l $rate_fields ] # create an array of formated values so can key off name. # however, in tcl, easier to pass arrays in list form first set formated_l [ create_formated_array_p $delta_l $args(format) field_lengths ] array set formated_a $formated_l tmsh::clear_screen #Print Headers puts [ format_line_p $fields1 $field_lengths ] puts [ format_line_p $fields2 $field_lengths ] puts [ format_spacer_p $fields2 $field_lengths ] #Now comes the sorting set tx 0 foreach object [ lsort -decreasing -real -index $args(field_index) $delta_l ] { if { $tx <= $args(num_display) } { #handle first field "object name" differently set obj_name [ lindex $object 0 ] puts [ format_line_p $formated_a($obj_name) $field_lengths ] incr tx } } # use the most recent results as the next previous results set results_1_l $results_2_l set results_2_l {} } } proc script::help {} { tmsh::add_help "$::usage_string" } proc script::tabc {} { lappend tab_types $::object_types $::fields $::formats help foreach tab_type $tab_types { foreach tab $tab_type { tmsh::add_tabc $tab } } } }