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