Programming with Ruby
Documentation
Regenerate all ri documentation
gem rdoc --all --ri --no-rdoc
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]