elesai 0.9.3 → 0.10.5

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.
@@ -0,0 +1,120 @@
1
+ module Elesai; module Megacli
2
+
3
+ class PDlist_aAll < Megacli
4
+
5
+ def initialize
6
+ @megacli = { :adapter => { :re => /^Adapter\s+#*(?<value>\d+)/, :method => self.method(:adapter_match) },
7
+ :physicaldrive => { :re => /^(?<key>Enclosure\s+Device\s+ID):\s+(?<value>\d+)/, :method => self.method(:physicaldrive_match) },
8
+ :exit => { :re => /^Exit Code: /, :method => self.method(:exit_match) },
9
+ :attribute => { :re => /^(?<key>[A-Za-z0-9()\s#'-.&]+)[:|=](?<value>.*)/, :method => self.method(:attribute_match) }
10
+ }.freeze
11
+ @command_arguments = "-pdlist -aall".freeze
12
+ @command_output_file = "pdlist_aall".freeze
13
+ end
14
+
15
+ def parse!(lsi,opts)
16
+ fake = opts[:fake].nil? ? @command_arguments : File.join(opts[:fake],@command_output_file)
17
+ super lsi, :fake => fake, :megacli => opts[:megacli]
18
+ end
19
+
20
+ # State Machine
21
+
22
+ workflow do
23
+
24
+ state :start do
25
+ event :adapter_line, :transitions_to => :adapter
26
+ event :exit_line, :transitions_to => :exit
27
+ end
28
+
29
+ state :adapter do
30
+ event :adapter_line, :transitions_to => :adapter # empty adapter
31
+ event :physicaldrive_line, :transitions_to => :physicaldrive
32
+ event :exit_line, :transitions_to => :exit
33
+ end
34
+
35
+ state :physicaldrive do
36
+ event :attribute_line, :transitions_to => :physicaldrive
37
+ event :exit_line, :transitions_to => :exit
38
+ event :adapter_line, :transitions_to => :adapter
39
+ event :physicaldrive_line, :transitions_to => :physicaldrive
40
+ event :attribute_line, :transitions_to => :attribute
41
+ end
42
+
43
+ state :attribute do
44
+ event :attribute_line, :transitions_to => :attribute
45
+ event :physicaldrive_line, :transitions_to => :physicaldrive
46
+ event :adapter_line, :transitions_to => :adapter
47
+ event :exit_line, :transitions_to => :exit
48
+ end
49
+
50
+ state :exit
51
+
52
+ on_transition do |from, to, triggering_event, *event_args|
53
+ #puts self.spec.states[to].class
54
+ # puts " transition: #{from} >> #{triggering_event}! >> #{to}: #{event_args.join(' ')}"
55
+ #puts " #{current_state.meta}"
56
+ end
57
+ end
58
+
59
+ ### Match Handlers
60
+
61
+ def virtualdrive_match(k,match)
62
+ @log.debug "VIRTUALDRIVE! #{match.string}"
63
+ key = match[:key].gsub(/\s+/,"").downcase
64
+ value = match[:value]
65
+ virtualdrive_line!(LSI::VirtualDrive.new,key,value)
66
+ end
67
+
68
+ def physicaldrive_match(k,match)
69
+ @log.debug "PHYSICALDRIVE! #{match.string}"
70
+ key = match[:key].gsub(/\s+/,"").downcase
71
+ value = match[:value]
72
+ physicaldrive_line!(LSI::PhysicalDrive.new,key,value)
73
+ end
74
+
75
+ ### Line Handlers
76
+
77
+ # Virtual Drives
78
+
79
+ def virtualdrive_line(virtualdrive,key,value)
80
+ @log.debug " [#{current_state}] event: virtualdrive_line: new #{virtualdrive.inspect}"
81
+ virtualdrive[key.to_sym] = value.to_i
82
+ end
83
+
84
+ def on_virtualdrive_entry(old_state, event, *args)
85
+ @log.debug " [#{current_state}] on_entry: leaving #{old_state}; args: #{args}"
86
+
87
+ unless @context.current.nil?
88
+ if Elesai::LSI::VirtualDrive === @context.current
89
+ @context.close
90
+ end
91
+ end
92
+ virtualdrive = args[0]
93
+ @context.open virtualdrive
94
+ end
95
+
96
+ def on_virtualdrive_exit(new_state, event, *args)
97
+ @log.debug " [#{current_state}] on_exit: entering #{new_state}; args: #{args}"
98
+ @context.flash!(new_state)
99
+ end
100
+
101
+ # Physical Drives
102
+
103
+ def physicaldrive_line(physicaldrive,key,value)
104
+ @log.debug " [#{current_state}] event: physicaldrive_line: new #{physicaldrive.inspect}"
105
+ physicaldrive[key.to_sym] = value.to_i
106
+ end
107
+
108
+ def on_physicaldrive_entry(old_state, event, *args)
109
+ @log.debug " [#{current_state}] on_entry: leaving #{old_state}; args: #{args}"
110
+ @context.open args[0]
111
+ end
112
+
113
+ def on_physicaldrive_exit(new_state, event, *args)
114
+ @log.debug " [#{current_state}] on_exit: entering #{new_state}; args: #{args}"
115
+ @context.flash!(new_state)
116
+ end
117
+
118
+ end
119
+
120
+ end end
@@ -0,0 +1,241 @@
1
+ require 'workflow'
2
+ require 'open3'
3
+ require 'io/wait'
4
+
5
+ module Elesai; module Megacli
6
+
7
+ class Megacli
8
+
9
+ include Workflow
10
+
11
+ ### Context
12
+
13
+ class Context
14
+
15
+ def initialize(current_state,lsi)
16
+ current_state.meta[:context] = { :stack => [] }
17
+ @context = current_state.meta[:context]
18
+ @lsi = lsi
19
+ @log = Elesai::Logger.instance.log
20
+ end
21
+
22
+ def open(component)
23
+ @log.debug " * Open #{component.inspect}"
24
+ @context[:stack].push(component)
25
+ @context[component.class] = component
26
+ @log.debug " + context: #{@context[:stack]}"
27
+ end
28
+
29
+ def flash!(new_state)
30
+ new_state.meta[:context] = @context
31
+ @context = nil
32
+ @context = new_state.meta[:context]
33
+ @log.debug " + Flash context: #{@context[:stack]}"
34
+ end
35
+
36
+ def close
37
+ component = @context[:stack].pop
38
+ @context[component.class] = nil
39
+ @log.debug " * Close #{component.inspect}"
40
+ case component
41
+ when Elesai::LSI::PhysicalDrive
42
+ pd = @lsi.add(component)
43
+ pd.add_adapter(adapter)
44
+ pd.add_virtualdrive(virtualdrive) unless virtualdrive.nil?
45
+ adapter.add_physicaldrive(pd)
46
+ when Elesai::LSI::VirtualDrive
47
+ vd = @lsi.add(component)
48
+ when Elesai::LSI::BBU
49
+ @lsi.add(component)
50
+ end
51
+ @log.debug " + context: #{@context[:stack]}"
52
+ end
53
+
54
+ def current
55
+ @context[:stack][-1]
56
+ end
57
+
58
+ def adapter
59
+ @context[Elesai::LSI::Adapter]
60
+ end
61
+
62
+ def virtualdrive
63
+ @context[Elesai::LSI::VirtualDrive]
64
+ end
65
+
66
+ def physicaldrive
67
+ @context[Elesai::LSI::PhysicalDrive]
68
+ end
69
+
70
+ def bbu
71
+ @context[Elesai::LSI::BBU]
72
+ end
73
+
74
+ end
75
+
76
+ ### Regular Expression Handlers
77
+
78
+ # Adapter
79
+
80
+ def adapter_match(k,match)
81
+ @log.debug "ADAPTER! #{match.string}"
82
+ key = 'id'
83
+ value = match[:value]
84
+ adapter_line!(LSI::Adapter.new,key,value)
85
+ end
86
+
87
+ # Attribute
88
+
89
+ def attribute_match(k,match)
90
+ @log.debug "ATTRIBUTE! #{match.string}"
91
+ key = match[:key].gsub(/\s+/,"").downcase
92
+ value_tmp = match[:value]
93
+ value = value_tmp.nil? ? nil : value_tmp.strip
94
+ attribute_line!(key,value)
95
+ end
96
+
97
+ # Exit
98
+
99
+ def exit_match(k,match)
100
+ @log.debug "EXIT! #{match.string}"
101
+ exit_line!
102
+ end
103
+
104
+ ### State Machine Handlers
105
+
106
+ # Start
107
+
108
+ def on_start_exit(new_state, event, *args)
109
+ @log.debug " [#{current_state}]: on_exit : #{event} -> #{new_state}; args: #{args}"
110
+ @context = Context.new(current_state,@lsi)
111
+ end
112
+
113
+ # Adapter
114
+
115
+ def adapter_line(adapter,key,value)
116
+ @log.debug " [#{current_state}] event adapter_line: new #{adapter.inspect}"
117
+ adapter[key.to_sym] = value.to_i
118
+ @lsi.add(adapter)
119
+ end
120
+
121
+ def on_adapter_entry(old_state, event, *args)
122
+ @log.debug " [#{current_state}] on_entry: leaving #{old_state}; args: #{args}"
123
+
124
+ @context.close unless @context.current.nil? or Elesai::LSI::Adapter === @context.current
125
+ @context.open args[0]
126
+ end
127
+
128
+ def on_adapter_exit(new_state, event, *args)
129
+ @log.debug " [#{current_state}] on_exit: entering #{new_state}; args: #{args}"
130
+ @context.flash!(new_state)
131
+ end
132
+
133
+ # Attribute
134
+
135
+ def attribute_line(key,value)
136
+ @log.debug " [#{current_state}] event: attribute_line: #{key} => #{value}"
137
+ end
138
+
139
+ def on_attribute_entry(old_state, event, *args)
140
+ @log.debug " [#{current_state}] entry: leaving #{old_state}; args: #{args}; context: #{@context.current.class}"
141
+
142
+ c = @context.current
143
+ k = args[0].to_sym
144
+ v = args[1]
145
+
146
+ # Some attributes require special treatment for our purposes
147
+
148
+ case k
149
+ when :coercedsize, :noncoercedsize, :rawsize, :size
150
+ m = /(?<number>[0-9\.]+)\s+(?<unit>[A-Z]+)/.match(v)
151
+ v = LSI::PhysicalDrive::Size.new(m[:number],m[:unit])
152
+ when :raidlevel
153
+ m = /Primary-(?<primary>\d+),\s+Secondary-(?<secondary>\d+)/.match(v)
154
+ v = LSI::VirtualDrive::RaidLevel.new(m[:primary],m[:secondary])
155
+ when :firmwarestate
156
+ st,sp = v.gsub(/\s/,'').split(/,/)
157
+ state = st.gsub(/\s/,'_').downcase.to_sym
158
+ spin = sp.gsub(/\s/,'_').downcase.to_sym unless sp.nil?
159
+ v = LSI::PhysicalDrive::FirmwareState.new(state,spin)
160
+ when :state
161
+ v = v.gsub(/\s/,'_').downcase.to_sym
162
+ when :mediatype
163
+ v = v.scan(/[A-Z]/).join
164
+ when :inquirydata
165
+ v = v.gsub(/\s+/,' ')
166
+ when :relativedtateofcharge, :absolutestateofcharge, :remainingcapacityalarm, :remainingcapacity
167
+ m = /(?<number>[0-9\.]+)\s+(?<unit>[A-Za-z%]+)/.match(v)
168
+ v = LSI::BBU::NumberUnit.new(m[:number].to_f,m[:unit])
169
+ end
170
+ c[k] = v
171
+ end
172
+
173
+ def on_attribute_exit(new_state, event, *args)
174
+ @log.debug " [#{current_state}] exit: entering #{new_state} throught event #{event}; args: #{args}"
175
+ @context.close if Elesai::LSI::PhysicalDrive === @context.current and event != :attribute_line
176
+
177
+ @context.flash!(new_state)
178
+ end
179
+
180
+ # Exit
181
+
182
+ def exit_line
183
+ @log.debug " [#{current_state}] event: exit_line"
184
+ end
185
+
186
+ def on_exit_entry(new_state, event, *args)
187
+ @log.debug " [#{current_state}] exit: entering #{new_state} through event #{event}; args: #{args}"
188
+ until @context.current.nil? do
189
+ @context.close
190
+ end
191
+ end
192
+
193
+ ### Parse!
194
+
195
+ def parse!(lsi,opts)
196
+
197
+ @lsi = lsi
198
+ @log = Elesai::Logger.instance.log
199
+ output = nil
200
+
201
+ if STDIN.ready?
202
+ output = $stdin.read
203
+ else
204
+ if opts[:fake].start_with? '-'
205
+ megacli = opts[:megacli].nil? ? "Megacli" : opts[:megacli]
206
+ command = "#{megacli} #{opts[:fake]} -nolog"
207
+ command = Process.uid == 0 ? command : "sudo " << command
208
+ output, stderr_str, status = Open3.capture3(command)
209
+ raise RuntimeError, stderr_str unless status.exitstatus == 0
210
+ else
211
+ output = File.read(opts[:fake])
212
+ end
213
+ end
214
+
215
+ output.each_line do |line|
216
+ begin
217
+ line.strip!
218
+ line.gsub!(/^=+$/,'')
219
+ next if line == ''
220
+
221
+ match_flag = false
222
+ @megacli.each do |k, v|
223
+ if line =~ v[:re]
224
+ v[:method].call(k,v[:re].match(line))
225
+ match_flag = true
226
+ break
227
+ else
228
+ match_flag = false
229
+ next
230
+ end
231
+ end
232
+ raise StandardError, "cannot parse '#{line}'" unless match_flag
233
+ rescue ArgumentError # ignore lines with invalid byte sequence in UTF-8
234
+ next
235
+ end
236
+ end
237
+ end
238
+
239
+ end
240
+
241
+ end end