Programming Ruby
One Liners
Nagios
Generate a Nagios **hostgroup {}** entry from a DNS *-cluster record:
$> host www-cluster 192.168.0.1 192.168.0.2 ...
define hostgroup {
hostgroup_name www-cluster
alias web servers
members www1,www2,...,wwwN
}
Ruby code:
$> ruby -rresolv -e 'ips = Resolv.getaddresses "www1-cluster";
puts "define hostgroup {\n\thostgroup_name www1-servers\n\talias www servers\n";
print "\tmembers "; ips.each { |ip| print Resolv.getname(ip).split(/\./).first;
print "," unless ip == ips.last }; puts "\n}"'
If you have a list of clusters, then you can use this version to generate all:
$> cat ~/solaris-clusters | ruby -rresolv -ne 'cluster = $_.split(/-/).first;
ips = Resolv.getaddresses $_.chomp;
puts "define hostgroup {\n\thostgroup_name #{cluster}-servers\n\talias #{cluster} servers\n";
print "\tmembers "; ips.each { |ip| print Resolv.getname(ip).split(/\./).first; print "," unless ip == ips.last };
puts "\n}"'
Flip-Flops
Get a list of packages from a Solaris metacluster. This flip-flop evaluates to "true" when it finds **Cprog**, and it starts printing those lines. It will stop printing when it finds **END**. Very useful ;-)
cat /var/sadm/system/admin/.clustertoc|ruby -ne 'print $_ if $_=~/Cprog/..$_=~/^END/' > cprog_cluster.list
Useful Scripts
DNS
lshosts
#!/usr/bin/env ruby
# 2009-09-21
# avoid using Resolv.getaddresses as this has a recursion limit on Ruby 1.8
# Luis Mondesi <lemsx1@gmail.com>
#
# License: GPL
#
# TODO
# - sort result nicely
require "resolv"
ARGV.each { |arg|
hosts = `host #{arg}`
hosts.split(/\n/).each { |line|
ip = line.split(/\s+/).last.chomp
if ip =~ /^[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+$/
name = Resolv::getname(ip)
puts name.split(/\./).first
end
}
}
Useful for querying various hosts like:
lshosts debian-cluster|xargs -I {} ssh {} 'sudo apt-get update; sudo apt-get upgrade; sudo tripwire --check -I'
Solaris
IO, Network, CPU
Save this as a file named **diag**, make it executable and run!
#!/usr/bin/env ruby # Luis Mondesi <lemsx1@gmail.com> # # diagnostic - A script to take a quick, bird's eye view of a Solaris system # # License: GPL # # ChangeLog # 2009-09-23 18:21 EDT # - initial rev # 2009-09-24 11:51 EDT # - adds iostat plus other nifty stats # - adds overall averages # 2009-09-24 13:50 EDT # - multi-threaded # 2009-09-24 15:27 EDT # - adds mpstat # 2009-09-25 12:50 EDT # - adds tcp in order and unorder bytes # 2009-09-25 13:49 EDT # - makes output Human more human-friendly # 2009-09-25 16:24 EDT # - adds rate of IO per sec # 2009-09-25 16:35 EDT # - takes highest value of disk IO (not weighted avg) # 2009-09-28 13:48 EDT # - adds IO operations per sec # - formats CPU output better # - adds labels for %b and %w # 2009-09-28 17:36 EDT # - report information about the disk with highest # throughput only
# globals _interval = 5 # in sec _sample = 3
# DO NOT CHANGE FROM THIS LINE # require "thread" require "socket" hostname = Socket.gethostname
# interrupted is a real global var
@interrupted = false
trap("INT") { @interrupted = true }
# values managed by iostat thread: @_b = 0 @_w = 0 @_asvc_t = 0 @_throughput = 0 # kw/s + kr/s from iostat @_iops = 0 # r/s + w/s from iostat
@iomutex = Mutex.new @cpumutex = Mutex.new
# values managed by mpstat thread: @_wt = 0 @_syscl = 0 @_icsw = 0 @_csw = 0
# helpers
def _pad_float(precision,digit)
sprintf("%0.#{precision}f",digit)
end
def _pad_digit(precision,digit)
sprintf("%#{precision}d",digit)
end
def _pad_string(precision,str)
sprintf("%#{precision} ",str)
end
def iostat_avg(interval,sample)
#puts "*** calling iostat ***"
io = `iostat -nrx #{interval} #{sample}`.split(/\n/)
_w = 0
_b = 0
_asvc_t = 0.0
_throughput = 0 # keep highest throughput
_iops = 0 # keep highest number of IO oper/sec
# take highest values from all disks, if high
# throughput is found for 1 disk, we save the
# details as to why
menu = 0
io.each { |l|
exit if (@interrupted)
menu += 1 if l =~ /device/
next if menu < sample
tok = l.split(/,/)
_tp = tok[-8].to_i + tok[-9].to_i
if (_tp > _throughput)
_throughput = _tp
# save the rest of the details for this high-throughput
_iops = tok[0].to_i + tok[1].to_i
_b = tok[-2].to_i
_w = tok[-3].to_i
_asvc_t = tok[-4].to_f
end
}
# save to globals to be displayed later:
@iomutex.synchronize {
@_b = _b # highest percent busy (utilization)
@_w = _w # highest percent wait (saturation)
@_asvc_t = _asvc_t # highest service time
@_throughput = _throughput # highest throughput
@_iops = _iops # highest IO operations/sec
}
end
def mpstat_avg(interval,sample)
#puts "*** calling mpstat ***"
cpu = `mpstat #{interval} #{sample}`.split(/\n/)
_n = 0
_wt_sum = 0
_syscl_sum = 0
_csw_sum = 0
_icsw_sum = 0
menu = 0
# we average from all CPUs
cpu.each { |l|
exit if (@interrupted)
menu += 1 if l =~ /CPU/
next if menu < sample
tok = l.split(/\s+/)
_wt_sum += tok[-2].to_i
_syscl_sum += tok[-5].to_i
_icsw_sum += tok[-9].to_i
_csw_sum += tok[-10].to_i
_n += 1
}
# _n_div avoids ugly errors when CTRL-C is pressed
_n_div = (_n > 0) ? _n : 1
# save so they can be displayed later:
@cpumutex.synchronize {
@_wt = _wt_sum/_n_div
@_syscl = _syscl_sum/_n_div
@_icsw = _icsw_sum/_n_div
@_csw = _csw_sum/_n_div
}
end
puts "#{hostname} Sampling every #{_interval} seconds"
# threads to handle io and mpstat
io1 = Thread.new {
while not @interrupted
iostat_avg(_interval,_sample)
end
}
cpu1 = Thread.new {
while not @interrupted
mpstat_avg(_interval,_sample)
end
}
# helper vars _prev_bytes_out = 0 _prev_retrans = 0 _prev_in_unorder_bytes = 0 _prev_in_inorder_bytes = 0 _counter = 1
while not @interrupted
# TCP output and retransmissions:
netstat = `netstat -sP tcp|egrep '\(tcpRetransBytes\|tcpOutDataBytes\|tcpInInorderBytes\|tcpInUnorderBytes\)'`.split(/\t+/)
_in_unorder_bytes = netstat[-1].chomp.gsub(/[^0-9]/,"").to_i
_in_inorder_bytes = netstat[-3].chomp.gsub(/[^0-9]/,"").to_i
_retrans = netstat[-5].chomp.gsub(/[^0-9]/,"").to_i
_bytes_out = netstat[-7].chomp.gsub(/[^0-9]/,"").to_i
puts "#{_counter += 1}. diag on #{hostname} per second"
puts `uptime`
puts " Network:"
puts " TCP:"
inu = (_prev_in_unorder_bytes > 0) ? _in_unorder_bytes - _prev_in_unorder_bytes : 0
puts "\tIn (Unorder):\t#{_pad_digit(12,_in_unorder_bytes)} (#{_pad_float(6,inu.to_f * 8 / 1024 / 1024)} Mb/s)"
_prev_in_unorder_bytes = _in_unorder_bytes
ini = (_prev_in_inorder_bytes > 0) ? _in_inorder_bytes - _prev_in_inorder_bytes : 0
puts "\tIn (Inorder):\t#{_pad_digit(12,_in_inorder_bytes)} (#{_pad_float(6,ini.to_f * 8 / 1024 / 1024)} Mb/s)"
_prev_in_inorder_bytes = _in_inorder_bytes
out = (_prev_bytes_out > 0) ? _bytes_out - _prev_bytes_out : 0
puts "\tOut:\t\t#{_pad_digit(12,_bytes_out)} (#{_pad_float(6,out.to_f * 8 / 1024 / 1024)} Mb/s)"
_prev_bytes_out = _bytes_out
retr = (_prev_retrans > 0) ? _retrans - _prev_retrans : 0
puts "\tRetrans:\t#{_pad_digit(12,_retrans)} (#{_pad_float(6,retr.to_f * 8 / 1024 / 1024)} Mb/s)"
_prev_retrans = _retrans
@iomutex.synchronize {
puts " IO:"
puts "\tIOPS: \t\t\t#{@_iops}" # operations per second
puts "\tIO Rate: \t\t#{@_throughput} KB/s"
puts "\tUtilization (%b): \t#{@_b}"
puts "\tSaturation (%w): \t#{@_w}"
puts "\tasvc_t: \t\t#{@_asvc_t}"
}
@cpumutex.synchronize {
puts " CPU:"
puts "\twait time:\t#{_pad_digit(6,@_wt)}"
puts "\tsys calls:\t#{_pad_digit(6,@_syscl)}"
puts "\tinvol. cs:\t#{_pad_digit(6,@_icsw)}"
puts "\tvol. cs:\t#{_pad_digit(6,@_csw)}"
}
4.times { puts "" } # scroll up
# refresh screen every 1 sec
# Important: do not change this number without changing Mb/s above!
sleep 1
end
# we never reach here io1.exit cpu1.exit
Sample output
936. diag on db18 per second
1:55pm up 4:10, 4 users, load average: 0.23, 0.23, 0.28
Network:
TCP:
In (Unorder): 11 (0.000000 Mb/s)
In (Inorder): 46145761 (0.000000 Mb/s)
Out: 353855003 (0.010132 Mb/s)
Retrans: 7460 (0.000000 Mb/s)
IO:
IOPS: 176
IO Rate: 4700 KB/s
Utilization (%b): 100
Saturation (%w): 0
asvc_t: 20.7
CPU:
wait time: 0
sys calls: 27
invol. cs: 0
vol. cs: 17
A simple command line client for Twitter (written in Ruby)
http://lems.kiskeyix.org/toolbox/?f=twitter.rb
Guevos Movie Trivia
A script that gets one or more random movies from the top 250 list of thethe internet movie database (IMDB)
The script replaces part of the title with the string "guevos" (slang for Penis). The script skips single word movie titles to make things interesting.
http://lems.kiskeyix.org/toolbox/?f=rand_movie_title_guevos.rb
$> ./Projects/rand_movie_title_guevos.rb --count 3 Monsters, guevos [Monsters, Inc.] Little Miss guevos [Little Miss Sunshine] The Dark guevos [The Dark Knight]


