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

Twitter

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]

Advertisement