#!/usr/bin/ruby

require 'yaml'

class HardwareRAID


  #
  #  The controllers we've discovered, if any.
  #
  attr_reader :controllers

  #
  #  Constructor
  #
  def initialize
    @controllers = nil
  end


  #
  #  Is this hardware present on this host?
  #
  def present?
    result = false

    cmd_output('lspci -v').each do |line|
      if (line =~ /RAID/i) &&
          (line =~ /3ware/i)
        result = true
      end
    end

    result
  end

  #
  #  Run a command and capture output.
  #
  def cmd_output(cmd)
    verbose("Running: #{cmd}")
    IO.popen(cmd, 'r') { |pipe| pipe.readlines }
  end

  #
  #  Find the controllers on this machine, if any.
  #
  def find_controllers
    found = []

    cmd_output('/usr/sbin/tw_cli show').each do |line|
      found.push(Regexp.last_match(1).dup) if line =~ /^(c\d+)/
    end

    found
  end

  #
  #  Check the status of the drives
  #
  def drives?
    h = {}

    @controllers = find_controllers if @controllers.nil?

    @controllers.each do |c|
      cmd_output("/usr/sbin/tw_cli /#{c} show").each do |line|
        if line =~ /^p([0-9]+)\s+([^\s]+)/
          h["p#{Regexp.last_match(1)}"] = Regexp.last_match(2).dup
        end
      end
    end

    h
  end

  #
  # Check the status of the array(s)
  #
  def arrays?
    h = {}

    @controllers = find_controllers if @controllers.nil?

    @controllers.each do |c|
      cmd_output("/usr/sbin/tw_cli /#{c} show").each do |line|
        if line =~ /^u([0-9]+)\s+([^\s]+)\s+([^\s]+)/
          h["u#{Regexp.last_match(1)}"] = Regexp.last_match(3).dup
        end
      end
    end

    h
  end

  #
  #  Get the current state of the system - used for the detail if
  # any alert is raised.
  #
  def state
    txt = ''

    @controllers = find_controllers if @controllers.nil?

    @controllers.each do |c|
      cmd_output("/usr/sbin/tw_cli /#{c} show").each do |line|
        txt += line
        txt += "\n"
      end
    end
    txt
  end
end

if __FILE__ == $PROGRAM_NAME

  def verbose(str)
    STDERR.puts(str)
  end

  #
  #  The alerts we'll raise, if any.
  #
  to_raise = []

  detail = ''

  #
  #  Look for errors.
  #
  status = HardwareRAID.new

  #
  #  Check if this is present?
  #
  unless status.present?
    verbose('Software/Hardware not present')
    puts YAML.dump(to_raise)
    exit(0)
  end

  #
  #  Check the status of the drives
  #
  drives = status.drives?
  drives.each_pair do |key, val|
    h = {}
    h[:id] = "drive-#{key}"
    h[:summary] = "Drive status (#{key}) is #{val}"

    if (val != 'OK')
      verbose "Drive #{key} status is #{val} -- raising alert"

      if detail.empty?
        detail =  status.state
      end
      h[:detail]  = detail

      to_raise.push(h)
    else
      verbose "Drive #{key} status is #{val} -- not sending alert"
    end
  end

  #
  #  Check the status of the arrays.
  #
  arrays = status.arrays?
  arrays.each_pair do |key, val|
    h = {}
    h[:id] = "raid-#{key}"
    h[:summary] = "RAID array (#{key}) is #{val}"

    if (val != 'OK')
      if detail.empty?
        detail =  status.state
      end
      h[:detail]  = detail
      verbose "RAID array #{key} status is #{val} -- raising alert"
      to_raise.push(h)
    else
      verbose "RAID array #{key} status is #{val} -- not sending alert"
    end
  end

  #
  #  Show the output.
  #
  puts YAML.dump(to_raise)
  exit(0)

end
