rubyipmi 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/Gemfile +5 -3
- data/README.md +24 -14
- data/Rakefile +82 -4
- data/VERSION +1 -1
- data/lib/rubyipmi.rb +36 -14
- data/lib/rubyipmi/commands/basecommand.rb +57 -119
- data/lib/rubyipmi/freeipmi/commands/basecommand.rb +29 -25
- data/lib/rubyipmi/freeipmi/commands/bmc.rb +3 -3
- data/lib/rubyipmi/freeipmi/commands/bmcconfig.rb +6 -6
- data/lib/rubyipmi/freeipmi/commands/bmcinfo.rb +3 -23
- data/lib/rubyipmi/freeipmi/commands/fru.rb +117 -23
- data/lib/rubyipmi/freeipmi/commands/lan.rb +32 -37
- data/lib/rubyipmi/freeipmi/commands/power.rb +1 -1
- data/lib/rubyipmi/freeipmi/commands/sensors.rb +53 -50
- data/lib/rubyipmi/freeipmi/connection.rb +16 -2
- data/lib/rubyipmi/freeipmi/errorcodes.rb +25 -2
- data/lib/rubyipmi/ipmitool/commands/basecommand.rb +20 -37
- data/lib/rubyipmi/ipmitool/commands/bmc.rb +1 -24
- data/lib/rubyipmi/ipmitool/commands/chassis.rb +1 -0
- data/lib/rubyipmi/ipmitool/commands/fru.rb +112 -30
- data/lib/rubyipmi/ipmitool/commands/lan.rb +55 -77
- data/lib/rubyipmi/ipmitool/commands/power.rb +10 -4
- data/lib/rubyipmi/ipmitool/commands/sensors.rb +53 -80
- data/lib/rubyipmi/ipmitool/connection.rb +19 -3
- data/lib/rubyipmi/ipmitool/errorcodes.rb +51 -3
- data/rubyipmi.gemspec +73 -33
- data/spec/Vagrantfile +45 -0
- data/spec/fixtures/freeipmi/bmc_config.txt +317 -0
- data/spec/fixtures/freeipmi/bmc_config_lan_conf.txt +19 -0
- data/spec/fixtures/freeipmi/bmc_info.txt +32 -0
- data/spec/fixtures/freeipmi/errors.txt +3 -0
- data/spec/fixtures/freeipmi/fru.txt +13 -0
- data/spec/fixtures/freeipmi/sensors.txt +29 -0
- data/spec/fixtures/ipmitool/bmc_info.txt +20 -0
- data/spec/fixtures/ipmitool/errors.txt +10 -0
- data/spec/fixtures/ipmitool/fru.txt +96 -0
- data/spec/fixtures/ipmitool/lan.txt +17 -0
- data/spec/fixtures/ipmitool/sensors.txt +105 -0
- data/spec/integration/bmc_spec.rb +49 -0
- data/spec/{chassis_config_spec.rb → integration/chassis_config_spec.rb} +6 -6
- data/spec/integration/chassis_spec.rb +26 -0
- data/spec/integration/connection_spec.rb +35 -0
- data/spec/{fru_spec.rb → integration/fru_spec.rb} +11 -10
- data/spec/{lan_spec.rb → integration/lan_spec.rb} +9 -7
- data/spec/integration/power_spec.rb +40 -0
- data/spec/{rubyipmi_spec.rb → integration/rubyipmi_spec.rb} +7 -10
- data/spec/{sensor_spec.rb → integration/sensor_spec.rb} +5 -20
- data/spec/manifests/default.pp +50 -0
- data/spec/puppetmodules/archive/LICENSE-2.0.txt +202 -0
- data/spec/puppetmodules/archive/Modulefile +8 -0
- data/spec/puppetmodules/archive/README.md +40 -0
- data/spec/puppetmodules/archive/manifests/download.pp +157 -0
- data/spec/puppetmodules/archive/manifests/extract.pp +81 -0
- data/spec/puppetmodules/archive/manifests/init.pp +70 -0
- data/spec/puppetmodules/archive/manifests/tar-gz.pp +7 -0
- data/spec/puppetmodules/archive/manifests/zip.pp +7 -0
- data/spec/puppetmodules/archive/metadata.json +26 -0
- data/spec/spec_helper.rb +35 -3
- data/spec/unit/freeipmi/bmc-info_spec.rb +42 -0
- data/spec/unit/freeipmi/bmc_spec.rb +44 -0
- data/spec/unit/freeipmi/connection_spec.rb +56 -0
- data/spec/unit/freeipmi/errorcodes_spec.rb +34 -0
- data/spec/unit/freeipmi/fru_spec.rb +77 -0
- data/spec/unit/freeipmi/lan_spec.rb +0 -0
- data/spec/unit/freeipmi/sensors_spec.rb +83 -0
- data/spec/unit/ipmitool/bmc_spec.rb +78 -0
- data/spec/unit/ipmitool/connection_spec.rb +58 -0
- data/spec/unit/ipmitool/errorcodes_spec.rb +34 -0
- data/spec/unit/ipmitool/fru_spec.rb +77 -0
- data/spec/unit/ipmitool/lan_spec.rb +93 -0
- data/spec/unit/ipmitool/sensors_spec.rb +95 -0
- data/spec/unit/rubyipmi_spec.rb +31 -0
- data/spec/vagrant +27 -0
- data/spec/vagrant.pub +1 -0
- metadata +157 -106
- data/.document +0 -5
- data/.rspec +0 -1
- data/Gemfile.lock +0 -33
- data/spec/bmc_spec.rb +0 -46
- data/spec/chassis_spec.rb +0 -26
- data/spec/connection_spec.rb +0 -31
- data/spec/power_spec.rb +0 -42
@@ -6,103 +6,106 @@ module Rubyipmi::Freeipmi
|
|
6
6
|
super("ipmi-sensors", opts)
|
7
7
|
@options["no-header-output"] = false
|
8
8
|
@options["output-sensor-state"] = false
|
9
|
+
@options["entity-sensor-names"] = false
|
9
10
|
end
|
10
11
|
|
11
12
|
def refresh
|
12
13
|
@sensors = nil
|
13
|
-
|
14
|
+
list
|
14
15
|
end
|
15
16
|
|
16
17
|
def list
|
17
|
-
sensors
|
18
|
+
@sensors ||= parse(getsensors)
|
18
19
|
end
|
19
20
|
|
20
21
|
def count
|
21
|
-
|
22
|
+
list.count
|
22
23
|
end
|
23
24
|
|
24
25
|
def names
|
25
|
-
|
26
|
+
list.keys
|
26
27
|
end
|
27
28
|
|
29
|
+
# returns a hash of fan sensors where the key is fan name and value is the sensor
|
28
30
|
def fanlist(refreshdata=false)
|
29
31
|
refresh if refreshdata
|
30
|
-
flist =
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
if match[1] == "fan"
|
35
|
-
num = (match[2].to_i) -1
|
36
|
-
flist[num] = sensor.last[:value]
|
32
|
+
flist = {}
|
33
|
+
list.each do | name,sensor |
|
34
|
+
if name =~ /.*fan.*/
|
35
|
+
flist[name] = sensor
|
37
36
|
end
|
38
37
|
end
|
39
|
-
flist
|
38
|
+
return flist
|
40
39
|
end
|
41
40
|
|
41
|
+
# returns a hash of sensors where each key is the name of the sensor and the value is the sensor
|
42
42
|
def templist(refreshdata=false)
|
43
43
|
refresh if refreshdata
|
44
|
-
tlist =
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
if match[1] == "temp"
|
49
|
-
num = (match[2].to_i) -1
|
50
|
-
tlist[num] = sensor.last[:value]
|
44
|
+
tlist = {}
|
45
|
+
list.each do | name , sensor |
|
46
|
+
if sensor[:unit] =~ /.*degree.*/ || name =~ /.*temp.*/
|
47
|
+
tlist[name] = sensor
|
51
48
|
end
|
52
49
|
end
|
53
|
-
tlist
|
50
|
+
return tlist
|
54
51
|
end
|
55
52
|
|
56
|
-
|
53
|
+
def getsensors
|
54
|
+
value = runcmd
|
55
|
+
return @result
|
56
|
+
end
|
57
57
|
|
58
58
|
private
|
59
59
|
|
60
|
-
def sensors
|
61
|
-
@sensors ||= parse(getsensors)
|
62
|
-
end
|
63
|
-
|
64
60
|
def method_missing(method, *args, &block)
|
65
|
-
if not
|
61
|
+
if not list.has_key?(method.to_s)
|
66
62
|
raise NoMethodError
|
67
63
|
else
|
68
|
-
|
64
|
+
list[method.to_s]
|
69
65
|
end
|
70
66
|
end
|
71
67
|
|
72
|
-
|
73
|
-
def getsensors
|
74
|
-
value = runcmd
|
75
|
-
@result
|
76
|
-
end
|
77
|
-
|
78
68
|
def parse(data)
|
79
69
|
sensorlist = {}
|
80
|
-
data.
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
sensor[:type] = data[1].strip
|
87
|
-
sensor[:state] = data[2].strip
|
88
|
-
sensor[:value] = data[3].strip
|
89
|
-
sensor[:unit] = data[4].strip
|
90
|
-
sensor[:status] = data[5].strip
|
91
|
-
sensorlist[sensor[:name]] = sensor
|
92
|
-
|
70
|
+
if ! data.nil?
|
71
|
+
data.lines.each do | line|
|
72
|
+
# skip the header
|
73
|
+
sensor = Sensor.new(line)
|
74
|
+
sensorlist[sensor[:name]] = sensor
|
75
|
+
end
|
93
76
|
end
|
94
77
|
return sensorlist
|
95
|
-
|
96
78
|
end
|
79
|
+
|
97
80
|
end
|
98
81
|
|
99
82
|
class Sensor < Hash
|
83
|
+
def initialize(line)
|
84
|
+
parse(line)
|
85
|
+
self[:name] = normalize(self[:name])
|
86
|
+
end
|
100
87
|
|
101
|
-
|
102
|
-
|
103
|
-
|
88
|
+
private
|
89
|
+
def normalize(text)
|
90
|
+
text.gsub(/\ /, '_').gsub(/\./, '').downcase
|
104
91
|
end
|
105
92
|
|
93
|
+
# Parse the individual sensor
|
94
|
+
# Note: not all fields will exist on every server
|
95
|
+
def parse(line)
|
96
|
+
fields = [:id_num, :name, :value, :unit, :status, :type, :state, :lower_nonrec,
|
97
|
+
:lower_crit,:lower_noncrit, :upper_crit, :upper_nonrec, :asserts_enabled, :deasserts_enabled ]
|
98
|
+
data = line.split(/\|/)
|
99
|
+
# should we ever encounter a field not in the fields list, just use a counter based fieldname so we just
|
100
|
+
# use field1, field2, field3, ...
|
101
|
+
i = 0
|
102
|
+
data.each do | value |
|
103
|
+
field ||= fields.shift || "field#{i}"
|
104
|
+
self[field] = value.strip
|
105
|
+
i = i.next
|
106
|
+
end
|
107
|
+
end
|
106
108
|
end
|
109
|
+
|
107
110
|
end
|
108
111
|
|
@@ -14,7 +14,7 @@ module Rubyipmi
|
|
14
14
|
attr_accessor :options
|
15
15
|
|
16
16
|
|
17
|
-
def initialize(user, pass, host)
|
17
|
+
def initialize(user, pass, host,debug=false)
|
18
18
|
@options = Rubyipmi::ObservableHash.new
|
19
19
|
raise("Must provide a host to connect to") unless host
|
20
20
|
@options["hostname"] = host
|
@@ -22,7 +22,7 @@ module Rubyipmi
|
|
22
22
|
# So they are not required
|
23
23
|
@options["username"] = user if user
|
24
24
|
@options["password"] = pass if pass
|
25
|
-
|
25
|
+
#@options["driver-type"] = 'LAN_2_0'
|
26
26
|
#getWorkArounds
|
27
27
|
end
|
28
28
|
|
@@ -46,6 +46,20 @@ module Rubyipmi
|
|
46
46
|
@sensors ||= Rubyipmi::Freeipmi::Sensors.new(@options)
|
47
47
|
end
|
48
48
|
|
49
|
+
def get_diag
|
50
|
+
data = {}
|
51
|
+
data['provider'] = provider
|
52
|
+
if @fru
|
53
|
+
data['frus'] = @fru.getfrus
|
54
|
+
end
|
55
|
+
if @sensors
|
56
|
+
data['sensors'] = @sensors.getsensors
|
57
|
+
end
|
58
|
+
if @bmc
|
59
|
+
data['bmc_info'] = @bmc.info
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
49
63
|
end
|
50
64
|
end
|
51
65
|
end
|
@@ -2,14 +2,37 @@ module Rubyipmi::Freeipmi
|
|
2
2
|
|
3
3
|
class ErrorCodes
|
4
4
|
|
5
|
-
@@
|
5
|
+
@@codes = {
|
6
6
|
"authentication type unavailable for attempted privilege level" => {"driver-type" => "LAN_2_0"},
|
7
|
+
"authentication type unavailable for attempted privilege level\n" => {"driver-type" => "LAN_2_0"},
|
7
8
|
|
8
9
|
}
|
9
10
|
|
11
|
+
def self.length
|
12
|
+
@@codes.length
|
13
|
+
end
|
14
|
+
|
10
15
|
def self.code
|
11
|
-
@@
|
16
|
+
@@codes
|
12
17
|
end
|
18
|
+
|
19
|
+
def self.search(code)
|
20
|
+
# example error code:
|
21
|
+
# "/usr/local/sbin/bmc-info: authentication type unavailable for attempted privilege level\n"
|
22
|
+
code.chomp! # clean up newline
|
23
|
+
code = code.split(':').last.strip # clean up left hand side and strip white space from sides
|
24
|
+
fix = @@codes.fetch(code,nil)
|
25
|
+
if fix.nil?
|
26
|
+
@@codes.each do | error, result |
|
27
|
+
if code =~ /.*#{error}.*/i
|
28
|
+
fix = result
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
raise "No Fix found" if fix.nil?
|
33
|
+
return fix
|
34
|
+
end
|
35
|
+
|
13
36
|
end
|
14
37
|
|
15
38
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'rubyipmi/ipmitool/errorcodes'
|
2
|
+
|
1
3
|
module Rubyipmi::Ipmitool
|
2
4
|
|
3
5
|
class BaseCommand < Rubyipmi::BaseCommand
|
@@ -5,68 +7,49 @@ module Rubyipmi::Ipmitool
|
|
5
7
|
def setpass
|
6
8
|
super
|
7
9
|
@options["f"] = @passfile.path
|
8
|
-
@passfile.
|
10
|
+
@passfile.write "#{@options["P"]}"
|
11
|
+
@passfile.rewind
|
9
12
|
@passfile.close
|
13
|
+
end
|
10
14
|
|
11
|
-
|
15
|
+
def max_retry_count
|
16
|
+
@max_retry_count ||= Rubyipmi::Ipmitool::ErrorCodes.length
|
12
17
|
end
|
13
18
|
|
14
19
|
def makecommand
|
15
|
-
args =
|
20
|
+
args = ''
|
16
21
|
# need to format the options to ipmitool format
|
17
22
|
@options.each do |k,v|
|
23
|
+
# must remove from command line as its handled via conf file
|
24
|
+
next if k == "P"
|
18
25
|
next if k == "cmdargs"
|
19
|
-
args << "-#{k} #{v}
|
26
|
+
args << " -#{k} #{v}"
|
20
27
|
end
|
21
|
-
# must remove from command line as its handled via conf file
|
22
|
-
args.delete("-P")
|
23
28
|
|
24
29
|
# since ipmitool requires commands to be in specific order
|
25
|
-
args << " " + options["cmdargs"]
|
26
30
|
|
27
|
-
|
28
|
-
end
|
31
|
+
args << ' ' + options.fetch('cmdargs', '')
|
29
32
|
|
33
|
+
return "#{cmd} #{args.lstrip}"
|
34
|
+
end
|
30
35
|
|
31
36
|
# The findfix method acts like a recursive method and applies fixes defined in the errorcodes
|
32
37
|
# If a fix is found it is applied to the options hash, and then the last run command is retried
|
33
38
|
# until all the fixes are exhausted or a error not defined in the errorcodes is found
|
34
|
-
def
|
39
|
+
def find_fix(result)
|
35
40
|
if result
|
36
41
|
# The errorcode code hash contains the fix
|
37
|
-
|
38
|
-
|
39
|
-
if not fix
|
40
|
-
raise "#{result}"
|
41
|
-
else
|
42
|
+
begin
|
43
|
+
fix = ErrorCodes.search(result)
|
42
44
|
@options.merge_notify!(fix)
|
43
|
-
# retry the last called method
|
44
|
-
# its quicker to explicitly call these commands than calling a command block
|
45
|
-
if runmethod == "runcmd"
|
46
|
-
runcmd(debug)
|
47
|
-
else
|
48
|
-
runcmd_without_opts(args, debug)
|
49
|
-
end
|
50
45
|
|
46
|
+
rescue
|
47
|
+
raise "Could not find fix for error code: \n#{result}"
|
51
48
|
end
|
52
|
-
|
53
49
|
end
|
54
50
|
end
|
55
|
-
def throwError
|
56
|
-
# Find out what kind of error is happening, parse results
|
57
|
-
# Check for authentication or connection issue
|
58
|
-
#puts "ipmi call: #{@lastcall}"
|
59
51
|
|
60
|
-
|
61
|
-
code = "ipmi call: #{@lastcall} timed out"
|
62
|
-
raise code
|
63
|
-
else
|
64
|
-
# ipmitool spits out many errors so for now we will just take the first error
|
65
|
-
code = @result.split(/\r\n/).first if not @result.empty?
|
66
|
-
end
|
67
|
-
throw :ipmierror, code
|
68
|
-
end
|
52
|
+
end
|
69
53
|
|
70
54
|
|
71
|
-
end
|
72
55
|
end
|
@@ -49,29 +49,6 @@ module Rubyipmi::Ipmitool
|
|
49
49
|
|
50
50
|
end
|
51
51
|
|
52
|
-
# Some sample data for info
|
53
|
-
# Device ID : 17
|
54
|
-
# Device Revision : 1
|
55
|
-
# Firmware Revision : 2.9
|
56
|
-
# IPMI Version : 2.0
|
57
|
-
# Manufacturer ID : 11
|
58
|
-
# Manufacturer Name : Hewlett-Packard
|
59
|
-
# Product ID : 8192 (0x2000)
|
60
|
-
# Product Name : Unknown (0x2000)
|
61
|
-
# Device Available : yes
|
62
|
-
# Provides Device SDRs : yes
|
63
|
-
# Additional Device Support :
|
64
|
-
# Sensor Device
|
65
|
-
# SDR Repository Device
|
66
|
-
# SEL Device
|
67
|
-
# FRU Inventory Device
|
68
|
-
# Aux Firmware Rev Info :
|
69
|
-
# 0x00
|
70
|
-
# 0x00
|
71
|
-
# 0x00
|
72
|
-
# 0x30
|
73
|
-
|
74
|
-
|
75
52
|
# This function will get the bmcinfo and return a hash of each item in the info
|
76
53
|
def retrieve
|
77
54
|
@options["cmdargs"] = "bmc info"
|
@@ -87,7 +64,7 @@ module Rubyipmi::Ipmitool
|
|
87
64
|
key = item.first.strip
|
88
65
|
value = item.last.strip
|
89
66
|
# if the following condition is met we have subvalues
|
90
|
-
if
|
67
|
+
if value.empty?
|
91
68
|
subkey = key
|
92
69
|
@bmcinfo[subkey] = []
|
93
70
|
elsif key == value and subkey
|
@@ -2,62 +2,144 @@ module Rubyipmi::Ipmitool
|
|
2
2
|
|
3
3
|
class Fru < Rubyipmi::Ipmitool::BaseCommand
|
4
4
|
|
5
|
+
attr_accessor :list
|
6
|
+
|
7
|
+
DEFAULT_FRU = 'builtin_fru_device'
|
8
|
+
|
9
|
+
|
5
10
|
def initialize(opts = ObservableHash.new)
|
6
11
|
super("ipmitool", opts)
|
12
|
+
@list = {}
|
7
13
|
end
|
8
14
|
|
9
|
-
|
10
|
-
|
11
|
-
|
15
|
+
def names
|
16
|
+
list.keys
|
17
|
+
end
|
18
|
+
|
19
|
+
def manufacturer
|
20
|
+
list[DEFAULT_FRU]['product_manufacturer']
|
12
21
|
end
|
13
22
|
|
14
|
-
# returns the serial of the board
|
15
23
|
def serial
|
16
|
-
|
24
|
+
list[DEFAULT_FRU]['board_serial']
|
17
25
|
end
|
18
26
|
|
19
|
-
|
20
|
-
|
21
|
-
list["product_manufacturer"]
|
27
|
+
def model
|
28
|
+
list[DEFAULT_FRU]['product_manufacturer']
|
22
29
|
end
|
23
30
|
|
24
|
-
#
|
25
|
-
def
|
26
|
-
|
31
|
+
# return the list of fru information in a hash
|
32
|
+
def list
|
33
|
+
if @list.count < 1
|
34
|
+
parse(getfrus)
|
35
|
+
end
|
36
|
+
@list
|
37
|
+
end
|
38
|
+
|
39
|
+
# method to retrieve the raw fru data
|
40
|
+
def getfrus
|
41
|
+
command
|
27
42
|
end
|
28
43
|
|
29
44
|
private
|
30
45
|
|
46
|
+
# I use method missing to allow the user to say Fru.<name> which returns a frudata object unless the user
|
47
|
+
# passes a keyname from the default fru device
|
31
48
|
def method_missing(method, *args, &block)
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
49
|
+
name = method.to_s
|
50
|
+
fru = list.fetch(name, nil)
|
51
|
+
# if the user wanted some data from the default fru, lets show the data for the fru. Otherwise
|
52
|
+
# we return the Fru with the given name
|
53
|
+
if fru.nil?
|
54
|
+
if list[DEFAULT_FRU].keys.include?(name)
|
55
|
+
return list[DEFAULT_FRU][name]
|
56
|
+
else
|
57
|
+
# maybe we should return nil instead? hmm...
|
58
|
+
raise NoMethodError
|
59
|
+
end
|
60
|
+
else
|
61
|
+
return fru
|
62
|
+
end
|
63
|
+
end
|
38
64
|
|
39
65
|
# parse the fru information
|
40
66
|
def parse(data)
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
67
|
+
if ! data.nil?
|
68
|
+
parsed_data = []
|
69
|
+
data.lines.each do |line|
|
70
|
+
if line =~ /^FRU.*/
|
71
|
+
# this is the either the first line of of the fru or another fru
|
72
|
+
if parsed_data.count != 0
|
73
|
+
# we have reached a new fru device so lets record the previous fru
|
74
|
+
new_fru = FruData.new(parsed_data)
|
75
|
+
parsed_data = []
|
76
|
+
@list[new_fru[:name]] = new_fru
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
parsed_data << line
|
81
|
+
end
|
82
|
+
# process the last fru
|
83
|
+
if parsed_data.count != 0
|
84
|
+
# we have reached a new fru device so lets record the previous fru
|
85
|
+
new_fru = FruData.new(parsed_data)
|
86
|
+
parsed_data = []
|
87
|
+
@list[new_fru[:name]] = new_fru
|
88
|
+
end
|
47
89
|
end
|
48
|
-
return datalist
|
49
90
|
end
|
50
91
|
|
51
92
|
# run the command and return result
|
52
93
|
def command
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
94
|
+
@options["cmdargs"] = "fru"
|
95
|
+
value = runcmd
|
96
|
+
@options.delete_notify("cmdargs")
|
97
|
+
if value
|
98
|
+
return @result
|
99
|
+
end
|
59
100
|
end
|
60
101
|
|
61
102
|
end
|
62
103
|
|
104
|
+
|
105
|
+
|
106
|
+
|
107
|
+
class FruData < Hash
|
108
|
+
|
109
|
+
def name
|
110
|
+
self[:name]
|
111
|
+
end
|
112
|
+
|
113
|
+
def initialize(data)
|
114
|
+
parse(data)
|
115
|
+
end
|
116
|
+
|
117
|
+
# parse the fru information that should be an array of lines
|
118
|
+
def parse(data)
|
119
|
+
if ! data.nil?
|
120
|
+
data.each do |line|
|
121
|
+
key, value = line.split(':', 2)
|
122
|
+
if key =~ /^FRU\s+Device.*/
|
123
|
+
if value =~ /([\w\s]*)\(.*\)/
|
124
|
+
self[:name] = $~[1].strip.gsub(/\ /, '_').downcase
|
125
|
+
end
|
126
|
+
else
|
127
|
+
key = key.strip.gsub(/\ /, '_').downcase
|
128
|
+
if ! value.nil?
|
129
|
+
self[key] = value.strip
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
private
|
137
|
+
|
138
|
+
def method_missing(method, *args, &block)
|
139
|
+
self.fetch(method.to_s, nil)
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
143
|
+
|
144
|
+
|
63
145
|
end
|