rubyipmi 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. checksums.yaml +15 -0
  2. data/Gemfile +5 -3
  3. data/README.md +24 -14
  4. data/Rakefile +82 -4
  5. data/VERSION +1 -1
  6. data/lib/rubyipmi.rb +36 -14
  7. data/lib/rubyipmi/commands/basecommand.rb +57 -119
  8. data/lib/rubyipmi/freeipmi/commands/basecommand.rb +29 -25
  9. data/lib/rubyipmi/freeipmi/commands/bmc.rb +3 -3
  10. data/lib/rubyipmi/freeipmi/commands/bmcconfig.rb +6 -6
  11. data/lib/rubyipmi/freeipmi/commands/bmcinfo.rb +3 -23
  12. data/lib/rubyipmi/freeipmi/commands/fru.rb +117 -23
  13. data/lib/rubyipmi/freeipmi/commands/lan.rb +32 -37
  14. data/lib/rubyipmi/freeipmi/commands/power.rb +1 -1
  15. data/lib/rubyipmi/freeipmi/commands/sensors.rb +53 -50
  16. data/lib/rubyipmi/freeipmi/connection.rb +16 -2
  17. data/lib/rubyipmi/freeipmi/errorcodes.rb +25 -2
  18. data/lib/rubyipmi/ipmitool/commands/basecommand.rb +20 -37
  19. data/lib/rubyipmi/ipmitool/commands/bmc.rb +1 -24
  20. data/lib/rubyipmi/ipmitool/commands/chassis.rb +1 -0
  21. data/lib/rubyipmi/ipmitool/commands/fru.rb +112 -30
  22. data/lib/rubyipmi/ipmitool/commands/lan.rb +55 -77
  23. data/lib/rubyipmi/ipmitool/commands/power.rb +10 -4
  24. data/lib/rubyipmi/ipmitool/commands/sensors.rb +53 -80
  25. data/lib/rubyipmi/ipmitool/connection.rb +19 -3
  26. data/lib/rubyipmi/ipmitool/errorcodes.rb +51 -3
  27. data/rubyipmi.gemspec +73 -33
  28. data/spec/Vagrantfile +45 -0
  29. data/spec/fixtures/freeipmi/bmc_config.txt +317 -0
  30. data/spec/fixtures/freeipmi/bmc_config_lan_conf.txt +19 -0
  31. data/spec/fixtures/freeipmi/bmc_info.txt +32 -0
  32. data/spec/fixtures/freeipmi/errors.txt +3 -0
  33. data/spec/fixtures/freeipmi/fru.txt +13 -0
  34. data/spec/fixtures/freeipmi/sensors.txt +29 -0
  35. data/spec/fixtures/ipmitool/bmc_info.txt +20 -0
  36. data/spec/fixtures/ipmitool/errors.txt +10 -0
  37. data/spec/fixtures/ipmitool/fru.txt +96 -0
  38. data/spec/fixtures/ipmitool/lan.txt +17 -0
  39. data/spec/fixtures/ipmitool/sensors.txt +105 -0
  40. data/spec/integration/bmc_spec.rb +49 -0
  41. data/spec/{chassis_config_spec.rb → integration/chassis_config_spec.rb} +6 -6
  42. data/spec/integration/chassis_spec.rb +26 -0
  43. data/spec/integration/connection_spec.rb +35 -0
  44. data/spec/{fru_spec.rb → integration/fru_spec.rb} +11 -10
  45. data/spec/{lan_spec.rb → integration/lan_spec.rb} +9 -7
  46. data/spec/integration/power_spec.rb +40 -0
  47. data/spec/{rubyipmi_spec.rb → integration/rubyipmi_spec.rb} +7 -10
  48. data/spec/{sensor_spec.rb → integration/sensor_spec.rb} +5 -20
  49. data/spec/manifests/default.pp +50 -0
  50. data/spec/puppetmodules/archive/LICENSE-2.0.txt +202 -0
  51. data/spec/puppetmodules/archive/Modulefile +8 -0
  52. data/spec/puppetmodules/archive/README.md +40 -0
  53. data/spec/puppetmodules/archive/manifests/download.pp +157 -0
  54. data/spec/puppetmodules/archive/manifests/extract.pp +81 -0
  55. data/spec/puppetmodules/archive/manifests/init.pp +70 -0
  56. data/spec/puppetmodules/archive/manifests/tar-gz.pp +7 -0
  57. data/spec/puppetmodules/archive/manifests/zip.pp +7 -0
  58. data/spec/puppetmodules/archive/metadata.json +26 -0
  59. data/spec/spec_helper.rb +35 -3
  60. data/spec/unit/freeipmi/bmc-info_spec.rb +42 -0
  61. data/spec/unit/freeipmi/bmc_spec.rb +44 -0
  62. data/spec/unit/freeipmi/connection_spec.rb +56 -0
  63. data/spec/unit/freeipmi/errorcodes_spec.rb +34 -0
  64. data/spec/unit/freeipmi/fru_spec.rb +77 -0
  65. data/spec/unit/freeipmi/lan_spec.rb +0 -0
  66. data/spec/unit/freeipmi/sensors_spec.rb +83 -0
  67. data/spec/unit/ipmitool/bmc_spec.rb +78 -0
  68. data/spec/unit/ipmitool/connection_spec.rb +58 -0
  69. data/spec/unit/ipmitool/errorcodes_spec.rb +34 -0
  70. data/spec/unit/ipmitool/fru_spec.rb +77 -0
  71. data/spec/unit/ipmitool/lan_spec.rb +93 -0
  72. data/spec/unit/ipmitool/sensors_spec.rb +95 -0
  73. data/spec/unit/rubyipmi_spec.rb +31 -0
  74. data/spec/vagrant +27 -0
  75. data/spec/vagrant.pub +1 -0
  76. metadata +157 -106
  77. data/.document +0 -5
  78. data/.rspec +0 -1
  79. data/Gemfile.lock +0 -33
  80. data/spec/bmc_spec.rb +0 -46
  81. data/spec/chassis_spec.rb +0 -26
  82. data/spec/connection_spec.rb +0 -31
  83. data/spec/power_spec.rb +0 -42
@@ -10,7 +10,7 @@ module Rubyipmi::Freeipmi
10
10
  @options[opt] = false
11
11
  value = runcmd
12
12
  @options.delete_notify(opt)
13
- return value
13
+ return @result
14
14
  end
15
15
 
16
16
  # Turn on the system
@@ -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
- sensors
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
- sensors.count
22
+ list.count
22
23
  end
23
24
 
24
25
  def names
25
- sensors.keys
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
- values = sensors.each do |sensor|
32
- match = sensor.first.match(/(fan)_(\d+)/)
33
- next if match.nil?
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
- values = sensors.each do |sensor|
46
- match = sensor.first.match(/(temp)_(\d+)/)
47
- next if match.nil?
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 sensors.has_key?(method.to_s)
61
+ if not list.has_key?(method.to_s)
66
62
  raise NoMethodError
67
63
  else
68
- sensors[method.to_s]
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.lines.each do | line|
81
- # skip the header
82
- data = line.split(/\|/)
83
- # remove number column
84
- data.shift
85
- sensor = Sensor.new(data[0].strip)
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
- def initialize(sname)
102
- self[:fullname] = sname
103
- self[:name] = sname.gsub(/\ /, '_').gsub(/\./, '').downcase
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
- @@code = {
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
- @@code
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.puts "#{@options["P"]}"
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
- return "#{cmd} #{args}"
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 findfix(result, args, debug, runmethod)
39
+ def find_fix(result)
35
40
  if result
36
41
  # The errorcode code hash contains the fix
37
- fix = Rubyipmi::Ipmitool::ErrorCodes.code[result]
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
- if @result =~ /timeout|timed\ out/
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 key == value and not subkey
67
+ if value.empty?
91
68
  subkey = key
92
69
  @bmcinfo[subkey] = []
93
70
  elsif key == value and subkey
@@ -8,6 +8,7 @@ module Rubyipmi::Ipmitool
8
8
  end
9
9
 
10
10
  # Turn the led light on / off or with a delay
11
+ # status means to enable or disable the blinking
11
12
  def identify(status=false, delay=0)
12
13
  if status
13
14
  if not delay.between?(1,255)
@@ -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
- # return the list of fru information in a hash
10
- def list
11
- @list ||= parse(command)
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
- list["board_serial"]
24
+ list[DEFAULT_FRU]['board_serial']
17
25
  end
18
26
 
19
- # returns the manufacturer of the server
20
- def manufacturer
21
- list["product_manufacturer"]
27
+ def model
28
+ list[DEFAULT_FRU]['product_manufacturer']
22
29
  end
23
30
 
24
- # returns the product name of the server
25
- def product
26
- list["product_name"]
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
- if not list.has_key?(method.to_s)
33
- raise NoMethodError
34
- else
35
- list[method.to_s]
36
- end
37
- end
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
- datalist = {}
42
- data.lines.each do |line|
43
- key, value = line.split(':')
44
- next if key =~ /\n/
45
- key = key.strip.gsub(/\ /, '_').downcase
46
- datalist[key] = value.strip
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
- @options["cmdargs"] = "fru"
54
- value = runcmd
55
- @options.delete_notify("cmdargs")
56
- if value
57
- return @result
58
- end
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