elesai 0.4.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.
- data/LICENSE +31 -0
- data/README.md +81 -0
- data/bin/check_elesai +18 -0
- data/bin/elesai +18 -0
- data/lib/elesai/about.rb +5 -0
- data/lib/elesai/cli.rb +203 -0
- data/lib/elesai/logger.rb +20 -0
- data/lib/elesai/lsi.rb +210 -0
- data/lib/elesai/megacli.rb +393 -0
- data/lib/elesai/version.rb +3 -0
- data/lib/elesai.rb +6 -0
- metadata +90 -0
data/LICENSE
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
/*
|
2
|
+
* Copyright (c) 2012 by Evernote Corporation, All rights reserved.
|
3
|
+
*
|
4
|
+
* Use of the source code and binary libraries included in this package
|
5
|
+
* is permitted under the following terms:
|
6
|
+
*
|
7
|
+
* Redistribution and use in source and binary forms, with or without
|
8
|
+
* modification, are permitted provided that the following conditions
|
9
|
+
* are met:
|
10
|
+
*
|
11
|
+
* 1. Redistributions of source code must retain the above copyright
|
12
|
+
* notice, this list of conditions and the following disclaimer.
|
13
|
+
* 2. Redistributions in binary form must reproduce the above copyright
|
14
|
+
* notice, this list of conditions and the following disclaimer in the
|
15
|
+
* documentation and/or other materials provided with the distribution.
|
16
|
+
*
|
17
|
+
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
18
|
+
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
19
|
+
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
20
|
+
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
21
|
+
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
22
|
+
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
23
|
+
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
24
|
+
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
25
|
+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
26
|
+
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
27
|
+
*
|
28
|
+
* Licensed under the Apache License, Version 2.0 (the "License"); you may
|
29
|
+
* not use this file except in compliance with the License. You may obtain a
|
30
|
+
* copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
31
|
+
*/
|
data/README.md
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
# OVERVIEW
|
2
|
+
|
3
|
+
*Elesai* is a wrapper around LSI's `MegaCli` utility that provides access to common types of information about the RAID controllers via a `show` action without the need to speak martian. It is a line-oriented tool so that it can be combined with other Unix command-line tools to process and manipulate the data (i.e., `sed`, `awk`, and friends). It also provides a `check` action (currently as a Nagios plugin in both active and passive modes) which monitors the health of the array and its components and reports it accordingly (this is not yet configurable).
|
4
|
+
|
5
|
+
# SYNOPSIS
|
6
|
+
|
7
|
+
elesai [global-options] <action> [action-options] [<component>]
|
8
|
+
|
9
|
+
where:
|
10
|
+
|
11
|
+
* `<action>` is one of `show` or `check`
|
12
|
+
* `<component>` is one of `virtualdrive` (or `vd`) or `physicaldrive` (or `pd`) (for `show` action)
|
13
|
+
|
14
|
+
Global options include:
|
15
|
+
|
16
|
+
* `-d`, `--debug`: enable *debug* mode
|
17
|
+
* `-f`, `--fake DIRECTORY`: specifies path to directory containing output of MegaCli invocations:
|
18
|
+
* `ldlist_aall`: output from `MegaCli -pdlist -aall`
|
19
|
+
* `ldpdinfo_aall`: output from `MegaCli -ldpdinfo -aall`
|
20
|
+
* `-m`, `--megacli MEGACLI`: path to `MegaCli` binary (if noth in `$PATH`)
|
21
|
+
* `-a`, `--about`: display general information about `elesai`
|
22
|
+
* `-V`, `--version`: display `elesai`'s version
|
23
|
+
|
24
|
+
The `<check>` can have options specific to itself:
|
25
|
+
|
26
|
+
* `-M`, `--monitor MONITOR`: Monitoring system (default: `nagios`)
|
27
|
+
* `-m`, `--mode [active|passive]`: Monitoring mode
|
28
|
+
* `-H`, `--nsca_hostname HOSTNAME`: NSCA hostname to send passive checks
|
29
|
+
* `-c`, `--config CONFIG`: Path to Senedsa (send_nsca) configuration
|
30
|
+
* `-S`, `--svc_descr SVC_DESCR`: Nagios service description
|
31
|
+
|
32
|
+
*Elesai* uses [Senedsa](https://rubygems.org/gems/senedsa "Senedsa") for Nagios passive check submission, which can use a configuration file to set options.
|
33
|
+
|
34
|
+
# Invocations
|
35
|
+
|
36
|
+
## Normal Invocation
|
37
|
+
|
38
|
+
root@boxie:~# elesai show pd | sort -k 4
|
39
|
+
[PD] e253s3 27 online:spunup 1.82TB HDD SATA 0 0 9WM1B7VDST32000644NS SN11
|
40
|
+
[PD] e252s7 23 online:spunup 1.82TB HDD SATA 0 0 9WM1FX1LST32000644NS SN11
|
41
|
+
[PD] e253s0 24 online:spunup 1.82TB HDD SATA 0 0 9WM1GF2NST32000644NS SN11
|
42
|
+
[PD] e253s1 25 online:spunup 1.82TB HDD SATA 0 0 9WM1GY85ST32000644NS SN11
|
43
|
+
[PD] e252s6 22 online:spunup 1.82TB HDD SATA 0 0 9WM1GYKJST32000644NS SN11
|
44
|
+
[PD] e253s2 26 online:spunup 1.82TB HDD SATA 0 0 9WM1HA0NST32000644NS SN11
|
45
|
+
[PD] e253s5 29 online:spunup 1.82TB HDD SATA 0 0 9WM7L834ST32000644NS SN12
|
46
|
+
[PD] e252s4 12 online:spunup 223.06GB SSD SATA 0 0 CVPR138405AQ300EGN INTEL SSDSA2CW300G3 4PC10362
|
47
|
+
[PD] e252s2 9 online:spunup 223.06GB SSD SATA 0 0 CVPR140100MQ300EGN INTEL SSDSA2CW300G3 4PC10362
|
48
|
+
[PD] e252s0 11 online:spunup 223.06GB SSD SATA 0 0 CVPR140201KZ300EGN INTEL SSDSA2CW300G3 4PC10362
|
49
|
+
[PD] e252s1 10 online:spunup 223.06GB SSD SATA 0 0 CVPR141300K9300EGN INTEL SSDSA2CW300G3 4PC10362
|
50
|
+
[PD] e252s3 8 online:spunup 223.06GB SSD SATA 0 0 CVPR141301KG300EGN INTEL SSDSA2CW300G3 4PC10362
|
51
|
+
[PD] e253s4 30 failed:spunup 1.82TB HDD SATA 0 0 9WM5Y4AEST32000644NS SN12
|
52
|
+
[PD] e252s5 13 hotspare:spunup 223.06GB SSD SATA 0 0 CVPR140100TT300EGN INTEL SSDSA2CW300G3 4PC10362
|
53
|
+
|
54
|
+
## Remote Invocation
|
55
|
+
|
56
|
+
Pipe output to `elesai`:
|
57
|
+
|
58
|
+
root@boxie:~# ssh server.example.com sudo MegaCli -pdlist -aall | elesai show pd
|
59
|
+
[PD] e252s0 16 online:spunup 278.46GB HDD SAS 0 0 a0 SEAGATE ST3300657SS 00066SJ01W4G
|
60
|
+
[PD] e252s1 17 online:spunup 278.46GB HDD SAS 0 0 a0 SEAGATE ST3300657SS 00066SJ01TTR
|
61
|
+
[PD] e252s2 20 online:spunup 278.46GB HDD SAS 0 0 a0 SEAGATE ST3300657SS 00066SJ02BCX
|
62
|
+
[PD] e252s3 21 online:spunup 278.46GB HDD SAS 0 0 a0 SEAGATE ST3300657SS 00066SJ025LP
|
63
|
+
[PD] e252s4 18 online:spunup 1.82TB HDD SATA 0 0 a0 9WM1G6MXST32000644NS SN11
|
64
|
+
[PD] e252s5 28 online:spunup 1.82TB HDD SATA 0 0 a0 9WM0FFYCST32000644NS SN11
|
65
|
+
[PD] e252s6 22 online:spunup 1.82TB HDD SATA 0 0 a0 9WM1GYKJST32000644NS SN11
|
66
|
+
[PD] e252s7 23 online:spunup 1.82TB HDD SATA 0 0 a0 9WM1FX1LST32000644NS SN11
|
67
|
+
[PD] e253s0 24 online:spunup 1.82TB HDD SATA 0 0 a0 9WM1GF2NST32000644NS SN11
|
68
|
+
[PD] e253s1 25 online:spunup 1.82TB HDD SATA 0 0 a0 9WM1GY85ST32000644NS SN11
|
69
|
+
[PD] e253s2 26 online:spunup 1.82TB HDD SATA 0 0 a0 9WM1HA0NST32000644NS SN11
|
70
|
+
[PD] e253s3 27 online:spunup 1.82TB HDD SATA 0 0 a0 9WM1B7VDST32000644NS SN11
|
71
|
+
[PD] e253s4 30 online:spunup 1.82TB HDD SATA 0 0 a0 9WM5Y4AEST32000644NS SN12
|
72
|
+
[PD] e253s5 29 online:spunup 1.82TB HDD SATA 0 0 a0 9WM7L834ST32000644NS SN12
|
73
|
+
[PD] e253s6 31 unconfigured(bad): 0.00KB HDD SAS 0 0 a0 SEAGATE ST33000650SS 0003Z29182VM
|
74
|
+
[PD] e253s7 32 unconfigured(bad): 0.00KB HDD SAS 0 0 a0 SEAGATE ST33000650SS 0003Z291A3QV
|
75
|
+
|
76
|
+
# STATUS
|
77
|
+
|
78
|
+
Very much in progress. The tool does not yet poke MegaCli itself.
|
79
|
+
|
80
|
+
|
81
|
+
|
data/bin/check_elesai
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
lib = File.expand_path(File.dirname(__FILE__) + '/../lib')
|
4
|
+
$LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
|
5
|
+
|
6
|
+
require 'socket'
|
7
|
+
require 'rubygems'
|
8
|
+
require 'elesai'
|
9
|
+
require 'elesai/cli'
|
10
|
+
|
11
|
+
module Elesai
|
12
|
+
|
13
|
+
ID = File.basename($PROGRAM_NAME).to_sym
|
14
|
+
|
15
|
+
app = CLI.new(ARGV)
|
16
|
+
app.run
|
17
|
+
|
18
|
+
end
|
data/bin/elesai
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
lib = File.expand_path(File.dirname(__FILE__) + '/../lib')
|
4
|
+
$LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
|
5
|
+
|
6
|
+
require 'socket'
|
7
|
+
require 'rubygems'
|
8
|
+
require 'elesai'
|
9
|
+
require 'elesai/cli'
|
10
|
+
|
11
|
+
module Elesai
|
12
|
+
|
13
|
+
ID = File.basename($PROGRAM_NAME).to_sym
|
14
|
+
|
15
|
+
app = CLI.new(ARGV)
|
16
|
+
app.run
|
17
|
+
|
18
|
+
end
|
data/lib/elesai/about.rb
ADDED
data/lib/elesai/cli.rb
ADDED
@@ -0,0 +1,203 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'senedsa'
|
3
|
+
include Senedsa
|
4
|
+
require 'elesai/version'
|
5
|
+
require 'elesai/about'
|
6
|
+
|
7
|
+
module Elesai
|
8
|
+
|
9
|
+
class CLI
|
10
|
+
|
11
|
+
COMMANDS = %w(show check)
|
12
|
+
COMPONENTS = %w(virtualdrive vd physicaldrive pd)
|
13
|
+
DEFAULT_CONFIG_FILE = File.join(ENV['HOME'],"/.senedsa/config")
|
14
|
+
|
15
|
+
def initialize(arguments)
|
16
|
+
@arguments = arguments
|
17
|
+
|
18
|
+
@global_options = { :debug => false, :megacli => 'MegaCli' }
|
19
|
+
@action_options = { :monitor => 'nagios', :mode => 'active' }
|
20
|
+
@action = nil
|
21
|
+
end
|
22
|
+
|
23
|
+
def run
|
24
|
+
begin
|
25
|
+
parsed_options?
|
26
|
+
|
27
|
+
@log = Elesai::Logger.instance.log
|
28
|
+
@log.level = Log4r::INFO unless @global_options[:debug]
|
29
|
+
|
30
|
+
config_options?
|
31
|
+
arguments_valid?
|
32
|
+
options_valid?
|
33
|
+
process_options
|
34
|
+
process_arguments
|
35
|
+
process_command
|
36
|
+
|
37
|
+
rescue => e #ArgumentError, OptionParser::MissingArgument, Senedsa::SendNsca::ConfigurationError => e
|
38
|
+
output_message e.message, 1
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
protected
|
43
|
+
|
44
|
+
def parsed_options?
|
45
|
+
opts = OptionParser.new
|
46
|
+
|
47
|
+
opts.banner = "Usage: #{ID} [options] <action> [options]"
|
48
|
+
opts.separator ""
|
49
|
+
opts.separator "Actions:"
|
50
|
+
opts.separator " show Displays component information"
|
51
|
+
opts.separator " check Performs health checks"
|
52
|
+
opts.separator ""
|
53
|
+
opts.separator "General options:"
|
54
|
+
opts.on('-m', '--megacli MEGACLI', String, "Path to MegaCli binary") { |megacli| @global_options[:megacli] = megacli }
|
55
|
+
opts.on('-f', '--fake DIRECTORY', String, "Path to directory with Megacli output") { |dir| @global_options[:fake] = dir }
|
56
|
+
opts.on('-d', '--debug', "Enable debug mode") { @global_options[:debug] = true}
|
57
|
+
opts.on('-a', '--about', "Display #{ID} information") { output_message ABOUT, 0 }
|
58
|
+
opts.on('-V', '--version', "Display #{ID} version") { output_message VERSION, 0 }
|
59
|
+
opts.on_tail('--help', "Show this message") { @global_options[:HELP] = true }
|
60
|
+
|
61
|
+
actions = {
|
62
|
+
:show => OptionParser.new do |aopts|
|
63
|
+
aopts.banner = "Usage: #{ID} [options] show <component>"
|
64
|
+
aopts.separator ""
|
65
|
+
aopts.separator " <component> is physicaldisk|pd, virtualdisk|vd"
|
66
|
+
end,
|
67
|
+
:check => OptionParser.new do |aopts|
|
68
|
+
aopts.banner = "Usage: #{ID} [options] check [check_options]"
|
69
|
+
aopts.separator ""
|
70
|
+
aopts.separator "Check Options"
|
71
|
+
aopts.on('-M', '--monitor [nagios]', [:nagios], "Monitoring system") { |monitor| @action_options[:monitor] = monitor }
|
72
|
+
aopts.on('-m', '--mode [active|passive]', [:active, :passive], "Monitoring mode") { |mode| @action_options[:mode] = mode }
|
73
|
+
aopts.on('-H', '--nsca_hostname HOSTNAME', String, "NSCA hostname to send passive checks") { |nsca_hostname| @action_options[:nsca_hostame] = nsca_hostname }
|
74
|
+
aopts.on('-c', '--config CONFIG', String, "Path to Senedsa (send_nsca) configuration" ) { |config| @action_options[:senedsa_config] = config }
|
75
|
+
aopts.on('-S', '--svc_descr SVC_DESR', String, "Nagios service description") { |svc_descr| @action_options[:svc_descr] = svc_descr }
|
76
|
+
end
|
77
|
+
}
|
78
|
+
|
79
|
+
opts.order!
|
80
|
+
output_message opts, 0 if @arguments.size == 0 or @global_options[:HELP]
|
81
|
+
|
82
|
+
@action = ARGV.shift.to_sym
|
83
|
+
actions[@action].order!
|
84
|
+
end
|
85
|
+
|
86
|
+
def config_options?
|
87
|
+
cfg_file = nil
|
88
|
+
cfg_file = @action_options[:senedsa_config] unless @action_options[:senedsa_config].nil?
|
89
|
+
cfg_file = DEFAULT_CONFIG_FILE if @action_options[:senedsa_config].nil? and File.readable? DEFAULT_CONFIG_FILE
|
90
|
+
|
91
|
+
unless cfg_file.nil?
|
92
|
+
@action_options.merge!(Senedsa::SendNsca.configure(cfg_file))
|
93
|
+
@action_options[:senedsa_config] = cfg_file
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def arguments_valid?
|
98
|
+
true
|
99
|
+
end
|
100
|
+
|
101
|
+
def options_valid?
|
102
|
+
case @action
|
103
|
+
when :check
|
104
|
+
raise OptionParser::MissingArgument, "NSCA hostname (-H) must be specified" if @action_options[:nsca_hostname].nil?
|
105
|
+
raise OptionParser::MissingArgument, "service description (-S) must be specified" if @action_options[:svc_descr].nil?
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def process_options
|
110
|
+
true
|
111
|
+
end
|
112
|
+
|
113
|
+
def process_arguments
|
114
|
+
@action_options[:hint] = @arguments[0].nil? ? nil : @arguments[0].to_sym
|
115
|
+
true
|
116
|
+
end
|
117
|
+
|
118
|
+
def process_command
|
119
|
+
|
120
|
+
@lsi = LSIArray.new(:megacli => @global_options[:megacli], :fake => @global_options[:fake], :hint => @action_options[:hint])
|
121
|
+
|
122
|
+
case @action
|
123
|
+
when :show then run_show
|
124
|
+
when :check then run_check
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
128
|
+
|
129
|
+
def run_show
|
130
|
+
|
131
|
+
raise ArgumentError, "missing component" if @arguments.size == 0
|
132
|
+
component = @arguments[0]
|
133
|
+
|
134
|
+
case component
|
135
|
+
when 'virtualdrive', 'vd'
|
136
|
+
@lsi.virtualdrives.each do |virtualdrive|
|
137
|
+
print "#{virtualdrive}\n"
|
138
|
+
end
|
139
|
+
when 'physicaldrive', 'pd'
|
140
|
+
@lsi.physicaldrives.each do |id,physicaldrive|
|
141
|
+
print "#{physicaldrive}\n"
|
142
|
+
end
|
143
|
+
else
|
144
|
+
raise ArgumentError, "invalid component #{component}"
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def run_check
|
149
|
+
|
150
|
+
plugin_output = ""
|
151
|
+
plugin_status = ""
|
152
|
+
|
153
|
+
@lsi.physicaldrives.each do |id,physicaldrive|
|
154
|
+
drive_plugin_string = "[PD:#{physicaldrive._id}:#{physicaldrive[:size]}:#{physicaldrive[:mediatype]}:#{physicaldrive[:pdtype]}]"
|
155
|
+
unless physicaldrive[:firmwarestate].state == :online or physicaldrive[:firmwarestate].state == :hotspare
|
156
|
+
plugin_output += " #{drive_plugin_string}:#{physicaldrive[:firmwarestate].state}"
|
157
|
+
plugin_status = :warning if plugin_status.empty?
|
158
|
+
end
|
159
|
+
unless physicaldrive[:mediaerrorcount].to_i == 0
|
160
|
+
plugin_output += " #{drive_plugin_string}:me:#{physicaldrive[:mediaerrorcount]}"
|
161
|
+
plugin_status = :warning if plugin_status.empty?
|
162
|
+
end
|
163
|
+
unless physicaldrive[:predictivefailurecount].to_i == 0
|
164
|
+
plugin_output += " #{drive_plugin_string}:pf:#{physicaldrive[:predictivefailurecount]}"
|
165
|
+
plugin_status = :warning if plugin_status.empty?
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
plugin_output = "no RAID subsystems errors found" if plugin_output.empty? and plugin_status.empty?
|
170
|
+
plugin_status = :ok if plugin_status.empty?
|
171
|
+
|
172
|
+
case @action_options[:monitor]
|
173
|
+
when 'nagios'
|
174
|
+
case @action_options[:mode]
|
175
|
+
when 'active'
|
176
|
+
puts "#{plugin_status}: #{plugin_output}"
|
177
|
+
exit SendNsca::STATUS[plugin_status]
|
178
|
+
when 'passive'
|
179
|
+
sn = SendNsca.new Socket.gethostname,'raid/lsi'
|
180
|
+
sn.nsca_hostname = @command_opts[:nsca_hostname]
|
181
|
+
begin
|
182
|
+
sn.send plugin_status , plugin_output
|
183
|
+
rescue SendNsca::SendNscaError => e
|
184
|
+
$stderr.write "#{ME}: error: send_nsca failed: #{e.message}\n"
|
185
|
+
exit
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def output_message(message, exitstatus=nil)
|
192
|
+
m = (! exitstatus.nil? and exitstatus > 0) ? "%s: error: %s" % [ID, message] : message
|
193
|
+
$stderr.write "#{m}\n"
|
194
|
+
exit exitstatus unless exitstatus.nil?
|
195
|
+
end
|
196
|
+
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
|
201
|
+
|
202
|
+
|
203
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
require 'log4r'
|
3
|
+
|
4
|
+
module Elesai
|
5
|
+
|
6
|
+
class Logger
|
7
|
+
|
8
|
+
include Singleton
|
9
|
+
|
10
|
+
attr_reader :log
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@log = Log4r::Logger.new("elesai")
|
14
|
+
@log.add Log4r::StderrOutputter.new('console', :formatter => Log4r::PatternFormatter.new(:pattern => "%c [%l] %m"), :level => Log4r::DEBUG)
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
data/lib/elesai/lsi.rb
ADDED
@@ -0,0 +1,210 @@
|
|
1
|
+
module Elesai
|
2
|
+
|
3
|
+
class LSIArray
|
4
|
+
|
5
|
+
attr_reader :adapters, :virtualdrives, :physicaldrives, :enclosures
|
6
|
+
|
7
|
+
def initialize(opts)
|
8
|
+
@adapters = []
|
9
|
+
@virtualdrives = []
|
10
|
+
@physicaldrives = {}
|
11
|
+
@enclosures = []
|
12
|
+
@spans = []
|
13
|
+
|
14
|
+
case opts[:hint]
|
15
|
+
when :pd,:physicaldrive
|
16
|
+
PDlist_aAll.new.parse!(self,opts)
|
17
|
+
when :vd, :virtualdrive
|
18
|
+
puts "vd!"
|
19
|
+
LDPDinfo_aAll.new.parse!(self,opts)
|
20
|
+
else
|
21
|
+
PDlist_aAll.new.parse!(self,opts)
|
22
|
+
LDPDinfo_aAll.new.parse!(self,opts)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def add_adapter(a)
|
27
|
+
@adapters[a[:id]] = a if @adapters[a[:id]].nil?
|
28
|
+
end
|
29
|
+
|
30
|
+
def add_virtualdrive(vd)
|
31
|
+
@virtualdrives.push(vd)
|
32
|
+
end
|
33
|
+
|
34
|
+
def add_physicaldrive(pd)
|
35
|
+
@physicaldrives[pd._id] = pd if @physicaldrives[pd._id].nil?
|
36
|
+
@physicaldrives[pd._id]
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_s
|
40
|
+
lsiarrayout = "LSI Array\n"
|
41
|
+
@adapters.each do |adapter|
|
42
|
+
lsiarrayout += " adapter #{adapter.id}\n"
|
43
|
+
adapter.virtualdrives.each do |virtualdrive|
|
44
|
+
lsiarrayout += " +--+ #{virtualdrive.to_str}\n"
|
45
|
+
virtualdrive.physicaldrives.each do |id,physicaldrive|
|
46
|
+
lsiarrayout += " | |-- pd #{physicaldrive.to_str}\n"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
lsiarrayout
|
51
|
+
end
|
52
|
+
|
53
|
+
### Adapter
|
54
|
+
|
55
|
+
class Adapter < Hash
|
56
|
+
|
57
|
+
def initialize
|
58
|
+
self[:virtualdrives] = []
|
59
|
+
self[:physicaldrives] = {}
|
60
|
+
super
|
61
|
+
end
|
62
|
+
|
63
|
+
def _id
|
64
|
+
"#{self[:id]}"
|
65
|
+
end
|
66
|
+
|
67
|
+
def type
|
68
|
+
:adapter
|
69
|
+
end
|
70
|
+
|
71
|
+
def type_of?(type)
|
72
|
+
self.type == type
|
73
|
+
end
|
74
|
+
|
75
|
+
def inspect
|
76
|
+
"#{self.class}:#{self.__id__}"
|
77
|
+
end
|
78
|
+
|
79
|
+
def add_physicaldrive(pd)
|
80
|
+
self[:physicaldrives][pd._id] = pd unless self[:physicaldrives][pd._id].nil?
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
### Virtual Drive
|
86
|
+
|
87
|
+
class VirtualDrive < Hash
|
88
|
+
|
89
|
+
STATES = {
|
90
|
+
:optimal => 'Optimal',
|
91
|
+
:degraded => 'Degraded',
|
92
|
+
:partial_degraded => 'Partial Degraded',
|
93
|
+
:failed => 'Failed',
|
94
|
+
:offline => 'Offline'
|
95
|
+
}
|
96
|
+
|
97
|
+
class Size < Struct.new(:number, :unit)
|
98
|
+
def to_s ; "%8.2f%s" % [self.number,self.unit] end
|
99
|
+
end
|
100
|
+
class RaidLevel < Struct.new(:primary, :secondary)
|
101
|
+
def to_s ; "raid%s:raid%s" % [self.primary,self.secondary] end
|
102
|
+
end
|
103
|
+
|
104
|
+
def initialize
|
105
|
+
self[:physicaldrives] = []
|
106
|
+
end
|
107
|
+
|
108
|
+
def _id
|
109
|
+
self[:targetid]
|
110
|
+
end
|
111
|
+
|
112
|
+
def type
|
113
|
+
:virtualdrive
|
114
|
+
end
|
115
|
+
|
116
|
+
def type_of?(type)
|
117
|
+
self.type == type
|
118
|
+
end
|
119
|
+
|
120
|
+
def inspect
|
121
|
+
"#{self.class}:#{self.__id__}"
|
122
|
+
end
|
123
|
+
|
124
|
+
def add_physicaldrive(pd)
|
125
|
+
|
126
|
+
end
|
127
|
+
|
128
|
+
def to_s
|
129
|
+
"[VD] %4s %18s %s %s %d" % [ self._id, self[:state], self[:size], self[:raidlevel], self[:physicaldrives].size ]
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
|
134
|
+
### Physical Drive
|
135
|
+
|
136
|
+
class PhysicalDrive < Hash
|
137
|
+
|
138
|
+
STATES = {
|
139
|
+
:online => 'Online',
|
140
|
+
:unconfigured_good => 'Unconfigured(good)',
|
141
|
+
:hotspare => 'Hotspare',
|
142
|
+
:failed => 'Failed',
|
143
|
+
:rebuild => 'Rebuild',
|
144
|
+
:unconfigured_bad => 'Unconfigured(bad)',
|
145
|
+
:missing => 'Missing',
|
146
|
+
:offline => 'Offline'
|
147
|
+
}
|
148
|
+
|
149
|
+
SPINS = {
|
150
|
+
:spun_up => 'Spun up'
|
151
|
+
}
|
152
|
+
|
153
|
+
class Size < Struct.new(:number, :unit)
|
154
|
+
def to_s ; "%8.2f%s" % [self.number,self.unit] end
|
155
|
+
end
|
156
|
+
class FirmwareState < Struct.new(:state, :spin)
|
157
|
+
def to_s
|
158
|
+
"#{self.state}:#{self.spin}"
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def initialize
|
163
|
+
self[:_adapter] = nil
|
164
|
+
self[:_virtualdrives] = []
|
165
|
+
end
|
166
|
+
|
167
|
+
def _id
|
168
|
+
"e#{self[:enclosuredeviceid].to_s}s#{self[:slotnumber].to_s}".to_sym
|
169
|
+
end
|
170
|
+
|
171
|
+
def type
|
172
|
+
:physicaldrive
|
173
|
+
end
|
174
|
+
|
175
|
+
def type_of?(type)
|
176
|
+
self.type == type
|
177
|
+
end
|
178
|
+
|
179
|
+
def to_s
|
180
|
+
keys = [:deviceid, :firmwarestate, :coercedsize, :mediatype, :pdtype, :mediaerrorcount, :predictivefailurecount,:inquirydata]
|
181
|
+
#"[PD] %8s %4s %19s %8.2f%s %5s %5s %3d %3d %s" % [ self.id, @deviceid, "#{@state}:#{@spin}", @_size.number, @_size.unit, @mediatype, @pdtype, @mediaerrors, @predictivefailure, @inquirydata ]
|
182
|
+
"[PD] %8s %4s %19s %s %5s %5s %3d %3d a%s %s" % [ self._id, self[:deviceid], self[:firmwarestate], self[:coercedsize], self[:mediatype], self[:pdtype], self[:mediaerrorcount], self[:predictivefailurecount], self[:_adapter]._id, self[:inquirydata] ]
|
183
|
+
end
|
184
|
+
|
185
|
+
def inspect
|
186
|
+
"#{self.class}:#{self.__id__}"
|
187
|
+
end
|
188
|
+
|
189
|
+
def add_adapter(a)
|
190
|
+
self[:_adapter] = a
|
191
|
+
end
|
192
|
+
|
193
|
+
def get_adapter
|
194
|
+
self[:_adapter]
|
195
|
+
end
|
196
|
+
|
197
|
+
def add_virtualdrive(vd)
|
198
|
+
self[:_virtualdrives][vd._id] = vd if self[:_virtualdrives][vd._id].nil?
|
199
|
+
end
|
200
|
+
|
201
|
+
def get_virtualdrive(vd_id)
|
202
|
+
self[:_virtualdrives][vd_id]
|
203
|
+
end
|
204
|
+
|
205
|
+
def get_virtualdrives
|
206
|
+
self[:_virtualdrives]
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
@@ -0,0 +1,393 @@
|
|
1
|
+
require 'workflow'
|
2
|
+
require 'open3'
|
3
|
+
|
4
|
+
module Elesai
|
5
|
+
|
6
|
+
class Megacli
|
7
|
+
|
8
|
+
include Workflow
|
9
|
+
|
10
|
+
ADAPTER_RE = /^Adapter\s+#*(?<value>\d+)/
|
11
|
+
VIRTUALDRIVE_RE = /^Virtual\s+Drive:\s+\d+\s+\((?<key>Target\s+Id):\s+(?<value>\d+)\)/
|
12
|
+
SPAN_RE = /^Span:\s+(?<value>\d+)/
|
13
|
+
PHYSICALDRIVE_RE = /^(?<key>Enclosure\s+Device\s+ID):\s+(?<value>\d+)/
|
14
|
+
ATTRIBUTE_RE = /^(?<key>[A-Za-z0-9()\s#]+):(?<value>.*)/
|
15
|
+
EXIT_RE = /^Exit Code: /
|
16
|
+
|
17
|
+
### Context
|
18
|
+
|
19
|
+
class Context
|
20
|
+
|
21
|
+
def initialize(current_state,lsi)
|
22
|
+
current_state.meta[:context] = { :stack => [], :adapter => nil, :virtualdrive => nil, :physicaldrive => nil }
|
23
|
+
@context = current_state.meta[:context]
|
24
|
+
@lsi = lsi
|
25
|
+
@log = Elesai::Logger.instance.log
|
26
|
+
end
|
27
|
+
|
28
|
+
def open(component)
|
29
|
+
@log.debug " * Open #{component.inspect}"
|
30
|
+
@context[:stack].push(component)
|
31
|
+
@context[component.type] = component
|
32
|
+
@log.debug " + context: #{@context[:stack]}"
|
33
|
+
end
|
34
|
+
|
35
|
+
def flash!(new_state)
|
36
|
+
new_state.meta[:context] = @context
|
37
|
+
@context = nil
|
38
|
+
@context = new_state.meta[:context]
|
39
|
+
@log.debug " + Flash context: #{@context[:stack]}"
|
40
|
+
end
|
41
|
+
|
42
|
+
def close
|
43
|
+
component = @context[:stack].pop
|
44
|
+
@context[component.type] = nil
|
45
|
+
@log.debug " * Close #{component.inspect}"
|
46
|
+
if component.type_of? :physicaldrive
|
47
|
+
pd = @lsi.add_physicaldrive(component)
|
48
|
+
pd.add_adapter(adapter)
|
49
|
+
pd.add_virtualdrive(virtualdrive) unless virtualdrive.nil?
|
50
|
+
adapter.add_physicaldrive(pd)
|
51
|
+
elsif component.type_of? :virtualdrive
|
52
|
+
vd = @lsi.add_virtualdrive(component)
|
53
|
+
elsif component.type_of? :adapter
|
54
|
+
@lsi.add_adapter(component)
|
55
|
+
end
|
56
|
+
@log.debug " + context: #{@context[:stack]}"
|
57
|
+
end
|
58
|
+
|
59
|
+
def current
|
60
|
+
@context[:stack][-1]
|
61
|
+
end
|
62
|
+
|
63
|
+
def adapter
|
64
|
+
@context[:adapter]
|
65
|
+
end
|
66
|
+
|
67
|
+
def virtualdrive
|
68
|
+
@context[:virtualdrive]
|
69
|
+
end
|
70
|
+
|
71
|
+
def physicaldrive
|
72
|
+
@context[:physicaldrive]
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
### State Machine Handlers
|
78
|
+
|
79
|
+
# Start
|
80
|
+
|
81
|
+
def on_start_exit(new_state, event, *args)
|
82
|
+
@log.debug " [#{current_state}]: on_exit : #{event} -> #{new_state}; args: #{args}"
|
83
|
+
@context = Context.new(current_state,@lsi)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Adapter
|
87
|
+
|
88
|
+
def adapter_line(adapter,key,value)
|
89
|
+
@log.debug " [#{current_state}] event adapter_line: new #{adapter.inspect}"
|
90
|
+
adapter[key.to_sym] = value.to_i
|
91
|
+
end
|
92
|
+
|
93
|
+
def on_adapter_entry(old_state, event, *args)
|
94
|
+
@log.debug " [#{current_state}] on_entry: leaving #{old_state}; args: #{args}"
|
95
|
+
|
96
|
+
@context.close unless @context.current.nil? or @context.current.type_of? :adapter
|
97
|
+
@context.open args[0]
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
def on_adapter_exit(new_state, event, *args)
|
102
|
+
@log.debug " [#{current_state}] on_exit: entering #{new_state}; args: #{args}"
|
103
|
+
@context.flash!(new_state)
|
104
|
+
end
|
105
|
+
|
106
|
+
# Virtual Drive
|
107
|
+
|
108
|
+
def virtualdrive_line(virtualdrive,key,value)
|
109
|
+
@log.debug " [#{current_state}] event: virtualdrive_line: new #{virtualdrive.inspect}"
|
110
|
+
virtualdrive[key.to_sym] = value.to_i
|
111
|
+
end
|
112
|
+
|
113
|
+
def on_virtualdrive_entry(old_state, event, *args)
|
114
|
+
@log.debug " [#{current_state}] on_entry: leaving #{old_state}; args: #{args}"
|
115
|
+
|
116
|
+
unless @context.current.nil?
|
117
|
+
if @context.current.type_of? :virtualdrive
|
118
|
+
@context.close
|
119
|
+
end
|
120
|
+
end
|
121
|
+
virtualdrive = args[0]
|
122
|
+
@context.open virtualdrive
|
123
|
+
end
|
124
|
+
|
125
|
+
def on_virtualdrive_exit(new_state, event, *args)
|
126
|
+
@log.debug " [#{current_state}] on_exit: entering #{new_state}; args: #{args}"
|
127
|
+
@context.flash!(new_state)
|
128
|
+
end
|
129
|
+
|
130
|
+
# Physical Drive
|
131
|
+
|
132
|
+
def physicaldrive_line(physicaldrive,key,value)
|
133
|
+
@log.debug " [#{current_state}] event: physicaldrive_line: new #{physicaldrive.inspect}"
|
134
|
+
physicaldrive[key.to_sym] = value.to_i
|
135
|
+
end
|
136
|
+
|
137
|
+
def on_physicaldrive_entry(old_state, event, *args)
|
138
|
+
@log.debug " [#{current_state}] on_entry: leaving #{old_state}; args: #{args}"
|
139
|
+
@context.open args[0]
|
140
|
+
end
|
141
|
+
|
142
|
+
def on_physicaldrive_exit(new_state, event, *args)
|
143
|
+
@log.debug " [#{current_state}] on_exit: entering #{new_state}; args: #{args}"
|
144
|
+
@context.flash!(new_state)
|
145
|
+
end
|
146
|
+
|
147
|
+
# Attribute
|
148
|
+
|
149
|
+
def attribute_line(key,value)
|
150
|
+
@log.debug " [#{current_state}] event: attribute_line: #{key} => #{value}"
|
151
|
+
end
|
152
|
+
|
153
|
+
def on_attribute_entry(old_state, event, *args)
|
154
|
+
@log.debug " [#{current_state}] entry: leaving #{old_state}; args: #{args}"
|
155
|
+
|
156
|
+
|
157
|
+
c = @context.current
|
158
|
+
k = args[0].to_sym
|
159
|
+
v = args[1]
|
160
|
+
|
161
|
+
# Some attributes require special treatment for our purposes
|
162
|
+
|
163
|
+
case k
|
164
|
+
when :coercedsize, :noncoercedsize, :rawsize, :size
|
165
|
+
m = /(?<number>[0-9\.]+)\s+(?<unit>[A-Z]+)/.match(v)
|
166
|
+
v = LSIArray::PhysicalDrive::Size.new(m[:number],m[:unit])
|
167
|
+
when :raidlevel
|
168
|
+
m = /Primary-(?<primary>\d+),\s+Secondary-(?<secondary>\d+)/.match(v)
|
169
|
+
v = LSIArray::VirtualDrive::RaidLevel.new(m[:primary],m[:secondary])
|
170
|
+
when :firmwarestate
|
171
|
+
st,sp = v.gsub(/\s/,'').split(/,/)
|
172
|
+
state = st.gsub(/\s/,'_').downcase.to_sym
|
173
|
+
spin = sp.gsub(/\s/,'_').downcase.to_sym unless sp.nil?
|
174
|
+
v = LSIArray::PhysicalDrive::FirmwareState.new(state,spin)
|
175
|
+
when :state
|
176
|
+
v = v.gsub(/\s/,'_').downcase.to_sym
|
177
|
+
when :mediatype
|
178
|
+
v = v.scan(/[A-Z]/).join
|
179
|
+
when :inquirydata
|
180
|
+
v = v.gsub(/\s+/,' ')
|
181
|
+
end
|
182
|
+
c[k] = v
|
183
|
+
end
|
184
|
+
|
185
|
+
def on_attribute_exit(new_state, event, *args)
|
186
|
+
@log.debug " [#{current_state}] exit: entering #{new_state} throught event #{event}; args: #{args}"
|
187
|
+
@context.close if @context.current.type_of? :physicaldrive and event != :attribute_line
|
188
|
+
|
189
|
+
@context.flash!(new_state)
|
190
|
+
end
|
191
|
+
|
192
|
+
# Exit
|
193
|
+
|
194
|
+
def exit_line
|
195
|
+
@log.debug " [#{current_state}] event: exit_line"
|
196
|
+
end
|
197
|
+
|
198
|
+
def on_exit_entry(new_state, event, *args)
|
199
|
+
@log.debug " [#{current_state}] exit: entering #{new_state} throught event #{event}; args: #{args}"
|
200
|
+
until @context.current.nil? do
|
201
|
+
@context.close
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
### Regular Expression Match Handlers
|
206
|
+
|
207
|
+
# Adapter
|
208
|
+
|
209
|
+
def adapter_match(match)
|
210
|
+
@log.debug "ADAPTER! #{match.string}"
|
211
|
+
key = 'id'
|
212
|
+
value = match[:value]
|
213
|
+
adapter_line!(LSIArray::Adapter.new,key,value)
|
214
|
+
end
|
215
|
+
|
216
|
+
# Virtual Drive
|
217
|
+
|
218
|
+
def virtualdrive_match(match)
|
219
|
+
@log.debug "VIRTUALDRIVE! #{match.string}"
|
220
|
+
key = match[:key].gsub(/\s+/,"").downcase
|
221
|
+
value = match[:value]
|
222
|
+
virtualdrive_line!(LSIArray::VirtualDrive.new,key,value)
|
223
|
+
end
|
224
|
+
|
225
|
+
# Physical Drive
|
226
|
+
|
227
|
+
def physicaldrive_match(match)
|
228
|
+
@log.debug "PHYSICALDRIVE! #{match.string}"
|
229
|
+
key = match[:key].gsub(/\s+/,"").downcase
|
230
|
+
value = match[:value]
|
231
|
+
physicaldrive_line!(LSIArray::PhysicalDrive.new,key,value)
|
232
|
+
end
|
233
|
+
|
234
|
+
# Attribute
|
235
|
+
|
236
|
+
def attribute_match(match)
|
237
|
+
@log.debug "ATTRIBUTE! #{match.string}"
|
238
|
+
key = match[:key].gsub(/\s+/,"").downcase
|
239
|
+
value = match[:value].strip
|
240
|
+
attribute_line!(key,value)
|
241
|
+
end
|
242
|
+
|
243
|
+
# Exit
|
244
|
+
|
245
|
+
def exit_match(match)
|
246
|
+
@log.debug "EXIT! #{match.string}"
|
247
|
+
exit_line!
|
248
|
+
end
|
249
|
+
|
250
|
+
### Parse!
|
251
|
+
|
252
|
+
def parse!(lsi,opts)
|
253
|
+
|
254
|
+
@lsi = lsi
|
255
|
+
@log = Elesai::Logger.instance.log
|
256
|
+
|
257
|
+
if STDIN.tty?
|
258
|
+
if opts[:fake].start_with? '-'
|
259
|
+
megacli = opts[:megacli].nil? ? "Megacli" : opts[:megacli]
|
260
|
+
command = "#{megacli} #{opts[:fake]}"
|
261
|
+
output = Open3.popen3(command) do |stdin, stdout, stderr, wait_thr|
|
262
|
+
stdin.close
|
263
|
+
raise RuntimeError, stderr.gets.chomp unless wait_thr.value.exitstatus == 0
|
264
|
+
stdout.gets
|
265
|
+
end
|
266
|
+
else
|
267
|
+
output = File.read(opts[:fake])
|
268
|
+
end
|
269
|
+
else
|
270
|
+
output = STDIN.read
|
271
|
+
end
|
272
|
+
|
273
|
+
output.each_line do |line|
|
274
|
+
line.strip!
|
275
|
+
next if line == ''
|
276
|
+
|
277
|
+
case line
|
278
|
+
when ADAPTER_RE then adapter_match(ADAPTER_RE.match(line))
|
279
|
+
when VIRTUALDRIVE_RE then virtualdrive_match(VIRTUALDRIVE_RE.match(line))
|
280
|
+
when PHYSICALDRIVE_RE then physicaldrive_match(PHYSICALDRIVE_RE.match(line))
|
281
|
+
when EXIT_RE then exit_match(EXIT_RE.match(line))
|
282
|
+
when ATTRIBUTE_RE then attribute_match(ATTRIBUTE_RE.match(line))
|
283
|
+
else raise StandardError, "cannot parse '#{line}'"
|
284
|
+
end
|
285
|
+
|
286
|
+
@log.debug "\n\n"
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
end
|
291
|
+
|
292
|
+
class PDlist_aAll < Megacli
|
293
|
+
|
294
|
+
def parse!(lsi,opts)
|
295
|
+
fake = opts[:fake].nil? ? "-pdlist -aall" : File.join(opts[:fake],"pdlist_aall")
|
296
|
+
super lsi, :fake => fake, :megacli => opts[:megacli]
|
297
|
+
end
|
298
|
+
|
299
|
+
workflow do
|
300
|
+
|
301
|
+
state :start do
|
302
|
+
event :adapter_line, :transitions_to => :adapter
|
303
|
+
event :exit_line, :transitions_to => :exit
|
304
|
+
end
|
305
|
+
|
306
|
+
state :adapter do
|
307
|
+
event :adapter_line, :transitions_to => :adapter # empty adapter
|
308
|
+
event :physicaldrive_line, :transitions_to => :physicaldrive
|
309
|
+
event :exit_line, :transitions_to => :exit
|
310
|
+
end
|
311
|
+
|
312
|
+
state :physicaldrive do
|
313
|
+
event :attribute_line, :transitions_to => :physicaldrive
|
314
|
+
event :exit_line, :transitions_to => :exit
|
315
|
+
event :adapter_line, :transitions_to => :adapter
|
316
|
+
event :physicaldrive_line, :transitions_to => :physicaldrive
|
317
|
+
event :attribute_line, :transitions_to => :attribute
|
318
|
+
end
|
319
|
+
|
320
|
+
state :attribute do
|
321
|
+
event :attribute_line, :transitions_to => :attribute
|
322
|
+
event :physicaldrive_line, :transitions_to => :physicaldrive
|
323
|
+
event :adapter_line, :transitions_to => :adapter
|
324
|
+
event :exit_line, :transitions_to => :exit
|
325
|
+
end
|
326
|
+
|
327
|
+
state :exit
|
328
|
+
|
329
|
+
on_transition do |from, to, triggering_event, *event_args|
|
330
|
+
#puts self.spec.states[to].class
|
331
|
+
# puts " transition: #{from} >> #{triggering_event}! >> #{to}: #{event_args.join(' ')}"
|
332
|
+
#puts " #{current_state.meta}"
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
end
|
337
|
+
|
338
|
+
class LDPDinfo_aAll < Megacli
|
339
|
+
|
340
|
+
def parse!(lsi,opts)
|
341
|
+
fake = opts[:fake].nil? ? "-ldpdinfo -aall" : File.join(opts[:fake],"ldpdinfo_aall")
|
342
|
+
super lsi, :fake => fake, :megacli => opts[:megacli]
|
343
|
+
end
|
344
|
+
|
345
|
+
|
346
|
+
workflow do
|
347
|
+
|
348
|
+
state :start do
|
349
|
+
event :adapter_line, :transitions_to => :adapter
|
350
|
+
event :exit_line, :transitions_to => :exit
|
351
|
+
end
|
352
|
+
|
353
|
+
state :adapter do
|
354
|
+
event :adapter_line, :transitions_to => :adapter
|
355
|
+
event :attribute_line, :transitions_to => :attribute
|
356
|
+
event :virtualdrive_line, :transitions_to => :virtualdrive
|
357
|
+
event :exit_line, :transitions_to => :exit
|
358
|
+
end
|
359
|
+
|
360
|
+
state :physicaldrive do
|
361
|
+
event :attribute_line, :transitions_to => :physicaldrive
|
362
|
+
event :exit_line, :transitions_to => :exit
|
363
|
+
event :adapter_line, :transitions_to => :adapter
|
364
|
+
event :physicaldrive_line, :transitions_to => :physicaldrive
|
365
|
+
event :attribute_line, :transitions_to => :attribute
|
366
|
+
end
|
367
|
+
|
368
|
+
state :virtualdrive do
|
369
|
+
event :physicaldrive_line, :transitions_to => :physicaldrive
|
370
|
+
event :attribute_line, :transitions_to => :attribute
|
371
|
+
end
|
372
|
+
|
373
|
+
state :attribute do
|
374
|
+
event :attribute_line, :transitions_to => :attribute
|
375
|
+
event :virtualdrive_line, :transitions_to => :virtualdrive
|
376
|
+
event :physicaldrive_line, :transitions_to => :physicaldrive
|
377
|
+
event :adapter_line, :transitions_to => :adapter
|
378
|
+
event :exit_line, :transitions_to => :exit
|
379
|
+
end
|
380
|
+
|
381
|
+
state :exit
|
382
|
+
|
383
|
+
on_transition do |from, to, triggering_event, *event_args|
|
384
|
+
#puts self.spec.states[to].class
|
385
|
+
# puts " transition: #{from} >> #{triggering_event}! >> #{to}: #{event_args.join(' ')}"
|
386
|
+
#puts " #{current_state.meta}"
|
387
|
+
end
|
388
|
+
end
|
389
|
+
|
390
|
+
end
|
391
|
+
|
392
|
+
|
393
|
+
end
|
data/lib/elesai.rb
ADDED
metadata
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: elesai
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.4.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Gerardo López-Fernádez
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-06-05 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: log4r
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.1.9
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 1.1.9
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: senedsa
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 0.1.0
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 0.1.0
|
46
|
+
description: Senedsa is a small utility and library wrapper for the Nagios send_nsca.
|
47
|
+
email: gerir@evernote.com
|
48
|
+
executables:
|
49
|
+
- elesai
|
50
|
+
- check_elesai
|
51
|
+
extensions: []
|
52
|
+
extra_rdoc_files: []
|
53
|
+
files:
|
54
|
+
- lib/elesai/about.rb
|
55
|
+
- lib/elesai/cli.rb
|
56
|
+
- lib/elesai/logger.rb
|
57
|
+
- lib/elesai/lsi.rb
|
58
|
+
- lib/elesai/megacli.rb
|
59
|
+
- lib/elesai/version.rb
|
60
|
+
- lib/elesai.rb
|
61
|
+
- bin/check_elesai
|
62
|
+
- bin/elesai
|
63
|
+
- LICENSE
|
64
|
+
- README.md
|
65
|
+
homepage: https://github.com/evernote/ops-elesai
|
66
|
+
licenses:
|
67
|
+
- Apache License, Version 2.0
|
68
|
+
post_install_message:
|
69
|
+
rdoc_options: []
|
70
|
+
require_paths:
|
71
|
+
- lib
|
72
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
79
|
+
none: false
|
80
|
+
requirements:
|
81
|
+
- - ! '>='
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: 1.3.5
|
84
|
+
requirements: []
|
85
|
+
rubyforge_project:
|
86
|
+
rubygems_version: 1.8.23
|
87
|
+
signing_key:
|
88
|
+
specification_version: 3
|
89
|
+
summary: Utility and library wrapper for Nagios send_nsca utility
|
90
|
+
test_files: []
|