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