rubyipmi 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
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