rubyからipmi操作してみる(ruby-ipmitoolを利用)

環境構築

rubyにはruby-ipmitoolというgemパッケージがある。
これを使えばipmi操作ができそう。
ということで導入して触ってみる。

インストール方法はgem installで一発です。

# gem install ruby-ipmitool

irbで使えるかどうか確認してみます。

$ irb
>require 'ruby-ipmitool'
SyntaxError: /usr/local/ruby/1.9.2/lib/ruby/gems/1.9.1/gems/ruby-ipmitool-0.4/lib/ruby-ipmitool.rb:51: syntax error, unexpected keyword_do, expecting keyword_then or ';' or '\n'
/usr/local/ruby/1.9.2/lib/ruby/gems/1.9.1/gems/ruby-ipmitool-0.4/lib/ruby-ipmitool.rb:194: syntax error, unexpected keyword_end, expecting $end
        from /usr/local/ruby/1.9.2/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:57:in `require'
        from /usr/local/ruby/1.9.2/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:57:in `rescue in require'
        from /usr/local/ruby/1.9.2/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:35:in `require'
        from (irb):2
        from /usr/local/ruby/1.9.2/bin/irb:12:in `<main>'

するとこんな感じでエラーが・・・
どうやらruby-ipmitool.rbの51行目にsyntax errorがある模様。
ということでruby-ipmitool.rbを確認。

 48   #Run a ping check to see if the host is responding
 49   def check_host
 50     result = `ping -q -c 2 #{@conn[:host]}`
 51     if ($?.exitstatus == 0) do
 52       pingable = true
 53     else
 54       pingable = false
 55     end
 56     pingable
 57   end

51行目を見ると、if文の後ろにdoが書かれている。
このdoをthenに変更して、再度実行してみます。

$ irb
>require 'ruby-ipmitool'
=>true

すると通りました。
github上の最新のruby-ipmitool.rbを見てもこの記述方法なのでそのままだとうまくいかないのかも。
ruby 1.9.2で試したのがまずかったのかな。

接続方法

ipmitoolで接続する方法。
とりあえずirbで試してみます。
接続先のIPを10.1.1.1、IPMI接続ユーザ名をipmi_user,IPMI接続パスワードをipmipasswordとすると下記のようになります。

$ irb
irb(main):001:0> require 'ruby-ipmitool'
=> true
irb(main):002:0> conn = Ipmitool.new(:host => '10.1.1.1', :user => 'ipmi_user', :password => 'ipmipassword')
=> #<Ipmitool:0x00000008642fa0 @conn={:host=>"10.1.1.1", :user=>"ipmi_user", :password=>"ipmipassword", :check_host=>true, :binary=>"/usr/bin/ipmitool"}>

こうすることによってIPMI接続が可能になりました。
まず第1歩としてsensor listの出力結果を取得してみようと思います。

irb(main):003:0> conn.sensor
Invalid sensor command: []
NoMethodError: undefined method `split' for nil:NilClass
        from /usr/local/ruby/1.9.2/lib/ruby/gems/1.9.1/gems/ruby-ipmitool-0.4/lib/ruby-ipmitool.rb:64:in `sensor'
        from (irb):6
        from /usr/local/ruby/1.9.2/bin/irb:12:in `<main>'

するとエラーが・・・
ライブラリの中身を確認してみると、ipmitoolコマンド実行部分の実装が怪しい模様。

def run_command(command, *args)
  `#{@conn[:binary]} -H #{@conn[:host]} -U #{@conn[:user]} -P #{@conn[:password]} #{command} #{args unless args.nil?}`.downcase!
end

ipmitoolコマンドを実行しているのは上記部分ですが、引数としてargsをとっています。
ここでsensorが実行された際、argsは空でやってくるので、ipmitoolコマンドの実際の実行内容は下記のようになっています。

ipmitool -H 10.1.1.1 -U ipmi_user -P ipmipassword sensor []

[]が余分です。
ということで、ここも書き換え。

def run_command(command, *args)
  `#{@conn[:binary]} -H #{@conn[:host]} -U #{@conn[:user]} -P #{@conn[:password]} #{command} #{args unless args.size == 0}`.downcase!
end

これでargs配列のサイズが0の場合は何もipmitoolコマンドの引数として渡さないようにしました。
すると実行可能になりこんな結果が返ってきます。

irb(main):003:0> conn.sensor
=> {
     :bios=>["na        ", "discrete  ", "na   ", "na       ", "na       ", "na       ", "na       ", "na       ", "na"], 
     :acpi_state=>["0x1       ", "discrete  ", "0x0100| na       ", "na       ", "na       ", "na       ", "na       ", "na"], 
     :system_reset=>["0x0       ", "discrete  ", "0x0000| na       ", "na       ", "na       ", "na       ", "na       ", "na"], 
     :cpu_fan=>["1980.590  ", "rpm       ", "ok   ", "na       ", "na       ", "na       ", "na       ", "771.962  ", "na"], 
     :system_fan=>["1766.472  ", "rpm       ", "ok   ", "na       ", "na       ", "na       ", "na       ", "771.962  ", "na"], 
     :system_12v=>["12.264    ", "volts     ", "ok   ", "na       ", "11.368   ", "na       ", "na       ", "12.600   ", "na"], 
     :system_5v=>["5.242     ", "volts     ", "ok   ", "na       ", "4.820    ", "na       ", "na       ", "5.335    ", "na"], 
     :system_aux_5v=>["5.101     ", "volts     ", "ok   ", "na       ", "4.820    ", "na       ", "na       ", "5.335    ", "na"], 
     :system_33v=>["3.326     ", "volts     ", "ok   ", "na       ", "3.219    ", "na       ", "na       ", "3.557    ", "na"], 
     :system_aux_33v=>["3.357     ", "volts     ", "ok   ", "na       ", "3.219    ", "na       ", "na       ", "3.557    ", "na"], 
     :cpu_vcore=>["1.117     ", "volts     ", "ok   ", "na       ", "0.823    ", "na       ", "na       ", "1.568    ", "na"], 
     :cpu_12v=>["12.264    ", "volts     ", "ok   ", "na       ", "11.368   ", "na       ", "na       ", "12.600   ", "na"], 
     :ht_12v=>["1.196     ", "volts     ", "ok   ", "na       ", "1.117    ", "na       ", "na       ", "1.235    ", "na"], 
     :memory_vcore=>["1.813     ", "volts     ", "ok   ", "na       ", "1.666    ", "na       ", "na       ", "1.862    ", "na"], 
     :memory_vterm=>["0.902     ", "volts     ", "ok   ", "na       ", "0.804    ", "na       ", "na       ", "0.970    ", "na"], 
     :mcp55_15v=>["1.499     ", "volts     ", "ok   ", "na       ", "1.372    ", "na       ", "na       ", "1.568    ", "na"], 
     :mcp55_14v=>["1.411     ", "volts     ", "ok   ", "na       ", "1.235    ", "na       ", "na       ", "1.509    ", "na"], 
     :pci_system_err=>["na        ", "discrete  ", "na   ", "na       ", "na       ", "na       ", "na       ", "na       ", "na"], 
     :mem_recover_err=>["na        ", "discrete  ", "na   ", "na       ", "na       ", "na       ", "na       ", "na       ", "na"], 
     :memunrecover_er=>["na        ", "discrete  ", "na   ", "na       ", "na       ", "na       ", "na       ", "na       ", "na"], 
     :rcpu_diode_1=>["30.000    ", "degrees c ", "ok   ", "na       ", "na       ", "na       ", "na       ", "85.000   ", "na"], 
     :ambient=>["31.000    ", "degrees c ", "ok   ", "na       ", "na       ", "na       ", "na       ", "53.000   ", "na"], 
     :local_vrd0_temp=>["35.000    ", "degrees c ", "ok   ", "na       ", "na       ", "na       ", "na       ", "85.000   ", "na"], 
     :"therm-trip"=>["0x0       ", "discrete  ", "0x0100| na       ", "na       ", "na       ", "na       ", "na       ", "na"], 
     :cpu_prochot=>["0x0       ", "discrete  ", "0x0100| na       ", "na       ", "na       ", "na       ", "na       ", "na"], 
     :lom_link_status=>["0x1       ", "discrete  ", "0x0200| na       ", "na       ", "na       ", "na       ", "na       ", "na"], 
     :lo100_present=>["0x0       ", "discrete  ", "0x0100| na       ", "na       ", "na       ", "na       ", "na       ", "na"],
     :watchdog=>["na        ", "discrete  ", "na   ", "na       ", "na       ", "na       ", "na       ", "na       ", "na"]
    }

これを実際のipmitoolコマンドの実行結果と比較すると

# ipmitool -H 10.1.1.1 -U ipmi_user -P ipmipassword sensor
BIOS             | na         | discrete   | na    | na        | na        | na        | na        | na        | na        
ACPI State       | 0x1        | discrete   | 0x0100| na        | na        | na        | na        | na        | na        
System Reset     | 0x0        | discrete   | 0x0000| na        | na        | na        | na        | na        | na        
CPU Fan          | 1980.590   | RPM        | ok    | na        | na        | na        | na        | 771.962   | na        
System Fan       | 1750.700   | RPM        | ok    | na        | na        | na        | na        | 771.962   | na        
System 12V       | 12.264     | Volts      | ok    | na        | 11.368    | na        | na        | 12.600    | na        
System 5V        | 5.242      | Volts      | ok    | na        | 4.820     | na        | na        | 5.335     | na        
System AUX 5V    | 5.101      | Volts      | ok    | na        | 4.820     | na        | na        | 5.335     | na        
System 3.3V      | 3.326      | Volts      | ok    | na        | 3.219     | na        | na        | 3.557     | na        
System AUX 3.3V  | 3.357      | Volts      | ok    | na        | 3.219     | na        | na        | 3.557     | na        
CPU Vcore        | 1.117      | Volts      | ok    | na        | 0.823     | na        | na        | 1.568     | na        
CPU 12V          | 12.264     | Volts      | ok    | na        | 11.368    | na        | na        | 12.600    | na        
HT 1.2V          | 1.196      | Volts      | ok    | na        | 1.117     | na        | na        | 1.235     | na        
Memory Vcore     | 1.813      | Volts      | ok    | na        | 1.666     | na        | na        | 1.862     | na        
Memory Vterm     | 0.902      | Volts      | ok    | na        | 0.804     | na        | na        | 0.970     | na        
MCP55 1.5V       | 1.499      | Volts      | ok    | na        | 1.372     | na        | na        | 1.568     | na        
MCP55 1.4V       | 1.411      | Volts      | ok    | na        | 1.235     | na        | na        | 1.509     | na        
PCI System Err   | na         | discrete   | na    | na        | na        | na        | na        | na        | na        
Mem Recover Err  | na         | discrete   | na    | na        | na        | na        | na        | na        | na        
MemUnrecover Er  | na         | discrete   | na    | na        | na        | na        | na        | na        | na        
RCPU Diode 1     | 27.000     | degrees C  | ok    | na        | na        | na        | na        | 85.000    | na        
Ambient          | 31.000     | degrees C  | ok    | na        | na        | na        | na        | 53.000    | na        
Local VRD0 Temp  | 35.000     | degrees C  | ok    | na        | na        | na        | na        | 85.000    | na        
Therm-Trip       | 0x0        | discrete   | 0x0100| na        | na        | na        | na        | na        | na        
CPU Prochot      | 0x0        | discrete   | 0x0100| na        | na        | na        | na        | na        | na        
LOM Link Status  | 0x1        | discrete   | 0x0200| na        | na        | na        | na        | na        | na        
LO100 Present    | 0x0        | discrete   | 0x0100| na        | na        | na        | na        | na        | na        
Watchdog         | na         | discrete   | na    | na        | na        | na        | na        | na        | na        

同じ項目が取れてきているのがわかります。(※値が違うのは実行タイミングの違いによるものです)

sensorアイテムの値は全てdowncaseされ、空白は_に置き換えられています。
なので、上記のCPU Fanの値だけを見たければこのようにします。

irb(main):004:0> conn.sensor.cpu_fan
=> ["1980.590  ", "rpm       ", "ok   ", "na       ", "na       ", "na       ", "na       ", "771.962  ", "na"]

ちょっと試してみて、中身を見てみましたが、
ruby-ipmitoolは結局ipmitoolのコマンドのwrapperです。
サーバにipmitoolが導入されていないと動きません。