Check Your Services with Ruby

written by edavis on April 27th, 2006 @ 04:12 PM

I just got my Pickaxe v2 book in the mail today and have already put it to good use writing a script to check that the services for a host are up and responding. It uses many of the libraries included with Ruby but also another library ‘net-ping’ which you can get with a gem install net-ping. To use it all you have to do is to call the script with one to four parameers:

  • First is the host you want to check, i.e. theadmin.org
  • Second is the username to login to for FTP, i.e. user-bob (this will default to guest is not defined)
  • Third is the password to login to for FTP, i.e. bobpassword (this will default to guest is not defined)
  • Fourth is where to place the logfile, i.e. /home/bob/server.log (this will default to /tmp/server_up.log)

Here is a link to the code, and here is it for viewing:

#!/usr/bin/env ruby
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
# Program to check that certin services are running on a host
#
# Copyright (c) 2006 Eric Davis 
# Released under the MIT License 
# Details: http://dropbox.theadmin.org/bin/server_up/LICENSE.txt
#
# Last Revision: [2006/04/27]
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #


require 'rubygems'
require 'net/ping'
require 'net/http'
require 'net/smtp'
require 'net/ftp'
require 'time'

def log_error(error)
  File.open(@log_file, 'a') do |log|
    log.puts Time.now.rfc2822.to_s + ": " + error
  end
end


def check_ping(host)
  if not Net::PingExternal.new(host).ping
    log_error("Error: #{host} is not responding to a ping")
  end
end


def check_mail(host, port=25, ehlo=host)
  begin
    Net::SMTP.start(host, port, ehlo) do |smtp|
      log_error("Mail Server down") unless smtp.started?
    end
  rescue SocketError => socket_error
    log_error("Error Connecting To SMTP: #{socket_error}")
  rescue TimeoutError => timeout_error
    log_error("SMTP connection timed out: #{timeout_error}")
  end    
end


def check_web(host, file='/')
  begin
    Net::HTTP.start(host) do |http|
      response = http.get(file)
      if not response.code.match(/200/)
        log_error("Web Server down or not resonding: #{response.code} #{response.message}")
      end
    end
  rescue SocketError => socket_error
    log_error("Error Connecting To Web: #{socket_error}")
  rescue TimeoutError => timeout_error
    log_error("Web connection timed out: #{timeout_error}")
  end    

end


def check_ftp(host, login, password)
  begin
    Net::FTP.open(host) do |ftp|
      log_error("FTP Server down or not responding #{ftp.last_response_code}") unless  ftp.last_response_code.match(/220/)
      if password.empty?
        ftp.login(login)
      else
        ftp.login(login, password)
      end
    end
  rescue Net::FTPPermError => login_error
    log_error("FTP Server not allowing logins for '#{login}' using '#{password}': #{login_error}")
  rescue SocketError => socket_error
    log_error("Error Connecting To FTP: #{socket_error}")
  rescue TimeoutError => timeout_error
    log_error("FTP connection timed out: #{timeout_error}")

  end
end


ftp_user = ARGV[1] || 'guest'
ftp_password = ARGV[2] || 'guest'


@log_file = ARGV[3] || '/tmp/server_up.log'

check_ping(ARGV[0])
check_mail(ARGV[0])
check_web(ARGV[0])
check_ftp(ARGV[0], ftp_user, ftp_password)

Eric Davis

Comments

  • Stephen Touset on May 07, 2006 @ 06:54 PM

    Aieee! It's pretty clear you're just starting Ruby--you've ignored one of its most powerful features (blocks), and duplicated a ton of code in the process. Instead of having a ton of places where you rescue Socket/Timeout errors and present a generic message, do something like this:
    def check_service(type)
      begin
        log_error("#{type} server down or not responding") unless yield
      rescue SocketError => socket_error
        log_error("Error connecting to #{type}: #{socket_error}")
      rescue TimeoutError => timeout_error
        log_error("#{type} connection timed out: #{timeout_error}")
      end
    end
    
    def check_mail(host, port=25, ehlo=host)
      check_service("SMTP") do
        Net::SMTP.start(host, port, ehlo) do |smtp|
          return smtp.started?
        end
      end    
    end
    
    This is just a rough idea, but I'm sure you get the picture.
  • Stephen Touset on May 07, 2006 @ 06:55 PM

    Seems that my pretty Ruby formatting was trashed :\ Ah well, copy/paste it into a text editor and you should be able to recreate it in its original form. Let me know if you end up implementing my suggestion--it would be interesting to hear about.
  • Eric Davis on May 08, 2006 @ 02:52 AM

    Yep, Ruby is still new to me (this is one of the larger scripts I have written that is actually useful). That definitely makes cleaner, I will have to try it out. I also planned to add some unit tests to it also. (I also adjusted your formatting so it should come out correct now)

Post a comment