Check Your Services with Ruby

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

3 comments

  1. Stephen Touset says:

    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.

  2. Stephen Touset says:

    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.

  3. Eric Davis says:

    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)

Comments are closed.