nagi 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/NEWS +10 -0
- data/README.md +126 -8
- data/lib/nagi/dsl.rb +38 -5
- data/lib/nagi/status.rb +12 -0
- data/lib/nagi/version.rb +1 -1
- data/spec/nagi/dsl_spec.rb +72 -0
- data/spec/nagi/status_spec.rb +16 -0
- metadata +8 -7
data/NEWS
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
0.2.0: 2012-05-31
|
2
|
+
- Added support for collecting multiple statuses during check
|
3
|
+
|
4
|
+
0.1.1: 2012-05-30
|
5
|
+
- Made Utility::execute() work with Ruby 1.8
|
6
|
+
- Properly escape quotes in Utility::execute()
|
7
|
+
- Fixed syntax errors in README example
|
8
|
+
|
9
|
+
0.1.0: 2012-05-18
|
10
|
+
- Initial release
|
data/README.md
CHANGED
@@ -9,11 +9,50 @@ GNU GPL v3.
|
|
9
9
|
|
10
10
|
## Example
|
11
11
|
|
12
|
-
A
|
12
|
+
A very simple plugin looks like this:
|
13
13
|
|
14
14
|
```ruby
|
15
15
|
#!/usr/bin/env ruby
|
16
|
+
require 'nagi'
|
17
|
+
|
18
|
+
Nagi do
|
19
|
+
name 'check_true'
|
20
|
+
version '0.1'
|
21
|
+
prefix 'TRUE'
|
22
|
+
|
23
|
+
check do |args|
|
24
|
+
if true
|
25
|
+
ok 'True is still true'
|
26
|
+
else
|
27
|
+
critical 'Uh oh'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
```
|
32
|
+
|
33
|
+
The first few methods just set some basic info for the plugin - the name,
|
34
|
+
version, and a prefix for the check output. These are all optional.
|
35
|
+
|
36
|
+
Next, the check block is defined, which runs the actual Nagios check. `args`
|
37
|
+
will contains any options (see command-line parsing below), and the methods ok,
|
38
|
+
warning, critical, and unknown are used to return the check status. A missing
|
39
|
+
status or uncaught exception will result in an 'unknown' status.
|
16
40
|
|
41
|
+
The plugin is run as any regular script - save it as a file, make it executable
|
42
|
+
and run it:
|
43
|
+
|
44
|
+
```
|
45
|
+
$ ./test.rb
|
46
|
+
TRUE OK: True is still true
|
47
|
+
```
|
48
|
+
|
49
|
+
### Command-line options
|
50
|
+
|
51
|
+
Nagi can automatically parse command-line arguments when requested, and provide
|
52
|
+
them as input to the check block. Here is a simple example:
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
#!/usr/bin/env ruby
|
17
56
|
require 'nagi'
|
18
57
|
require 'resolv'
|
19
58
|
|
@@ -40,23 +79,99 @@ Nagi do
|
|
40
79
|
end
|
41
80
|
```
|
42
81
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
82
|
+
The `argument` method specifies a required positional argument, and only takes
|
83
|
+
the name of the argument. `switch` specifies an optional switch, which may or
|
84
|
+
may not take an argument of its own. The first `switch` parameter is its name,
|
85
|
+
while the rest are passed to the standard Ruby OptionParser.on method - see its
|
86
|
+
documentation for details.
|
87
|
+
|
88
|
+
The parsed arguments are passed to the `check` block via the `args` parameter,
|
89
|
+
which is a hash containing argument names and values. In the case of a switch
|
90
|
+
with no argument of its own, the value will be `true`.
|
91
|
+
|
92
|
+
A few usage examples of this plugin:
|
93
|
+
|
94
|
+
```
|
95
|
+
$ ./check_dns.rb
|
96
|
+
Error: Argument 'hostname' not given
|
97
|
+
|
98
|
+
Usage: ./check_dns.rb [options] <hostname>
|
99
|
+
-i, --ip IP Expected IP address
|
100
|
+
-h, --help Display this help message
|
101
|
+
-V, --version Display version information
|
102
|
+
|
103
|
+
$ ./check_dns.rb www.google.com
|
104
|
+
DNS OK: www.google.com resolves to 173.194.35.148
|
105
|
+
|
106
|
+
$ ./check_dns.rb -i 1.2.3.4 www.google.com
|
107
|
+
DNS CRITICAL: www.google.com resolves to 173.194.35.148, expected 1.2.3.4
|
108
|
+
```
|
109
|
+
|
110
|
+
Nagi will automatically set up the -h/--help and -V/--version switches and
|
111
|
+
handle them appropriately.
|
112
|
+
|
113
|
+
### Handling multiple statuses
|
114
|
+
|
115
|
+
In some cases it is useful to collect several statuses and return the most
|
116
|
+
severe, rather than returning the first status encountered. This is typically
|
117
|
+
used when monitoring multiple resources with a single check.
|
118
|
+
|
119
|
+
The `collect` setting tells Nagi to continue running the check when a status
|
120
|
+
is given. It can be set to either `:severe`, in which case the most severe
|
121
|
+
status will be returned at the end, or `:all` which will use the most severe
|
122
|
+
status code but also combine all status messages into one.
|
123
|
+
|
124
|
+
This example checks used space on all disk drives, and returns critical if
|
125
|
+
any of the drives are above a threshold:
|
126
|
+
|
127
|
+
```ruby
|
128
|
+
#!/usr/bin/env ruby
|
129
|
+
require 'nagi'
|
130
|
+
|
131
|
+
Nagi do
|
132
|
+
name 'check_df'
|
133
|
+
version '0.1'
|
134
|
+
prefix 'DF'
|
135
|
+
argument :percentage
|
136
|
+
collect :all
|
137
|
+
|
138
|
+
check do |args|
|
139
|
+
execute('df').lines.each do |line|
|
140
|
+
if line =~ /^.*?(\d+)%\s*(.*)$/
|
141
|
+
if $1.to_i > args[:percentage].to_i
|
142
|
+
critical "#{$2} #{$1}% used"
|
143
|
+
else
|
144
|
+
ok "#{$2} #{$1}% used"
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
```
|
151
|
+
|
152
|
+
When run, it will output something like this:
|
153
|
+
|
154
|
+
```
|
155
|
+
$ ./check_df.rb 90
|
156
|
+
DF OK: / 80% used, /Volumes/Media 57% used
|
47
157
|
|
48
|
-
|
49
|
-
|
158
|
+
$ ./check_df.rb 70
|
159
|
+
DF CRITICAL: / 80% used, /Volumes/Media 57% used
|
160
|
+
```
|
50
161
|
|
51
162
|
## Reference
|
52
163
|
|
53
|
-
###
|
164
|
+
### Plugin info
|
54
165
|
|
55
166
|
These describe the program, and are usually given first, if necessary.
|
56
167
|
|
57
168
|
* `name` *name*: the program name.
|
58
169
|
* `version` *version*: the program version.
|
59
170
|
* `prefix` *prefix*: a prefix for the check output.
|
171
|
+
* `collect` *type*: collect statuses and continue the check, rather than
|
172
|
+
returning. *type* can be either `:severe` or `:all` - `:severe` will
|
173
|
+
return the last, most severe status, and `:all` will use the most severe
|
174
|
+
status but join all the status messages together into one.
|
60
175
|
|
61
176
|
### Command-line arguments
|
62
177
|
|
@@ -77,6 +192,9 @@ parsed command-line arguments as a hash. It should use one of the methods `ok`,
|
|
77
192
|
`warning`, `critical`, or `unknown` to return a status. If no status is given,
|
78
193
|
or the block raises an unhandled exception, an Unknown status will be returned.
|
79
194
|
|
195
|
+
The `collect` setting (see above) can be used to collect statuses and continue
|
196
|
+
the check, rather than returning the first status encountered.
|
197
|
+
|
80
198
|
* `check` *block*: the code block the the check. Parsed command-line arguments
|
81
199
|
are passed as a hash.
|
82
200
|
* `ok` *message*: returns an OK status.
|
data/lib/nagi/dsl.rb
CHANGED
@@ -4,6 +4,8 @@ module Nagi
|
|
4
4
|
|
5
5
|
def initialize(&block)
|
6
6
|
@plugin = Nagi::Plugin.new
|
7
|
+
@collect = nil
|
8
|
+
@collected = []
|
7
9
|
instance_eval &block
|
8
10
|
end
|
9
11
|
|
@@ -12,14 +14,36 @@ module Nagi
|
|
12
14
|
end
|
13
15
|
|
14
16
|
def check(&block)
|
17
|
+
# make data available to block
|
18
|
+
collect = @collect
|
19
|
+
collected = @collected
|
20
|
+
|
15
21
|
p = class << @plugin; self; end
|
16
22
|
p.send(:define_method, :check) do |options|
|
17
|
-
|
23
|
+
status = catch(:status) {
|
24
|
+
block.call(options)
|
25
|
+
nil # to avoid returning status if not thrown
|
26
|
+
}
|
27
|
+
return status if status or not collect
|
28
|
+
return nil if collected.empty?
|
29
|
+
return collected.reverse.max if collect == :severe
|
30
|
+
return collected.reverse.max.class.new(
|
31
|
+
collected.map { |s| s.message }.join(', ')
|
32
|
+
) if collect == :all
|
33
|
+
return nil
|
18
34
|
end
|
19
35
|
end
|
20
36
|
|
37
|
+
def collect(type)
|
38
|
+
raise "Invalid collect type #{type.to_s}" unless [:all, :severe].include?(type)
|
39
|
+
@collect = type
|
40
|
+
end
|
41
|
+
|
21
42
|
def critical(message)
|
22
|
-
|
43
|
+
status = Nagi::Status::Critical.new(message)
|
44
|
+
throw :status, status unless @collect
|
45
|
+
@collected.push(status)
|
46
|
+
return status
|
23
47
|
end
|
24
48
|
|
25
49
|
def execute(command)
|
@@ -31,7 +55,10 @@ module Nagi
|
|
31
55
|
end
|
32
56
|
|
33
57
|
def ok(message)
|
34
|
-
|
58
|
+
status = Nagi::Status::OK.new(message)
|
59
|
+
throw :status, status unless @collect
|
60
|
+
@collected.push(status)
|
61
|
+
return status
|
35
62
|
end
|
36
63
|
|
37
64
|
def prefix(prefix)
|
@@ -43,7 +70,10 @@ module Nagi
|
|
43
70
|
end
|
44
71
|
|
45
72
|
def unknown(message)
|
46
|
-
|
73
|
+
status = Nagi::Status::Unknown.new(message)
|
74
|
+
throw :status, status unless @collect
|
75
|
+
@collected.push(status)
|
76
|
+
return status
|
47
77
|
end
|
48
78
|
|
49
79
|
def version(version)
|
@@ -51,7 +81,10 @@ module Nagi
|
|
51
81
|
end
|
52
82
|
|
53
83
|
def warning(message)
|
54
|
-
|
84
|
+
status = Nagi::Status::Warning.new(message)
|
85
|
+
throw :status, status unless @collect
|
86
|
+
@collected.push(status)
|
87
|
+
return status
|
55
88
|
end
|
56
89
|
end
|
57
90
|
end
|
data/lib/nagi/status.rb
CHANGED
@@ -1,9 +1,21 @@
|
|
1
1
|
module Nagi
|
2
2
|
module Status
|
3
3
|
class Status
|
4
|
+
include Comparable
|
4
5
|
attr_accessor :message
|
5
6
|
attr_reader :code, :name
|
6
7
|
|
8
|
+
def <=>(other)
|
9
|
+
if not other.is_a? Nagi::Status::Status
|
10
|
+
raise ArgumentError.new("comparison of Nagi::Status::Status with #{other.class} failed.")
|
11
|
+
end
|
12
|
+
|
13
|
+
# Make Unknown the least severe status
|
14
|
+
c = @code >= 3 ? -1 : @code
|
15
|
+
o = other.code >= 3 ? -1 : other.code
|
16
|
+
return c <=> o
|
17
|
+
end
|
18
|
+
|
7
19
|
def initialize(code, name, message)
|
8
20
|
@code = code
|
9
21
|
@name = name
|
data/lib/nagi/version.rb
CHANGED
data/spec/nagi/dsl_spec.rb
CHANGED
@@ -38,6 +38,58 @@ describe Nagi::DSL do
|
|
38
38
|
end
|
39
39
|
@dsl.plugin.check({}).should eq 'status'
|
40
40
|
end
|
41
|
+
|
42
|
+
it 'catches throwns :status and returns payload, even when collect is set' do
|
43
|
+
@dsl.collect(:all)
|
44
|
+
@dsl.check do |options|
|
45
|
+
throw :status, 'status'
|
46
|
+
end
|
47
|
+
@dsl.plugin.check({}).should eq 'status'
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'returns first, most severe status for collect :severe' do
|
51
|
+
@dsl.collect(:severe)
|
52
|
+
@dsl.check do |o|
|
53
|
+
@dsl.warning 'w1'
|
54
|
+
@dsl.critical 'c1'
|
55
|
+
@dsl.ok 'o1'
|
56
|
+
@dsl.critical 'c2'
|
57
|
+
@dsl.ok 'o2'
|
58
|
+
end
|
59
|
+
|
60
|
+
status = @dsl.plugin.check({})
|
61
|
+
status.class.should eq Nagi::Status::Critical
|
62
|
+
status.message.should eq 'c2'
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'returns most severe status with all messages joined for collect :all' do
|
66
|
+
@dsl.collect(:all)
|
67
|
+
@dsl.check do |o|
|
68
|
+
@dsl.warning 'w1'
|
69
|
+
@dsl.critical 'c1'
|
70
|
+
@dsl.ok 'o1'
|
71
|
+
@dsl.critical 'c2'
|
72
|
+
@dsl.ok 'o2'
|
73
|
+
end
|
74
|
+
|
75
|
+
status = @dsl.plugin.check({})
|
76
|
+
status.class.should eq Nagi::Status::Critical
|
77
|
+
status.message.should eq 'w1, c1, o1, c2, o2'
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe '.collect' do
|
82
|
+
it 'accepts :all' do
|
83
|
+
@dsl.collect(:all)
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'accepts :severe' do
|
87
|
+
@dsl.collect(:severe)
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'raises exception on other value' do
|
91
|
+
lambda { @dsl.collect(:dummy) }.should raise_error
|
92
|
+
end
|
41
93
|
end
|
42
94
|
|
43
95
|
describe '.critical' do
|
@@ -46,6 +98,11 @@ describe Nagi::DSL do
|
|
46
98
|
@dsl.critical('message')
|
47
99
|
end.class.should eq Nagi::Status::Critical
|
48
100
|
end
|
101
|
+
|
102
|
+
it 'returns status if collection is enabled' do
|
103
|
+
@dsl.collect(:all)
|
104
|
+
@dsl.critical('message').class.should eq Nagi::Status::Critical
|
105
|
+
end
|
49
106
|
end
|
50
107
|
|
51
108
|
describe '.execute' do
|
@@ -67,6 +124,11 @@ describe Nagi::DSL do
|
|
67
124
|
@dsl.ok('message')
|
68
125
|
end.class.should eq Nagi::Status::OK
|
69
126
|
end
|
127
|
+
|
128
|
+
it 'returns status if collection is enabled' do
|
129
|
+
@dsl.collect(:all)
|
130
|
+
@dsl.ok('message').class.should eq Nagi::Status::OK
|
131
|
+
end
|
70
132
|
end
|
71
133
|
|
72
134
|
describe '.prefix' do
|
@@ -90,6 +152,11 @@ describe Nagi::DSL do
|
|
90
152
|
@dsl.unknown('message')
|
91
153
|
end.class.should eq Nagi::Status::Unknown
|
92
154
|
end
|
155
|
+
|
156
|
+
it 'returns status if collection is enabled' do
|
157
|
+
@dsl.collect(:all)
|
158
|
+
@dsl.unknown('message').class.should eq Nagi::Status::Unknown
|
159
|
+
end
|
93
160
|
end
|
94
161
|
|
95
162
|
describe '.version' do
|
@@ -105,5 +172,10 @@ describe Nagi::DSL do
|
|
105
172
|
@dsl.warning('message')
|
106
173
|
end.class.should eq Nagi::Status::Warning
|
107
174
|
end
|
175
|
+
|
176
|
+
it 'returns status if collection is enabled' do
|
177
|
+
@dsl.collect(:all)
|
178
|
+
@dsl.warning('message').class.should eq Nagi::Status::Warning
|
179
|
+
end
|
108
180
|
end
|
109
181
|
end
|
data/spec/nagi/status_spec.rb
CHANGED
@@ -5,6 +5,22 @@ describe Nagi::Status::Status do
|
|
5
5
|
@status = Nagi::Status::Status.new(0, 'name', 'message')
|
6
6
|
end
|
7
7
|
|
8
|
+
describe '<=>' do
|
9
|
+
it 'compares status code' do
|
10
|
+
(@status <=> Nagi::Status::Status.new(1, 'n', 'm')).should eq -1
|
11
|
+
(@status <=> Nagi::Status::Status.new(0, 'n', 'm')).should eq 0
|
12
|
+
(@status <=> Nagi::Status::Status.new(-1, 'n', 'm')).should eq 1
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'compares Unknown (3) as less severe than Ok (0)' do
|
16
|
+
(@status <=> Nagi::Status::Status.new(3, 'n', 'm')).should eq 1
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'raises ArgumentError if compared with non-Status object' do
|
20
|
+
lambda { @status <=> 1 }.should raise_error ArgumentError
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
8
24
|
describe '.code' do
|
9
25
|
it 'contains status code' do
|
10
26
|
@status.code.should eq 0
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nagi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-05-
|
12
|
+
date: 2012-05-31 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
16
|
-
requirement: &
|
16
|
+
requirement: &70342039915540 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70342039915540
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: rspec
|
27
|
-
requirement: &
|
27
|
+
requirement: &70342039911220 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,7 +32,7 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70342039911220
|
36
36
|
description: A DSL for writing Nagios plugins
|
37
37
|
email:
|
38
38
|
- erik@bengler.no
|
@@ -42,6 +42,7 @@ extra_rdoc_files: []
|
|
42
42
|
files:
|
43
43
|
- .gitignore
|
44
44
|
- Gemfile
|
45
|
+
- NEWS
|
45
46
|
- README.md
|
46
47
|
- Rakefile
|
47
48
|
- lib/nagi.rb
|
@@ -78,7 +79,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
78
79
|
version: '0'
|
79
80
|
requirements: []
|
80
81
|
rubyforge_project:
|
81
|
-
rubygems_version: 1.8.
|
82
|
+
rubygems_version: 1.8.10
|
82
83
|
signing_key:
|
83
84
|
specification_version: 3
|
84
85
|
summary: A DSL for writing Nagios plugins
|