Monitoring iLO2 with Nagios

Posted by Kevin Way Wed, 05 Sep 2007 12:21:00 GMT

We have a whole bunch of HP servers, purchased in large part for their excellent Lights-Out management software. We wanted to have Nagios monitor some basic things, like the status of the fans, internal temperatures, power supplies, and VRMs.

Fortunately, that turned out to be pretty easy.

The first step was to download the HP Lights-Out XML Perl Scripting Sample for Linux. I did this even though I don’t use Linux as a platform. The resulting file contains a bunch of sample XML scripts for accomplishing various goals, and a perl script (locfg.pl) that submits them to an iLO2 processor.

I then used locfg.pl to submit the following XML to each of our servers, in order to create a user with no substantial privileges.


<RIBCL VERSION="2.0">
  <LOGIN USER_LOGIN="adminuser" PASSWORD="adminpass">
  <USER_INFO MODE="write">
    <ADD_USER 
      USER_NAME="Nagios Monitor" 
      USER_LOGIN="nagiosuser" 
      PASSWORD="nagiospass">
      <ADMIN_PRIV value ="N"/>
      <REMOTE_CONS_PRIV value ="N"/>
      <RESET_SERVER_PRIV value ="N"/>
      <VIRTUAL_MEDIA_PRIV value ="N"/>
      <CONFIG_ILO_PRIV value="N"/>
    </ADD_USER>
  </USER_INFO>
  </LOGIN>
</RIBCL>

This script allowed me to quickly create unprivileged users on each of the iLO2 consoles.

Armed with an unprivileged user, I set about writing the actual plugin, and ended up with this:


#!/usr/bin/env ruby
require 'optparse'
require 'socket'
require 'openssl'
require 'rexml/document'

# Command Line Options
options = {
  :server => nil
}

opts = OptionParser.new do |opt|
  opt.banner = "Usage: #{$0}  [options]" 
  opt.on('-s', '--server HOSTNAME', String, "Hostname or IP of the server to query") { |i| options[:server] = i }
end
opts.parse!(ARGV)

if not options[:server]
  $stderr.puts "Server must be specified" 
  exit!
end

# iLO XML
xml_start = <<EOF
<RIBCL VERSION="2.22">
  <LOGIN USER_LOGIN="nagiosuser" PASSWORD="nagiospass">
EOF

xml_end = <<EOF
  </LOGIN>
</RIBCL>
EOF

xml_emhealth = <<EOF
<SERVER_INFO MODE="read">
  <GET_EMBEDDED_HEALTH />
</SERVER_INFO>
EOF

error_cnt = 0
error_msg = ''
error_summary = ''

s = TCPsocket.open(options[:server], 443)
ssl = OpenSSL::SSL::SSLSocket.new(s, OpenSSL::SSL::SSLContext.new)
ssl.sync
ssl.connect
ssl.write("<?xml version=\"1.0\"?>\r\n")
ssl.write(xml_start)
ssl.write(xml_emhealth)
ssl.write(xml_end)
ssl.flush
res = ssl.readlines

ssl.close
s.close

doc = REXML::Document.new(res.to_s.match(/<GET_EMBEDDED_HEALTH_DATA>.*<\/GET_EMBEDDED_HEALTH_DATA>/m).to_s)

if ! doc.elements["GET_EMBEDDED_HEALTH_DATA"]
  error_cnt += 1
  error_msg += "Unable to fetch embedded health data\n" 
end

doc.root.elements["FANS"].each_element('//FAN') { |mod|
  if mod.elements["STATUS"].attributes['VALUE'] != 'Ok'
    error_cnt += 1
    error_msg += "#{mod.elements['LABEL'].attributes['VALUE']} - #{mod.elements['ZONE'].attributes['VALUE']} - #{mod.elements['STATUS'].attributes['VALUE']}\n" 
    error_summary += "#{mod.elements['LABEL'].attributes['VALUE']}." 
  end
}

doc.root.elements["TEMPERATURE"].each_element('//TEMP') { |mod|
  if mod.elements["STATUS"].attributes['VALUE'] != 'Ok' and mod.elements["STATUS"].attributes['VALUE'] != 'n/a'
    error_cnt += 1
    error_msg += "#{mod.elements['LABEL'].attributes['VALUE']} - #{mod.elements['LOCATION'].attributes['VALUE']} - #{mod.elements['STATUS'].attributes['VALUE']} - #{mod.elements['CURRENTREADING'].attributes['VALUE']} #{mod.elements['CURRENTREADING'].attributes['UNIT']} (Caution/Critical: #{mod.elements['CAUTION'].attributes['VALUE']}/#{mod.elements['CRITICAL'].attributes['VALUE']})\n" 
    error_summary += "#{mod.elements['LABEL'].attributes['VALUE']}." 
  end
}

doc.root.elements["VRM"].each_element('//MODULE') { |mod|
  if mod.elements["STATUS"].attributes['VALUE'] != 'Ok'
    error_cnt += 1
    error_msg += "#{mod.elements['LABEL'].attributes['VALUE']} - #{mod.elements['STATUS'].attributes['VALUE']}\n" 
    error_summary += "#{mod.elements['LABEL'].attributes['VALUE']}." 
  end
}

doc.root.elements["POWER_SUPPLIES"].each_element('//SUPPLY') { |mod|
  if mod.elements["STATUS"].attributes['VALUE'] != 'Ok'
    error_cnt += 1
    error_msg += "#{mod.elements['LABEL'].attributes['VALUE']} - #{mod.elements['STATUS'].attributes['VALUE']}\n" 
    error_summary += "#{mod.elements['LABEL'].attributes['VALUE']}." 
  end
}

if error_cnt == 0
  puts "OK: 0 problems" 
  rc=0
else
  puts "Critical: #{error_cnt} problems. #{error_summary}" 
  puts error_msg
  rc=2
end

exit rc

Now having that in place, I simply added all of the iLO2 hosts to a hostgroup, and added a service to check that group using the script, and all my fans, power supplies and such are now monitored without any operating-system level overhead.

QS, AppleScript and moving windows

Posted by Kevin Way Mon, 20 Aug 2007 06:53:00 GMT

A 15” laptop has a relatively small display, so I find myself routinely wanting to put a window all the way to one edge or the other, to help maximize the use of this.

I decided that this should be simple, something along the lines of ^⌥⌘← to slam a window to the left edge of my screen and so on.

There were a few hitches, but I did it as follows:

First I made some scripts:

move_left

tell application "Finder" 
    set {disp_x1, disp_y1, disp_x2, disp_y2} to bounds of window of desktop
end tell

tell application "System Events" 
    set frontmostApplication to name of the first process whose frontmost is true

    tell process frontmostApplication
        tell window 1
            set {x1, y1} to position
            set position to {disp_x1, y1}
        end tell
    end tell
end tell

move_right

tell application "Finder" 
    set {disp_x1, disp_y1, disp_x2, disp_y2} to bounds of window of desktop
end tell

tell application "System Events" 
    set frontmostApplication to name of the first process whose frontmost is true

    tell process frontmostApplication
        tell window 1
            set {x1, y1} to position
            set {x_off, y_off} to size

            set position to {disp_x2 - x_off, y1}
        end tell
    end tell
end tell

move up

tell application "Finder" 
    set {disp_x1, disp_y1, disp_x2, disp_y2} to bounds of window of desktop
end tell

tell application "System Events" 
    set frontmostApplication to name of the first process whose frontmost is true

    set disp_y1 to disp_y1 + 22

    tell process frontmostApplication
        tell window 1
            set {x1, y1} to position

            set position to {x1, disp_y1}
        end tell
    end tell
end tell

move down

tell application "Finder" 
    set {disp_x1, disp_y1, disp_x2, disp_y2} to bounds of window of desktop
end tell

tell application "System Events" 
    set frontmostApplication to name of the first process whose frontmost is true

    tell process frontmostApplication
        tell window 1
            set {x1, y1} to position
            set {x_off, y_off} to size

            set position to {x1, disp_y2 - y_off}
        end tell
    end tell
end tell

And at the suggestion of Alex L.

move center

tell application "Finder" 
    set {disp_x1, disp_y1, disp_x2, disp_y2} to bounds of window of desktop
end tell

tell application "System Events" 
    set frontmostApplication to name of the first process whose frontmost is true

    set disp_y1 to disp_y1 + 22

    tell process frontmostApplication
        tell window 1
            set {x1, y1} to position
            set {x_off, y_off} to size

            set position to {disp_x1 + (disp_x2 - x_off) / 2, disp_y1 + (disp_y2 - y_off) / 2}
        end tell
    end tell
end tell

I added these to QS hotkeys, and used the same technique to make windows do incremental moves as well.

Credit to the MacNN Forums, who helped me find a useful answer in this thread.

Follow-up: Zachary Cohen wrote a correction: Seems like a cool little article, but you need to make sure that Access for assistive devices is turned on. You do this by going to “System Preferences” then selecting the “Universal Access” from the options. Then click the click the checkbox at the bottom “Enable access for assistive devices” then these scripts should work.

Growl, Mail.app and AppleScript

Posted by Kevin Way Fri, 17 Aug 2007 18:55:00 GMT

I wanted a simple system that would notify me whenever I received an e-mail from somebody “important”, but not for every new message that hit my inbox.

I tried GrowlMail and Mail.Appetizer, but found them both unsatisfactory. I finally decided that what I really wanted was a growl message to appear for messages from selected recipients. Something with the name and the subject of the e-mail, like this:

Fortunately, it turned out to be reasonably simple.

First, I opened up Address Book, and made sure that the important people were all listed in appropriate groups and correct e-mail addresses.

Having done that, I opened Mail.app, and added a rule:

Description: Growl

If any of the following conditions are met:
Sender is member of Group: Clients
Sender is member of Group: Family
Sender is member of Group: InsideSystems

Perform the following actions:
Run AppleScript: ~/Library/Scripts/Applications/Mail/MailScript.scpt
but ho, you say, that script does not exist! Never fear, then I loaded up Script Editor and created MailScript.scpt as follows:

on perform_mail_action(info)

    tell application "Mail" 

        set selectedMessages to |SelectedMessages| of info
        set theRule to |Rule| of info

        repeat with eachMessage in selectedMessages
            set theSubject to subject of eachMessage
            set theSender to sender of eachMessage

            tell application "GrowlHelperApp" 

                set the allNotificationsList to ¬
                    {"New Mail"}

                set the enabledNotificationsList to ¬
                    {"New Mail"}

                register as application ¬
                    "MailScript" all notifications allNotificationsList ¬
                    default notifications enabledNotificationsList ¬
                    icon of application "Mail" 

                notify with name ¬
                    "New Mail" title ¬
                    "New Mail from " & theSender description ¬
                    theSubject application name "MailScript" 

            end tell

        end repeat
    end tell
end perform_mail_action

Now, this was the first time I ever did anything in AppleScript, so it’s quite possible there is a much better way to do all of this. As it stands, I simply glued together some sample code from Growl and some sample code from this hint on the excellent MacOSXHints.com, and it appears to work as desired.

Finally, I went into the growl prefpane, and set the priority and stickiness, so that these messages would stay on my screen even if I happened to be otherwise occupied when they came in.

Voila! Nothing spectacularly complex, but it’s a nice example of what I like about Apple. Everything’s easy to use out of the box, but when I dreamed up a random feature, I was able to implement it in a short period of time, despite having never even used their glue language before.

Hopefully somebody else would like this feature too!