nagi 0.1.1 → 0.2.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/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
|