line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Nagios::Plugin::SNMP; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
=pod |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
=head1 NAME |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
Nagios::Plugin::SNMP - Helper module to make writing SNMP-based plugins for Nagios easier. |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
=head1 SYNOPSIS |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
This module requires Net::SNMP for its' SNMP functionality; it |
12
|
|
|
|
|
|
|
subclasses Nagios::Plugin. |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
It includes routines to do the following: |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
=head2 Parse and process common SNMP arguments: |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
* --warning|-w: Warning threshold [optional] |
19
|
|
|
|
|
|
|
* --critical|-c: Warning threshold [optional] |
20
|
|
|
|
|
|
|
* --hostname|-H: SNMP device to query |
21
|
|
|
|
|
|
|
* --alt-host|--ah: Additional SNMP devices to query |
22
|
|
|
|
|
|
|
* --port|-p: Port on remote device to connect to [default 161] |
23
|
|
|
|
|
|
|
* --snmp-local-ip: Local IP to bind to for outgoing requests |
24
|
|
|
|
|
|
|
* --snmp-version: SNMP version (1, 2c, 3) |
25
|
|
|
|
|
|
|
* --snmp-timeout: Connect timeout in seconds [default 0] |
26
|
|
|
|
|
|
|
* --snmp-debug: Turn on Net::SNMP debugging |
27
|
|
|
|
|
|
|
* --snmp-max-msg-size N: Set maximum SNMP message size in bytes |
28
|
|
|
|
|
|
|
* --rocommunity: Read-only community string for SNMP 1/v2c |
29
|
|
|
|
|
|
|
* --auth-username: Auth username for SNMP v3 |
30
|
|
|
|
|
|
|
* --auth-password: Auth password for SNMP v3 |
31
|
|
|
|
|
|
|
* --auth-protocol: Auth protocol for SNMP v3 (defaults to md5) |
32
|
|
|
|
|
|
|
* Connect to an SNMP device |
33
|
|
|
|
|
|
|
* Perform a get() or walk() request, each method does 'the right |
34
|
|
|
|
|
|
|
thing' based on the version of SNMP selected by the user. |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
=head2 noSuchObject and noSuchInstance behavior |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
You can configure your plugin to automatically exit with UNKNOWN if |
39
|
|
|
|
|
|
|
OIDs you query for on the remote agent do not exist on the agent by |
40
|
|
|
|
|
|
|
setting 'error_on_no_such' to 1 in new(). If you set it to 0 or |
41
|
|
|
|
|
|
|
do not specify the option at all, then results from get() and walk() |
42
|
|
|
|
|
|
|
will have the string tokens noSuchObject or noSuchInstance as values |
43
|
|
|
|
|
|
|
for OIDs that the agent does not support. |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
This option exists as some scripts will query agents for MIBs that have |
46
|
|
|
|
|
|
|
*some* tables or scalars that may or may not be supported depending on the |
47
|
|
|
|
|
|
|
remote agent version, operating system, etc. In this case |
48
|
|
|
|
|
|
|
it is nice to just parse the returns from get() and walk() to programatically |
49
|
|
|
|
|
|
|
determine if support exists. |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
In other cases the script may be querying a |
52
|
|
|
|
|
|
|
clustered application with multiple agents where only one agent will respond |
53
|
|
|
|
|
|
|
with the OIDs queried; the others will return noSuch* values .. for this case |
54
|
|
|
|
|
|
|
having the script exit with error if *none* of the agents return valid values |
55
|
|
|
|
|
|
|
makes sense at least one agent MUST have the OIDs for the check to |
56
|
|
|
|
|
|
|
succeed and all agents are known to respond to the OIDs in question |
57
|
|
|
|
|
|
|
(set error_on_no_such to 1). |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
my $plugin = Nagios::Plugin::SNMP->new( |
60
|
|
|
|
|
|
|
'error_on_no_such' => 1 |
61
|
|
|
|
|
|
|
); |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
=head2 Handle deltas for counters |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
my $plugin = Nagios::Plugin::SNMP->new( |
66
|
|
|
|
|
|
|
'process_deltas' => { |
67
|
|
|
|
|
|
|
'cache' => { 'type' => 'memcache' }, |
68
|
|
|
|
|
|
|
'default_interval' => 300, # seconds |
69
|
|
|
|
|
|
|
'delta_compute_function' => \&my_delta_function |
70
|
|
|
|
|
|
|
} |
71
|
|
|
|
|
|
|
); |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
Will use any Cache::Cache compliant data cache to store counter |
74
|
|
|
|
|
|
|
values and return deltas between counters to the end user. In order |
75
|
|
|
|
|
|
|
to do this, Nagios::Plugin::SNMP must be passed in a cache class name. |
76
|
|
|
|
|
|
|
Each cache type will cause Nagios::Plugin::SNMP to add required |
77
|
|
|
|
|
|
|
arguments for the cache type in question. Additionally, enabling |
78
|
|
|
|
|
|
|
counter delta code causes the script to require an interval for |
79
|
|
|
|
|
|
|
time between checks. From the command line this can be specified |
80
|
|
|
|
|
|
|
with --check-interval. The developer can pass in a default using |
81
|
|
|
|
|
|
|
the 'default_interval' parameter. |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
* cache => { 'type' => 'memcache' } |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
Causes Nagios::Plugin::SNMP to use Cache::Memcached for |
86
|
|
|
|
|
|
|
data persistence; also makes the following arguments to the plugin |
87
|
|
|
|
|
|
|
available to the user. Defaults for both can be provided in the |
88
|
|
|
|
|
|
|
memcache option hash as memcache_addr and memcache_port. |
89
|
|
|
|
|
|
|
* --memcache-addr - IP address of Memcache instance |
90
|
|
|
|
|
|
|
* --memcache-port - TCP port number memcache is listening on |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
my $plugin = Nagios::Plugin::SNMP->new( |
93
|
|
|
|
|
|
|
'process_deltas' => { |
94
|
|
|
|
|
|
|
'cache' => { |
95
|
|
|
|
|
|
|
'type' => 'memcache', |
96
|
|
|
|
|
|
|
'options => { 'memcache_port' => 11211, |
97
|
|
|
|
|
|
|
'memcache_addr' => 127.0.0.1 |
98
|
|
|
|
|
|
|
}, |
99
|
|
|
|
|
|
|
'default_interval' => 300, # seconds |
100
|
|
|
|
|
|
|
'delta_compute_function' => \&my_delta_function |
101
|
|
|
|
|
|
|
} |
102
|
|
|
|
|
|
|
); |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
Currently ONLY Cache::Memcached is supported. |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
* 'process_deltas' => { 'delta_compute_function' => \&my_delta_function } |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
Callback to allow user to pass in a function that will compute a |
109
|
|
|
|
|
|
|
smarter delta :). The default function purely does the following: |
110
|
|
|
|
|
|
|
* If previous value does not exist, it stores the current value |
111
|
|
|
|
|
|
|
and returns -0 |
112
|
|
|
|
|
|
|
* If previous value exists and is > 0, stores the current value and |
113
|
|
|
|
|
|
|
returns the delta between the current value and previous value |
114
|
|
|
|
|
|
|
* If delta between the two values is < 0, returns -0 as counter |
115
|
|
|
|
|
|
|
has wrapped. |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
sub my_delta_function { |
118
|
|
|
|
|
|
|
my ($self, $args) = @_; |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
my $previous_value = $args->{'previous_value'}; |
121
|
|
|
|
|
|
|
my $current_value = $args->{'current_value'}; |
122
|
|
|
|
|
|
|
my $interval = $args->{'interval'}; |
123
|
|
|
|
|
|
|
my $previous_run_at = $args->{'previous_run_at'}; |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
my ($value_to_store, $delta) = (); |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
# Processing code |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
return ($value_to_store, $delta); |
130
|
|
|
|
|
|
|
} |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
=head3 get_deltas(@oids) |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
Will query the agent for values associated with the SCALAR OIDs passed |
135
|
|
|
|
|
|
|
in and return a hash with the results of the query; the value for each |
136
|
|
|
|
|
|
|
oid will be the delta, massaged as needed by the built in delta-computation |
137
|
|
|
|
|
|
|
function or your own function. |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
=head3 get_delta_for_value($key, $value); |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
Will perform delta computation on the passed in value; using |
142
|
|
|
|
|
|
|
the key passed in in place of the OID that would be used in |
143
|
|
|
|
|
|
|
get_deltas as the hash key. This routine can be used by the end |
144
|
|
|
|
|
|
|
user for metrics that are created by the plugin developer as opposed to |
145
|
|
|
|
|
|
|
counters retrieved by the SNMP agent. |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
=head2 Other methods |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
=cut |
150
|
|
|
|
|
|
|
|
151
|
1
|
|
|
1
|
|
23546
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
58
|
|
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
require Exporter; |
154
|
1
|
|
|
1
|
|
5
|
use base qw(Exporter Nagios::Plugin); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
1253
|
|
155
|
|
|
|
|
|
|
|
156
|
1
|
|
|
1
|
|
60901
|
use Net::SNMP; |
|
1
|
|
|
|
|
100126
|
|
|
1
|
|
|
|
|
150
|
|
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
# Have to copy, inheritence doesn't work for these |
159
|
1
|
|
|
1
|
|
8
|
use constant OK => 0; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
53
|
|
160
|
1
|
|
|
1
|
|
5
|
use constant WARNING => 1; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
32
|
|
161
|
1
|
|
|
1
|
|
4
|
use constant CRITICAL => 2; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
29
|
|
162
|
1
|
|
|
1
|
|
3
|
use constant UNKNOWN => 3; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
30
|
|
163
|
1
|
|
|
1
|
|
4
|
use constant DEPENDENT => 4; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
3202
|
|
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
our @EXPORT = qw(OK WARNING CRITICAL UNKNOWN DEPENDENT); |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
our $VERSION = '1.2'; |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
our $SNMP_USAGE = <
|
170
|
|
|
|
|
|
|
--hostname|-H HOST --port|-p INT --snmp-version 1|2c|3 \\ |
171
|
|
|
|
|
|
|
[--snmp-timeout INT] \\ |
172
|
|
|
|
|
|
|
[--snmp-local-ip IP] \\ |
173
|
|
|
|
|
|
|
[--warning|-w STRING] [--critical|-c STRING] \\ |
174
|
|
|
|
|
|
|
[--snmp-debug] \\ |
175
|
|
|
|
|
|
|
[--snmp-max-msg-size N] \\ |
176
|
|
|
|
|
|
|
[--alt-host HOST_1 ... --alt-host HOST_N] \\ |
177
|
|
|
|
|
|
|
{ |
178
|
|
|
|
|
|
|
[--rocommunity S] | \\ |
179
|
|
|
|
|
|
|
[--auth-username S --auth-password S [--auth-protocol S]] |
180
|
|
|
|
|
|
|
} |
181
|
|
|
|
|
|
|
EOF |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
our %OS_TYPES = qw( |
184
|
|
|
|
|
|
|
.1.3.6.1.4.1.8072.3.2.1 hpux |
185
|
|
|
|
|
|
|
.1.3.6.1.4.1.8072.3.2.2 sunos4 |
186
|
|
|
|
|
|
|
.1.3.6.1.4.1.8072.3.2.3 solaris |
187
|
|
|
|
|
|
|
.1.3.6.1.4.1.8072.3.2.4 osf |
188
|
|
|
|
|
|
|
.1.3.6.1.4.1.8072.3.2.5 ultrix |
189
|
|
|
|
|
|
|
.1.3.6.1.4.1.8072.3.2.6 hpux10 |
190
|
|
|
|
|
|
|
.1.3.6.1.4.1.8072.3.2.7 netbsd1 |
191
|
|
|
|
|
|
|
.1.3.6.1.4.1.8072.3.2.8 freebsd |
192
|
|
|
|
|
|
|
.1.3.6.1.4.1.8072.3.2.9 irix |
193
|
|
|
|
|
|
|
.1.3.6.1.4.1.8072.3.2.10 linux |
194
|
|
|
|
|
|
|
.1.3.6.1.4.1.8072.3.2.11 bsdi |
195
|
|
|
|
|
|
|
.1.3.6.1.4.1.8072.3.2.12 openbsd |
196
|
|
|
|
|
|
|
.1.3.6.1.4.1.8072.3.2.13 win32 |
197
|
|
|
|
|
|
|
.1.3.6.1.4.1.8072.3.2.14 hpux11 |
198
|
|
|
|
|
|
|
.1.3.6.1.4.1.8072.3.2.255 unknown |
199
|
|
|
|
|
|
|
); |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
sub new { |
202
|
|
|
|
|
|
|
|
203
|
0
|
|
|
0
|
0
|
|
my $class = shift; |
204
|
0
|
|
|
|
|
|
my %args = (@_); |
205
|
|
|
|
|
|
|
|
206
|
0
|
|
|
|
|
|
$args{'usage'} .= $SNMP_USAGE; |
207
|
|
|
|
|
|
|
|
208
|
0
|
|
|
|
|
|
my $process_delta_opts = undef; |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
# have to do this before SUPER call |
211
|
0
|
0
|
|
|
|
|
if (exists $args{'process_deltas'}) { |
212
|
0
|
|
|
|
|
|
$process_delta_opts = $args{'process_deltas'}; |
213
|
0
|
|
|
|
|
|
$args{'usage'} .= " --check-interval seconds\n"; |
214
|
|
|
|
|
|
|
|
215
|
0
|
0
|
0
|
|
|
|
if ((exists $process_delta_opts->{'cache'}) && |
216
|
|
|
|
|
|
|
(exists $process_delta_opts->{'cache'}->{'type'})) { |
217
|
|
|
|
|
|
|
|
218
|
0
|
|
|
|
|
|
my $ct = lc($process_delta_opts->{'cache'}->{'type'}); |
219
|
|
|
|
|
|
|
|
220
|
0
|
0
|
|
|
|
|
if ($ct eq 'memcache') { |
221
|
0
|
|
|
|
|
|
$args{'usage'} .= <
|
222
|
|
|
|
|
|
|
--memcache-addr listening hostname or IP |
223
|
|
|
|
|
|
|
--memcache-port listening port |
224
|
|
|
|
|
|
|
EOF |
225
|
|
|
|
|
|
|
} |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
} |
228
|
0
|
|
|
|
|
|
delete $args{'process_deltas'}; |
229
|
|
|
|
|
|
|
} |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
# For multiple host checks, developer might want script to |
232
|
|
|
|
|
|
|
# exit with error if OID does not exist on any host checked; |
233
|
|
|
|
|
|
|
# this will cause a check for noSuchObject and noSuchInstance |
234
|
|
|
|
|
|
|
# to happen for every result returned by the remote agent .. |
235
|
|
|
|
|
|
|
# with this set to 1 the agent will exit with error if noSuch* |
236
|
|
|
|
|
|
|
# errors are found in result sets for all hosts. |
237
|
|
|
|
|
|
|
|
238
|
0
|
|
|
|
|
|
my $die_on_no_such = 0; |
239
|
|
|
|
|
|
|
|
240
|
0
|
0
|
|
|
|
|
if (exists $args{'error_on_no_such'}) { |
241
|
0
|
|
|
|
|
|
$die_on_no_such = $args{'error_on_no_such'}; |
242
|
|
|
|
|
|
|
} |
243
|
0
|
|
|
|
|
|
delete $args{'error_on_no_such'}; |
244
|
|
|
|
|
|
|
|
245
|
0
|
|
|
|
|
|
my $self = $class->SUPER::new(%args); |
246
|
|
|
|
|
|
|
|
247
|
0
|
0
|
|
|
|
|
if (defined $process_delta_opts) { |
248
|
0
|
|
|
|
|
|
$self->_setup_delta_cache_options($process_delta_opts); |
249
|
|
|
|
|
|
|
} |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
# Add standard SNMP options to the plugin |
252
|
0
|
|
|
|
|
|
$self->_snmp_add_options(); |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
# Hold the SNMP sessions we are using. Multiple |
255
|
|
|
|
|
|
|
# potential hosts can be specified using one or more |
256
|
|
|
|
|
|
|
# --alt-host arguments in addition to the single |
257
|
|
|
|
|
|
|
# --hostname argument given on the command line. |
258
|
0
|
|
|
|
|
|
$self->{'_SNMP_SESSIONS'} = []; |
259
|
|
|
|
|
|
|
|
260
|
0
|
|
|
|
|
|
$self->{'_SNMP_DIE_ON_NO_SUCH'} = $die_on_no_such; |
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
# Hold the name of the host used for polling when multiple |
263
|
|
|
|
|
|
|
# potential hosts are specified using --hostname and |
264
|
|
|
|
|
|
|
# one or more --alt-host options on the command line |
265
|
0
|
|
|
|
|
|
$self->{'_SNMP_POLLED_HOST'} = ''; |
266
|
|
|
|
|
|
|
|
267
|
0
|
|
|
|
|
|
return $self; |
268
|
|
|
|
|
|
|
} |
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
sub start_timer { |
271
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
272
|
|
|
|
|
|
|
|
273
|
0
|
0
|
0
|
|
|
|
if ( (defined $self->opts->get('timeout')) && |
274
|
|
|
|
|
|
|
($self->opts->get('timeout') > 0) ) { |
275
|
0
|
|
|
|
|
|
alarm($self->opts->get('timeout')); |
276
|
|
|
|
|
|
|
} |
277
|
|
|
|
|
|
|
} |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
# Add Nagios::Plugin options related to SNMP to the plugin |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
sub _snmp_add_options { |
282
|
|
|
|
|
|
|
|
283
|
0
|
|
|
0
|
|
|
my $self = shift; |
284
|
|
|
|
|
|
|
|
285
|
0
|
|
|
|
|
|
$self->add_arg( |
286
|
|
|
|
|
|
|
'spec' => 'snmp-version=s', |
287
|
|
|
|
|
|
|
'help' => '--snmp-version 1|2c|3 [default 3]', |
288
|
|
|
|
|
|
|
'required' => 1, |
289
|
|
|
|
|
|
|
'default' => '3' |
290
|
|
|
|
|
|
|
); |
291
|
|
|
|
|
|
|
|
292
|
0
|
|
|
|
|
|
$self->add_arg( |
293
|
|
|
|
|
|
|
'spec' => 'rocommunity=s', |
294
|
|
|
|
|
|
|
'help' => "--rocommunity NAME\n Community name: SNMP 1|2c ONLY", |
295
|
|
|
|
|
|
|
'required' => 0, |
296
|
|
|
|
|
|
|
'default' => '' |
297
|
|
|
|
|
|
|
); |
298
|
|
|
|
|
|
|
|
299
|
0
|
|
|
|
|
|
$self->add_arg( |
300
|
|
|
|
|
|
|
'spec' => 'auth-username=s', |
301
|
|
|
|
|
|
|
'help' => "--auth-username USER\n Auth username: SNMP 3 only", |
302
|
|
|
|
|
|
|
'required' => 0, |
303
|
|
|
|
|
|
|
'default' => '' |
304
|
|
|
|
|
|
|
); |
305
|
|
|
|
|
|
|
|
306
|
0
|
|
|
|
|
|
$self->add_arg( |
307
|
|
|
|
|
|
|
'spec' => 'auth-password=s', |
308
|
|
|
|
|
|
|
'help' => "--auth-password PASS\n Auth password: SNMP 3 only", |
309
|
|
|
|
|
|
|
'required' => 0, |
310
|
|
|
|
|
|
|
'default' => '' |
311
|
|
|
|
|
|
|
); |
312
|
|
|
|
|
|
|
|
313
|
0
|
|
|
|
|
|
$self->add_arg( |
314
|
|
|
|
|
|
|
'spec' => 'auth-protocol=s', |
315
|
|
|
|
|
|
|
'help' => "--auth-protocol PROTO\n" . |
316
|
|
|
|
|
|
|
" Auth protocol: SNMP 3 only [default md5]", |
317
|
|
|
|
|
|
|
'required' => 0, |
318
|
|
|
|
|
|
|
'default' => 'md5' |
319
|
|
|
|
|
|
|
); |
320
|
|
|
|
|
|
|
|
321
|
0
|
|
|
|
|
|
$self->add_arg( |
322
|
|
|
|
|
|
|
'spec' => 'port|p=s', |
323
|
|
|
|
|
|
|
'help' => "--port INT\n SNMP agent port [default 161]", |
324
|
|
|
|
|
|
|
'required' => 0, |
325
|
|
|
|
|
|
|
'default' => '161' |
326
|
|
|
|
|
|
|
); |
327
|
|
|
|
|
|
|
|
328
|
0
|
|
|
|
|
|
$self->add_arg( |
329
|
|
|
|
|
|
|
'spec' => 'hostname|H=s', |
330
|
|
|
|
|
|
|
'help' => "-H, --hostname\n Host to check NAME|IP", |
331
|
|
|
|
|
|
|
'required' => 1 |
332
|
|
|
|
|
|
|
); |
333
|
|
|
|
|
|
|
|
334
|
0
|
|
|
|
|
|
$self->add_arg( |
335
|
|
|
|
|
|
|
'spec' => 'alt-host|ah|A=s@', |
336
|
|
|
|
|
|
|
'help' => <
|
337
|
|
|
|
|
|
|
-A, --ah, --alt-host NAME|IP |
338
|
|
|
|
|
|
|
Additional hosts to attempt to connect to in addition to the host given |
339
|
|
|
|
|
|
|
with the --hostname option, pass in one host per --alt-host option . |
340
|
|
|
|
|
|
|
Use if you want to check a cluster of hosts in which only *ONE* has an |
341
|
|
|
|
|
|
|
active agent that will respond to a query properly. Each host will be |
342
|
|
|
|
|
|
|
tried in turn for each get() or walk() request until either a valid |
343
|
|
|
|
|
|
|
response is received or all hosts fail to respond or return noSuchObject |
344
|
|
|
|
|
|
|
or noSuchInstance. An error will only be thrown by the plugin if none |
345
|
|
|
|
|
|
|
of the hosts passed in with --hostname and --alt-host return a valid |
346
|
|
|
|
|
|
|
response. |
347
|
|
|
|
|
|
|
EOF |
348
|
|
|
|
|
|
|
'required' => 0, |
349
|
|
|
|
|
|
|
'default' => [] |
350
|
|
|
|
|
|
|
); |
351
|
|
|
|
|
|
|
|
352
|
0
|
|
|
|
|
|
$self->add_arg( |
353
|
|
|
|
|
|
|
'spec' => 'snmp-timeout=i', |
354
|
|
|
|
|
|
|
'help' => "--snmp-timeout INT\n" . |
355
|
|
|
|
|
|
|
" Timeout for SNMP queries [default - none]", |
356
|
|
|
|
|
|
|
'default' => 0 |
357
|
|
|
|
|
|
|
); |
358
|
|
|
|
|
|
|
|
359
|
0
|
|
|
|
|
|
$self->add_arg( |
360
|
|
|
|
|
|
|
'spec' => 'snmp-debug', |
361
|
|
|
|
|
|
|
'help' => "--snmp-debug [default off]", |
362
|
|
|
|
|
|
|
'default' => 0 |
363
|
|
|
|
|
|
|
); |
364
|
|
|
|
|
|
|
|
365
|
0
|
|
|
|
|
|
$self->add_arg( |
366
|
|
|
|
|
|
|
'spec' => 'warning|w=s', |
367
|
|
|
|
|
|
|
'help' => "-w, --warning STRING [optional]", |
368
|
|
|
|
|
|
|
'required' => 0 |
369
|
|
|
|
|
|
|
); |
370
|
|
|
|
|
|
|
|
371
|
0
|
|
|
|
|
|
$self->add_arg( |
372
|
|
|
|
|
|
|
'spec' => 'critical|c=s', |
373
|
|
|
|
|
|
|
'help' => "-c, --critical STRING", |
374
|
|
|
|
|
|
|
'required' => 0 |
375
|
|
|
|
|
|
|
); |
376
|
|
|
|
|
|
|
|
377
|
0
|
|
|
|
|
|
$self->add_arg( |
378
|
|
|
|
|
|
|
'spec' => 'snmp-local-ip=s', |
379
|
|
|
|
|
|
|
'help' => "--snmp-local-ip IP-ADDRESS\n" . |
380
|
|
|
|
|
|
|
" Local IP address to send traffic on [optional]", |
381
|
|
|
|
|
|
|
'default' => '' |
382
|
|
|
|
|
|
|
); |
383
|
|
|
|
|
|
|
|
384
|
0
|
|
|
|
|
|
$self->add_arg( |
385
|
|
|
|
|
|
|
'spec' => 'snmp-max-msg-size=i', |
386
|
|
|
|
|
|
|
'help' => "--snmp-max-msg-size BYTES\n" . |
387
|
|
|
|
|
|
|
" Specify SNMP maximum messages size [default 1470]", |
388
|
|
|
|
|
|
|
'default' => '1470' |
389
|
|
|
|
|
|
|
); |
390
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
} |
392
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
=pod |
394
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
=head3 _setup_delta_cache_options |
396
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
This method adds arguments to the Nagios::Plugin instance based on |
398
|
|
|
|
|
|
|
the type of cache along with making --check-interval required |
399
|
|
|
|
|
|
|
as a plugin needs to know the interval between plugin calls in |
400
|
|
|
|
|
|
|
order to calculate deltas and delta variance properly. |
401
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
'process_deltas' => { |
403
|
|
|
|
|
|
|
'cache' => { |
404
|
|
|
|
|
|
|
'type' => 'memcache', |
405
|
|
|
|
|
|
|
'options => { 'memcache_port' => 11211, |
406
|
|
|
|
|
|
|
'memcache_addr' => 127.0.0.1 |
407
|
|
|
|
|
|
|
}, |
408
|
|
|
|
|
|
|
'default_interval' => 300, # seconds |
409
|
|
|
|
|
|
|
'delta_compute_function' => \&my_delta_function |
410
|
|
|
|
|
|
|
} |
411
|
|
|
|
|
|
|
} |
412
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
=cut |
414
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
sub _setup_delta_cache_options { |
416
|
|
|
|
|
|
|
|
417
|
0
|
|
|
0
|
|
|
my ($self, $opts) = @_; |
418
|
|
|
|
|
|
|
|
419
|
0
|
0
|
|
|
|
|
if (! exists $opts->{'cache'}) { |
420
|
0
|
|
|
|
|
|
$self->die(q{'process_deltas' specified but required hash key} |
421
|
|
|
|
|
|
|
. q{'cache' does not exist!}); |
422
|
|
|
|
|
|
|
} |
423
|
|
|
|
|
|
|
|
424
|
0
|
0
|
|
|
|
|
if (! ref($opts->{'cache'}) eq 'HASH') { |
425
|
0
|
|
|
|
|
|
$self->die(q{'process_deltas' specified but key 'cache' does not} |
426
|
|
|
|
|
|
|
. q{contain a hash reference of options for cache type!}); |
427
|
|
|
|
|
|
|
} |
428
|
|
|
|
|
|
|
|
429
|
0
|
0
|
|
|
|
|
if (! exists $opts->{'cache'}->{'type'}) { |
430
|
0
|
|
|
|
|
|
$self->die(q{'process_deltas' specified but hash ref 'cache' does } |
431
|
|
|
|
|
|
|
. q{not specify a cache type with key 'type'!}); |
432
|
|
|
|
|
|
|
} |
433
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
# Add the --check-interval option - required for any delta checks |
435
|
|
|
|
|
|
|
|
436
|
0
|
|
|
|
|
|
my $default_check_interval = 300; |
437
|
|
|
|
|
|
|
|
438
|
0
|
0
|
|
|
|
|
if (exists $opts->{'default_interval'}) { |
439
|
0
|
|
|
|
|
|
$default_check_interval = $opts->{'default_interval'}; |
440
|
|
|
|
|
|
|
} |
441
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
$self->add_arg( |
443
|
0
|
|
|
|
|
|
'spec' => 'check-interval=i', |
444
|
|
|
|
|
|
|
'help' => q{--check-interval interval between checks in seconds} |
445
|
|
|
|
|
|
|
. qq{ [default interval $default_check_interval seconds]}, |
446
|
|
|
|
|
|
|
'required' => 0, |
447
|
|
|
|
|
|
|
'default' => $default_check_interval, |
448
|
|
|
|
|
|
|
); |
449
|
|
|
|
|
|
|
|
450
|
0
|
|
|
|
|
|
$self->{'_SNMP_PROCESS_DELTAS'} = {}; |
451
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
# Cache specific options |
453
|
|
|
|
|
|
|
|
454
|
0
|
|
|
|
|
|
my $cache_type = lc($opts->{'cache'}->{'type'}); |
455
|
|
|
|
|
|
|
|
456
|
0
|
0
|
|
|
|
|
if ($cache_type eq 'memcache') { |
457
|
|
|
|
|
|
|
|
458
|
0
|
|
|
|
|
|
eval "use Cache::Memcached"; |
459
|
|
|
|
|
|
|
|
460
|
0
|
0
|
|
|
|
|
if ($@) { |
461
|
0
|
|
|
|
|
|
$self->die(q{Delta caching with Memcache requested } |
462
|
|
|
|
|
|
|
. qq{but can't use Cache::Memcached: $@}); |
463
|
|
|
|
|
|
|
} |
464
|
|
|
|
|
|
|
|
465
|
0
|
|
|
|
|
|
my $default_memcache_addr = undef; |
466
|
0
|
|
|
|
|
|
my $addr_required = 1; |
467
|
0
|
|
|
|
|
|
my $addr_default = ""; |
468
|
|
|
|
|
|
|
|
469
|
0
|
0
|
|
|
|
|
if (exists $opts->{'cache'}->{'options'}->{'memcache_addr'}) { |
470
|
0
|
|
|
|
|
|
$addr_required = 0; |
471
|
0
|
|
|
|
|
|
$default_memcache_addr = |
472
|
|
|
|
|
|
|
$opts->{'cache'}->{'options'}->{'memcache_addr'}; |
473
|
0
|
|
|
|
|
|
$addr_default = qq{ (default $default_memcache_addr)}; |
474
|
|
|
|
|
|
|
} |
475
|
|
|
|
|
|
|
|
476
|
0
|
|
|
|
|
|
my $default_memcache_port = undef; |
477
|
0
|
|
|
|
|
|
my $port_required = 1; |
478
|
0
|
|
|
|
|
|
my $port_default = ""; |
479
|
|
|
|
|
|
|
|
480
|
0
|
0
|
|
|
|
|
if (exists $opts->{'cache'}->{'options'}->{'memcache_port'}) { |
481
|
0
|
|
|
|
|
|
$port_required = 0; |
482
|
0
|
|
|
|
|
|
$default_memcache_port = |
483
|
|
|
|
|
|
|
$opts->{'cache'}->{'options'}->{'memcache_port'}; |
484
|
0
|
|
|
|
|
|
$port_default = qq{ (default $default_memcache_port)}; |
485
|
|
|
|
|
|
|
} |
486
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
$self->add_arg( |
488
|
0
|
|
|
|
|
|
'spec' => 'memcache-addr|ma=s', |
489
|
|
|
|
|
|
|
'required' => $addr_required, |
490
|
|
|
|
|
|
|
'help' => "--ma, --memcache-addr\n" |
491
|
|
|
|
|
|
|
. qq( Host memcache runs on${addr_default}. Cache is) |
492
|
|
|
|
|
|
|
. q{ required to store deltas for counters for this script. }, |
493
|
|
|
|
|
|
|
'default' => $default_memcache_addr |
494
|
|
|
|
|
|
|
); |
495
|
|
|
|
|
|
|
|
496
|
0
|
0
|
|
|
|
|
if ($addr_required == 1) { |
497
|
0
|
|
|
|
|
|
$self->{'usage'} .= q{ --memcache-addr memcache IP or hostname}; |
498
|
|
|
|
|
|
|
} |
499
|
|
|
|
|
|
|
|
500
|
0
|
0
|
|
|
|
|
if ($port_required == 1) { |
501
|
0
|
|
|
|
|
|
$self->{'usage'} .= q{ --memcache-port memcache port number} |
502
|
|
|
|
|
|
|
} |
503
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
$self->add_arg( |
505
|
0
|
|
|
|
|
|
'spec' => 'memcache-port|mp=i', |
506
|
|
|
|
|
|
|
'required' => $port_required, |
507
|
|
|
|
|
|
|
'help' => "--mp, --memcache-port\n" |
508
|
|
|
|
|
|
|
. qq( Port number memcache runs on${port_default}. Cache is) |
509
|
|
|
|
|
|
|
. q{ required to store deltas for counters for this script. }, |
510
|
|
|
|
|
|
|
'default' => $default_memcache_port |
511
|
|
|
|
|
|
|
); |
512
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
} |
514
|
|
|
|
|
|
|
else { |
515
|
0
|
|
|
|
|
|
$self->die(qq{Unsupported cache type '$cache_type'}); |
516
|
|
|
|
|
|
|
} |
517
|
|
|
|
|
|
|
|
518
|
0
|
|
|
|
|
|
$self->{'_SNMP_PROCESS_DELTAS'}->{'cache_type'} = $cache_type; |
519
|
|
|
|
|
|
|
|
520
|
0
|
0
|
|
|
|
|
if (exists $opts->{'delta_compute_function'}) { |
521
|
0
|
|
|
|
|
|
my $callback = $opts->{'delta_compute_function'}; |
522
|
|
|
|
|
|
|
|
523
|
0
|
0
|
|
|
|
|
if (! ref($callback) eq 'CODE') { |
524
|
0
|
|
|
|
|
|
$self->die(q{'process_deltas' option 'delta_compute_function'} |
525
|
|
|
|
|
|
|
. q{ is not a reference to a function!}); |
526
|
|
|
|
|
|
|
} |
527
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
# Create the callback key here, if the key does not exist |
529
|
|
|
|
|
|
|
# we are using the built-in compute delta function. |
530
|
0
|
|
|
|
|
|
$self->{'_SNMP_PROCESS_DELTAS'}->{'callback'} = |
531
|
|
|
|
|
|
|
$opts->{'delta_compute_function'}; |
532
|
|
|
|
|
|
|
} |
533
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
} |
535
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
=pod |
537
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
=head3 _initialize_delta_cache() |
539
|
|
|
|
|
|
|
|
540
|
|
|
|
|
|
|
Initialize cache for processing deltas. All validation of cache |
541
|
|
|
|
|
|
|
options is done in _setup_delta_cache_options. |
542
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
=cut |
544
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
sub _initialize_delta_cache { |
546
|
|
|
|
|
|
|
|
547
|
0
|
|
|
0
|
|
|
my $self = shift; |
548
|
|
|
|
|
|
|
|
549
|
0
|
|
|
|
|
|
my $opts = $self->{'_SNMP_PROCESS_DELTAS'}; |
550
|
|
|
|
|
|
|
|
551
|
0
|
|
|
|
|
|
my $cache_type = $opts->{'cache_type'}; |
552
|
|
|
|
|
|
|
|
553
|
0
|
0
|
|
|
|
|
if ($cache_type eq 'memcache') { |
554
|
|
|
|
|
|
|
|
555
|
0
|
|
|
|
|
|
my $addr = $self->opts->get('memcache-addr'); |
556
|
0
|
|
|
|
|
|
my $port = $self->opts->get('memcache-port'); |
557
|
|
|
|
|
|
|
|
558
|
0
|
|
|
|
|
|
eval <
|
559
|
|
|
|
|
|
|
use Cache::Memcached; |
560
|
|
|
|
|
|
|
|
561
|
|
|
|
|
|
|
\$self->{'_SNMP_PROCESS_DELTAS'}->{'cache'} = |
562
|
|
|
|
|
|
|
Cache::Memcached->new({'servers' => [ "${addr}:${port}" ] }); |
563
|
|
|
|
|
|
|
EOF |
564
|
|
|
|
|
|
|
|
565
|
0
|
|
|
|
|
|
my $error = $@; |
566
|
|
|
|
|
|
|
|
567
|
0
|
0
|
|
|
|
|
$self->die("Cannot instantiate memcached instance: $error") |
568
|
|
|
|
|
|
|
if $error; |
569
|
|
|
|
|
|
|
} |
570
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
} |
572
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
=pod |
574
|
|
|
|
|
|
|
|
575
|
|
|
|
|
|
|
=head2 _get_cache() |
576
|
|
|
|
|
|
|
|
577
|
|
|
|
|
|
|
Return cache instance after ensuring it exists and is valid. Exit with |
578
|
|
|
|
|
|
|
error if it is not. |
579
|
|
|
|
|
|
|
|
580
|
|
|
|
|
|
|
=cut |
581
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
sub _get_cache { |
583
|
|
|
|
|
|
|
|
584
|
0
|
|
|
0
|
|
|
my $self = shift; |
585
|
|
|
|
|
|
|
|
586
|
0
|
0
|
|
|
|
|
if (! exists $self->{'_SNMP_PROCESS_DELTAS'}) { |
587
|
0
|
|
|
|
|
|
$self->die("Cache requested but delta processing not requested!"); |
588
|
|
|
|
|
|
|
} |
589
|
|
|
|
|
|
|
|
590
|
0
|
|
|
|
|
|
my $spd = $self->{'_SNMP_PROCESS_DELTAS'}; |
591
|
|
|
|
|
|
|
|
592
|
0
|
0
|
0
|
|
|
|
if ((! exists $spd->{'cache'}) || |
593
|
|
|
|
|
|
|
(! ref($self->{'cache'}) =~ m/^Cache::/)) { |
594
|
0
|
|
|
|
|
|
$self->die("Cache requested but never initialized!"); |
595
|
|
|
|
|
|
|
} |
596
|
|
|
|
|
|
|
|
597
|
0
|
|
|
|
|
|
return $spd->{'cache'}; |
598
|
|
|
|
|
|
|
} |
599
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
=pod |
601
|
|
|
|
|
|
|
|
602
|
|
|
|
|
|
|
=head2 _get_from_cache($key) |
603
|
|
|
|
|
|
|
|
604
|
|
|
|
|
|
|
Return the value associated with $key from the cache; if value does |
605
|
|
|
|
|
|
|
not exist, returns undef. If cache is invalid, will exit with |
606
|
|
|
|
|
|
|
error. |
607
|
|
|
|
|
|
|
|
608
|
|
|
|
|
|
|
=cut |
609
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
sub _get_from_cache { |
611
|
0
|
|
|
0
|
|
|
my ($self, $key) = @_; |
612
|
0
|
|
|
|
|
|
my $cache = $self->_get_cache(); |
613
|
0
|
|
|
|
|
|
my $tv_ref = $cache->get($key); |
614
|
0
|
0
|
|
|
|
|
$tv_ref = { 'timestamp' => 0, 'value' => undef } if ! defined $tv_ref; |
615
|
|
|
|
|
|
|
|
616
|
0
|
0
|
|
|
|
|
if ($self->opts->get('snmp-debug') == 1) { |
617
|
0
|
|
|
|
|
|
my $ts = 'NOT DEFINED'; |
618
|
0
|
0
|
|
|
|
|
$ts = $tv_ref->{'timestamp'} if defined $tv_ref->{'timestamp'}; |
619
|
0
|
|
|
|
|
|
my $v = 'NOT DEFINED'; |
620
|
0
|
0
|
|
|
|
|
$v = $tv_ref->{'value'} if defined $tv_ref->{'value'}; |
621
|
0
|
|
|
|
|
|
$self->debug(qq{_get_from_cache: $cache->get($key) returns } |
622
|
|
|
|
|
|
|
. qq{timestamp:$ts value:$v}); |
623
|
|
|
|
|
|
|
} |
624
|
0
|
|
|
|
|
|
return ( $tv_ref->{'timestamp'}, $tv_ref->{'value'} ); |
625
|
|
|
|
|
|
|
} |
626
|
|
|
|
|
|
|
|
627
|
|
|
|
|
|
|
=pod |
628
|
|
|
|
|
|
|
|
629
|
|
|
|
|
|
|
=head2 _store_in_cache($key, $value) |
630
|
|
|
|
|
|
|
|
631
|
|
|
|
|
|
|
Store a value in the cache using the passed in key. |
632
|
|
|
|
|
|
|
|
633
|
|
|
|
|
|
|
=cut |
634
|
|
|
|
|
|
|
|
635
|
|
|
|
|
|
|
sub _store_in_cache { |
636
|
|
|
|
|
|
|
|
637
|
0
|
|
|
0
|
|
|
my ($self, $key, $value) = @_; |
638
|
|
|
|
|
|
|
|
639
|
|
|
|
|
|
|
|
640
|
0
|
|
|
|
|
|
my $cache = $self->_get_cache(); |
641
|
0
|
|
|
|
|
|
my $now = time(); |
642
|
0
|
|
|
|
|
|
my $complex_value = { 'value' => $value, 'timestamp' => $now }; |
643
|
0
|
|
|
|
|
|
my $result = $cache->set($key, $complex_value); |
644
|
|
|
|
|
|
|
|
645
|
0
|
|
|
|
|
|
$self->debug("_store_in_cache: set($key, $value / $now) returns $result"); |
646
|
|
|
|
|
|
|
|
647
|
0
|
0
|
|
|
|
|
$self->die(qq{Unable to store ($key -> $value / $now) in cache, please} |
648
|
|
|
|
|
|
|
. qq{ check cache configuration parameters!}) if $result == 0; |
649
|
|
|
|
|
|
|
|
650
|
0
|
|
|
|
|
|
return $value; |
651
|
|
|
|
|
|
|
} |
652
|
|
|
|
|
|
|
|
653
|
|
|
|
|
|
|
=pod |
654
|
|
|
|
|
|
|
|
655
|
|
|
|
|
|
|
=head2 get_cache_key_for($key) |
656
|
|
|
|
|
|
|
|
657
|
|
|
|
|
|
|
Returns a unique key that can be used to retrieve a value from the cache; |
658
|
|
|
|
|
|
|
feel free to override this with a different unique key algorithm if the |
659
|
|
|
|
|
|
|
default does not suit you (by subclassing Nagios::Plugin::SNMP). By |
660
|
|
|
|
|
|
|
default, the unique key will be made by concatenating the following |
661
|
|
|
|
|
|
|
values, separated by colons: |
662
|
|
|
|
|
|
|
* hostname of the host being checked |
663
|
|
|
|
|
|
|
* port of the host being checked |
664
|
|
|
|
|
|
|
* user provided key (in the case of get_delta_for_value) or OID of |
665
|
|
|
|
|
|
|
the scalar requested (in the case of get_deltas(). |
666
|
|
|
|
|
|
|
|
667
|
|
|
|
|
|
|
=cut |
668
|
|
|
|
|
|
|
|
669
|
|
|
|
|
|
|
sub get_cache_key_for { |
670
|
|
|
|
|
|
|
|
671
|
0
|
|
|
0
|
1
|
|
my ($self, $key) = @_; |
672
|
|
|
|
|
|
|
|
673
|
0
|
|
|
|
|
|
my $hostname = $self->opts->get('hostname'); |
674
|
0
|
0
|
|
|
|
|
my $port = (defined $self->opts->get('port')) |
675
|
|
|
|
|
|
|
? $self->opts->get('port') : 0; |
676
|
|
|
|
|
|
|
|
677
|
0
|
|
|
|
|
|
my $cache_key = join(q{:}, $hostname, $port, $key); |
678
|
|
|
|
|
|
|
|
679
|
0
|
|
|
|
|
|
$self->debug("get_cache_key_for: $key -> $cache_key"); |
680
|
|
|
|
|
|
|
|
681
|
0
|
|
|
|
|
|
return $cache_key; |
682
|
|
|
|
|
|
|
} |
683
|
|
|
|
|
|
|
|
684
|
|
|
|
|
|
|
=pod |
685
|
|
|
|
|
|
|
|
686
|
|
|
|
|
|
|
=head3 _snmp_validate_opts() - Validate passed in SNMP options |
687
|
|
|
|
|
|
|
|
688
|
|
|
|
|
|
|
This method validates that any options passed to the plugin using |
689
|
|
|
|
|
|
|
this library make sense. Rules: |
690
|
|
|
|
|
|
|
|
691
|
|
|
|
|
|
|
=over 4 |
692
|
|
|
|
|
|
|
|
693
|
|
|
|
|
|
|
* If SNMP is version 1 or 2c, rocommunity must be set |
694
|
|
|
|
|
|
|
* If SNMP is version 3, auth-username and auth-password must be set |
695
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
=back |
697
|
|
|
|
|
|
|
|
698
|
|
|
|
|
|
|
=cut |
699
|
|
|
|
|
|
|
|
700
|
|
|
|
|
|
|
sub _snmp_validate_opts { |
701
|
|
|
|
|
|
|
|
702
|
0
|
|
|
0
|
|
|
my $self = shift; |
703
|
|
|
|
|
|
|
|
704
|
0
|
|
|
|
|
|
my $opts = $self->opts; |
705
|
|
|
|
|
|
|
|
706
|
0
|
0
|
|
|
|
|
if ($opts->get('snmp-version') eq '3') { |
707
|
|
|
|
|
|
|
|
708
|
0
|
|
|
|
|
|
my @errors; |
709
|
|
|
|
|
|
|
|
710
|
0
|
|
|
|
|
|
for my $p (qw(auth-username auth-password auth-protocol)) { |
711
|
0
|
0
|
|
|
|
|
push(@errors, $p) if $opts->get($p) eq ''; |
712
|
|
|
|
|
|
|
} |
713
|
|
|
|
|
|
|
|
714
|
0
|
0
|
|
|
|
|
die "SNMP parameter validation failed. Missing: " . |
715
|
|
|
|
|
|
|
join(', ', @errors) if scalar(@errors) > 0; |
716
|
|
|
|
|
|
|
|
717
|
|
|
|
|
|
|
} else { |
718
|
|
|
|
|
|
|
|
719
|
0
|
0
|
|
|
|
|
die "SNMP parameter validation failed. Missing rocommunity!" |
720
|
|
|
|
|
|
|
if $opts->get('rocommunity') eq ''; |
721
|
|
|
|
|
|
|
|
722
|
|
|
|
|
|
|
} |
723
|
|
|
|
|
|
|
|
724
|
0
|
0
|
|
|
|
|
if ($opts->get('snmp-local-ip') ne '') { |
725
|
0
|
|
|
|
|
|
my $ip = $opts->get('snmp-local-ip'); |
726
|
0
|
0
|
|
|
|
|
die "SNMP local bind IP address is invalid!" |
727
|
|
|
|
|
|
|
unless $ip =~ m/^(?:[0-9]{1,3}){4}$/; |
728
|
|
|
|
|
|
|
} |
729
|
|
|
|
|
|
|
|
730
|
0
|
|
|
|
|
|
return 1; |
731
|
|
|
|
|
|
|
|
732
|
|
|
|
|
|
|
} |
733
|
|
|
|
|
|
|
|
734
|
|
|
|
|
|
|
=pod |
735
|
|
|
|
|
|
|
|
736
|
|
|
|
|
|
|
=head3 connect() - Establish SNMP session |
737
|
|
|
|
|
|
|
|
738
|
|
|
|
|
|
|
Attempts to connect to the remote system specified in the |
739
|
|
|
|
|
|
|
command-line arguments; will die() with an error message if the |
740
|
|
|
|
|
|
|
session creation fails. |
741
|
|
|
|
|
|
|
|
742
|
|
|
|
|
|
|
=cut |
743
|
|
|
|
|
|
|
|
744
|
|
|
|
|
|
|
sub connect { |
745
|
|
|
|
|
|
|
|
746
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
747
|
|
|
|
|
|
|
|
748
|
0
|
|
|
|
|
|
$self->_snmp_validate_opts(); |
749
|
|
|
|
|
|
|
|
750
|
0
|
|
|
|
|
|
my $opts = $self->opts; |
751
|
|
|
|
|
|
|
|
752
|
0
|
|
|
|
|
|
my @args; |
753
|
|
|
|
|
|
|
|
754
|
0
|
|
|
|
|
|
my $version = $opts->get('snmp-version'); |
755
|
|
|
|
|
|
|
|
756
|
0
|
|
|
|
|
|
my @hosts = ($opts->get('hostname')); |
757
|
|
|
|
|
|
|
|
758
|
0
|
|
|
|
|
|
push(@hosts, @{$opts->get('alt-host')}) |
|
0
|
|
|
|
|
|
|
759
|
0
|
0
|
|
|
|
|
if (scalar(@{$opts->get('alt-host')}) > 0); |
760
|
|
|
|
|
|
|
|
761
|
0
|
|
|
|
|
|
my @sessions = (); |
762
|
0
|
|
|
|
|
|
my @errors = (); |
763
|
|
|
|
|
|
|
|
764
|
0
|
|
|
|
|
|
for my $host (@hosts) { |
765
|
0
|
|
|
|
|
|
push(@args, '-version' => $opts->get('snmp-version')); |
766
|
0
|
|
|
|
|
|
push(@args, '-hostname' => $host); |
767
|
0
|
|
|
|
|
|
push(@args, '-port' => $opts->get('port')); |
768
|
0
|
0
|
|
|
|
|
push(@args, '-timeout' => $opts->get('snmp-timeout')) |
769
|
|
|
|
|
|
|
if ($opts->get('snmp-timeout') > 0); |
770
|
0
|
|
|
|
|
|
push(@args, '-debug' => $opts->get('snmp-debug')); |
771
|
|
|
|
|
|
|
|
772
|
0
|
0
|
|
|
|
|
if ($version eq '3') { |
773
|
0
|
|
|
|
|
|
push(@args, '-username' => $opts->get('auth-username')); |
774
|
0
|
|
|
|
|
|
push(@args, '-authpassword' => $opts->get('auth-password')); |
775
|
0
|
|
|
|
|
|
push(@args, '-authprotocol' => $opts->get('auth-protocol')); |
776
|
|
|
|
|
|
|
} else { |
777
|
0
|
|
|
|
|
|
push(@args, '-community' => $opts->get('rocommunity')); |
778
|
|
|
|
|
|
|
} |
779
|
|
|
|
|
|
|
|
780
|
0
|
0
|
|
|
|
|
push(@args, '-localaddr' => $opts->get('snmp-local-ip')) |
781
|
|
|
|
|
|
|
if $opts->get('snmp-local-ip') ne ''; |
782
|
|
|
|
|
|
|
|
783
|
0
|
0
|
|
|
|
|
push(@args, '-maxMsgSize' => $opts->get('snmp-max-msg-size')) |
784
|
|
|
|
|
|
|
if $opts->get('snmp-max-msg-size') ne ''; |
785
|
|
|
|
|
|
|
|
786
|
0
|
|
|
|
|
|
my ($session, $error) = Net::SNMP->session(@args); |
787
|
|
|
|
|
|
|
|
788
|
0
|
0
|
|
|
|
|
if ($error ne '') { |
789
|
0
|
|
|
|
|
|
push(@errors, "$host - $error"); |
790
|
|
|
|
|
|
|
} |
791
|
|
|
|
|
|
|
else { |
792
|
0
|
|
|
|
|
|
push(@sessions, $session); |
793
|
|
|
|
|
|
|
} |
794
|
|
|
|
|
|
|
|
795
|
|
|
|
|
|
|
} |
796
|
|
|
|
|
|
|
|
797
|
0
|
0
|
0
|
|
|
|
if ( (scalar(@errors) > 0) && (scalar(@errors) == scalar(@sessions)) ) { |
798
|
0
|
|
|
|
|
|
$self->die(qq{Net-SNMP session creation failed for all hosts: } |
799
|
|
|
|
|
|
|
. join(', ', @errors)); |
800
|
|
|
|
|
|
|
} |
801
|
|
|
|
|
|
|
|
802
|
0
|
|
|
|
|
|
$self->{'_SNMP_SESSIONS'} = \@sessions; |
803
|
|
|
|
|
|
|
|
804
|
0
|
|
|
|
|
|
return $self; |
805
|
|
|
|
|
|
|
|
806
|
|
|
|
|
|
|
} |
807
|
|
|
|
|
|
|
|
808
|
|
|
|
|
|
|
=pod |
809
|
|
|
|
|
|
|
|
810
|
|
|
|
|
|
|
=head3 get(@oids) - Perform an SNMP get request |
811
|
|
|
|
|
|
|
|
812
|
|
|
|
|
|
|
Performs an SNMP get request on each passed in OID; returns results |
813
|
|
|
|
|
|
|
as a hash reference where keys are the passed in OIDs and the values are |
814
|
|
|
|
|
|
|
the values returned from the Net::SNMP get() calls. |
815
|
|
|
|
|
|
|
|
816
|
|
|
|
|
|
|
=cut |
817
|
|
|
|
|
|
|
|
818
|
|
|
|
|
|
|
sub get { |
819
|
|
|
|
|
|
|
|
820
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
821
|
0
|
|
|
|
|
|
my @oids = @_; |
822
|
|
|
|
|
|
|
|
823
|
0
|
0
|
|
|
|
|
die "Missing OIDs to get!" unless scalar(@oids) > 0; |
824
|
|
|
|
|
|
|
|
825
|
0
|
|
|
|
|
|
$self->_snmp_ensure_is_connected(); |
826
|
|
|
|
|
|
|
|
827
|
0
|
|
|
|
|
|
my @sessions = @{$self->{'_SNMP_SESSIONS'}}; |
|
0
|
|
|
|
|
|
|
828
|
0
|
|
|
|
|
|
my @errors = (); |
829
|
|
|
|
|
|
|
|
830
|
0
|
|
|
|
|
|
my $results = undef; |
831
|
|
|
|
|
|
|
|
832
|
|
|
|
|
|
|
# Attempt SNMP GET on each host listed .. first one that responds |
833
|
|
|
|
|
|
|
# properly wins .. if none respond, throw an error. |
834
|
|
|
|
|
|
|
|
835
|
|
|
|
|
|
|
SNMP_GET_AGENT: |
836
|
0
|
|
|
|
|
|
for my $s (@sessions) { |
837
|
|
|
|
|
|
|
|
838
|
|
|
|
|
|
|
# Ensure agent actually responded .. do not throw other errors |
839
|
|
|
|
|
|
|
# for now as invalid OIDs will throw errors and we do not want |
840
|
|
|
|
|
|
|
# the end user to have to catch those in parent code .. easy |
841
|
|
|
|
|
|
|
# enough to look for the string constants that represent a |
842
|
|
|
|
|
|
|
# missing OID condition - noSuchObject or noSuchInstance |
843
|
|
|
|
|
|
|
|
844
|
0
|
|
|
|
|
|
my $host = $s->hostname(); |
845
|
0
|
|
|
|
|
|
$self->debug("$host - attempting get_request()"); |
846
|
|
|
|
|
|
|
|
847
|
0
|
|
|
|
|
|
$results = $s->get_request('-varbindlist' => \@oids); |
848
|
|
|
|
|
|
|
|
849
|
0
|
0
|
|
|
|
|
if (! defined $results) { |
850
|
|
|
|
|
|
|
|
851
|
0
|
|
|
|
|
|
my $error = $s->error(); |
852
|
|
|
|
|
|
|
|
853
|
0
|
0
|
|
|
|
|
if ($error =~ /No response from/i) { |
854
|
0
|
|
|
|
|
|
push(@errors, qq{$host - no response - } . join(', ', @oids) |
855
|
|
|
|
|
|
|
. qq{ - $error}); |
856
|
0
|
|
|
|
|
|
$self->debug("$host - get_request failed - $error"); |
857
|
|
|
|
|
|
|
} |
858
|
|
|
|
|
|
|
else { |
859
|
|
|
|
|
|
|
|
860
|
|
|
|
|
|
|
# If we have multiple hosts to potentially check, |
861
|
|
|
|
|
|
|
# any error is recorded. |
862
|
|
|
|
|
|
|
|
863
|
0
|
0
|
|
|
|
|
if (scalar(@sessions) > 1) { |
864
|
0
|
|
|
|
|
|
push(@errors, qq{$host - } . join(', ', @oids) |
865
|
|
|
|
|
|
|
. qq{ - $error}); |
866
|
0
|
|
|
|
|
|
$self->debug("get_request() - $error"); |
867
|
|
|
|
|
|
|
} |
868
|
|
|
|
|
|
|
} |
869
|
|
|
|
|
|
|
|
870
|
|
|
|
|
|
|
} |
871
|
|
|
|
|
|
|
else { |
872
|
|
|
|
|
|
|
|
873
|
0
|
|
|
|
|
|
$self->{'_SNMP_POLLED_HOST'} = $host; |
874
|
0
|
|
|
|
|
|
$self->debug("$host - get_request succeeded!"); |
875
|
|
|
|
|
|
|
|
876
|
0
|
0
|
|
|
|
|
if ($self->{'_SNMP_DIE_ON_NO_SUCH'} == 1) { |
877
|
0
|
|
|
|
|
|
my $nosuch = _ensure_defined_results($host, $results); |
878
|
0
|
0
|
|
|
|
|
if (defined $nosuch) { |
879
|
0
|
|
|
|
|
|
push(@errors, $nosuch); |
880
|
0
|
|
|
|
|
|
$self->debug( |
881
|
|
|
|
|
|
|
"$host - get_request returned noSuch* errors"); |
882
|
|
|
|
|
|
|
# Skip to next agent as we found an unsupported OID |
883
|
0
|
|
|
|
|
|
next SNMP_GET_AGENT; |
884
|
|
|
|
|
|
|
} |
885
|
|
|
|
|
|
|
} |
886
|
|
|
|
|
|
|
|
887
|
0
|
|
|
|
|
|
last SNMP_GET_AGENT; |
888
|
|
|
|
|
|
|
|
889
|
|
|
|
|
|
|
} |
890
|
|
|
|
|
|
|
|
891
|
|
|
|
|
|
|
} |
892
|
|
|
|
|
|
|
|
893
|
0
|
0
|
0
|
|
|
|
if ( (scalar(@errors) > 0) && (scalar(@errors) == scalar(@sessions)) ) { |
894
|
0
|
|
|
|
|
|
$self->nagios_exit(UNKNOWN, q{Net::SNMP get_request() failed: } |
895
|
|
|
|
|
|
|
. join(', ', @errors)); |
896
|
|
|
|
|
|
|
} |
897
|
|
|
|
|
|
|
|
898
|
0
|
|
|
|
|
|
return $results; |
899
|
|
|
|
|
|
|
|
900
|
|
|
|
|
|
|
} |
901
|
|
|
|
|
|
|
|
902
|
|
|
|
|
|
|
=pod |
903
|
|
|
|
|
|
|
|
904
|
|
|
|
|
|
|
=head3 walk(@baseoids) - Perform an SNMP walk request |
905
|
|
|
|
|
|
|
|
906
|
|
|
|
|
|
|
Performs an SNMP walk on each passed in OID; uses the Net-SNMP |
907
|
|
|
|
|
|
|
get_table() method for each base OID to ensure that the method will |
908
|
|
|
|
|
|
|
work regardless of SNMP version in use. Returns results as |
909
|
|
|
|
|
|
|
a hash reference where keys are the passed in base OIDs and the values are |
910
|
|
|
|
|
|
|
references to the results of the Net::SNMP get_table calls. |
911
|
|
|
|
|
|
|
|
912
|
|
|
|
|
|
|
=cut |
913
|
|
|
|
|
|
|
|
914
|
|
|
|
|
|
|
sub walk { |
915
|
|
|
|
|
|
|
|
916
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
917
|
0
|
|
|
|
|
|
my @baseoids = @_; |
918
|
|
|
|
|
|
|
|
919
|
0
|
|
|
|
|
|
$self->_snmp_ensure_is_connected(); |
920
|
|
|
|
|
|
|
|
921
|
0
|
|
|
|
|
|
my @sessions = @{$self->{'_SNMP_SESSIONS'}}; |
|
0
|
|
|
|
|
|
|
922
|
|
|
|
|
|
|
|
923
|
0
|
|
|
|
|
|
my %results; |
924
|
0
|
|
|
|
|
|
my @errors = (); |
925
|
|
|
|
|
|
|
|
926
|
|
|
|
|
|
|
# Attempt a walk on all sessions; first successful walk wins, |
927
|
|
|
|
|
|
|
# throw an error if all sessions fail. |
928
|
|
|
|
|
|
|
|
929
|
|
|
|
|
|
|
SNMP_AGENT: |
930
|
0
|
|
|
|
|
|
for my $s (@sessions) { |
931
|
|
|
|
|
|
|
|
932
|
0
|
|
|
|
|
|
my $successes = 0; |
933
|
|
|
|
|
|
|
|
934
|
0
|
|
|
|
|
|
my $host = $s->hostname(); |
935
|
0
|
|
|
|
|
|
$self->debug("$host - attempting get_table()"); |
936
|
|
|
|
|
|
|
|
937
|
|
|
|
|
|
|
GET_TABLE: |
938
|
0
|
|
|
|
|
|
for my $baseoid (@baseoids) { |
939
|
|
|
|
|
|
|
|
940
|
|
|
|
|
|
|
# Ensure agent actually responded .. do not throw other errors |
941
|
|
|
|
|
|
|
# for now as invalid OIDs will throw errors and we do not want |
942
|
|
|
|
|
|
|
# the end user to have to catch those in parent code .. easy |
943
|
|
|
|
|
|
|
# enough to look for the string constants that represent a |
944
|
|
|
|
|
|
|
# missing OID condition - noSuchObject or noSuchInstance |
945
|
|
|
|
|
|
|
|
946
|
0
|
|
|
|
|
|
my $result = $s->get_table($baseoid); |
947
|
|
|
|
|
|
|
|
948
|
0
|
0
|
|
|
|
|
if (! defined $result) { |
949
|
|
|
|
|
|
|
|
950
|
0
|
|
|
|
|
|
my $error = $s->error(); |
951
|
|
|
|
|
|
|
|
952
|
0
|
0
|
|
|
|
|
if ($error =~ /No response from/i) { |
953
|
0
|
|
|
|
|
|
push(@errors, qq{$host - $error}); |
954
|
0
|
|
|
|
|
|
$self->debug("get_table() - $baseoid - $error"); |
955
|
0
|
|
|
|
|
|
%results = (); |
956
|
|
|
|
|
|
|
} |
957
|
|
|
|
|
|
|
else { |
958
|
|
|
|
|
|
|
|
959
|
|
|
|
|
|
|
# If we have multiple hosts to potentially check, |
960
|
|
|
|
|
|
|
# any error is recorded. |
961
|
|
|
|
|
|
|
|
962
|
1
|
|
|
1
|
|
18992
|
use Data::Dumper; |
|
1
|
|
|
|
|
10178
|
|
|
1
|
|
|
|
|
2135
|
|
963
|
0
|
|
|
|
|
|
$self->debug(Dumper($result)); |
964
|
|
|
|
|
|
|
|
965
|
0
|
0
|
|
|
|
|
if (scalar(@sessions) > 1) { |
966
|
0
|
|
|
|
|
|
push(@errors, qq{$host - $baseoid - $error}); |
967
|
0
|
|
|
|
|
|
$self->debug("get_table() - $baseoid - $error"); |
968
|
|
|
|
|
|
|
} |
969
|
|
|
|
|
|
|
|
970
|
|
|
|
|
|
|
} |
971
|
|
|
|
|
|
|
|
972
|
0
|
|
|
|
|
|
next SNMP_AGENT; |
973
|
|
|
|
|
|
|
} |
974
|
|
|
|
|
|
|
else { |
975
|
0
|
|
|
|
|
|
$self->debug("$host - get_table() succeeded for $baseoid"); |
976
|
|
|
|
|
|
|
|
977
|
0
|
0
|
|
|
|
|
if ($self->{'_SNMP_DIE_ON_NO_SUCH'} == 1) { |
978
|
0
|
|
|
|
|
|
my $error = _ensure_defined_results($host, $result); |
979
|
0
|
0
|
|
|
|
|
if (defined $error) { |
980
|
0
|
|
|
|
|
|
push(@errors, $error); |
981
|
0
|
|
|
|
|
|
$self->debug( |
982
|
|
|
|
|
|
|
"$host - get_table returned noSuch* errors"); |
983
|
|
|
|
|
|
|
# Skip to next agent as we found an unsupported OID |
984
|
0
|
|
|
|
|
|
next SNMP_AGENT; |
985
|
|
|
|
|
|
|
} |
986
|
|
|
|
|
|
|
|
987
|
|
|
|
|
|
|
} |
988
|
|
|
|
|
|
|
|
989
|
0
|
|
|
|
|
|
$results{$baseoid} = $result; |
990
|
0
|
|
|
|
|
|
$successes++; |
991
|
|
|
|
|
|
|
|
992
|
|
|
|
|
|
|
} |
993
|
|
|
|
|
|
|
|
994
|
|
|
|
|
|
|
} |
995
|
|
|
|
|
|
|
|
996
|
0
|
0
|
|
|
|
|
if ($successes == scalar(@baseoids)) { |
997
|
0
|
|
|
|
|
|
$self->debug("$host - walk succeeded for all OIDs"); |
998
|
0
|
|
|
|
|
|
last SNMP_AGENT; |
999
|
|
|
|
|
|
|
} |
1000
|
|
|
|
|
|
|
|
1001
|
|
|
|
|
|
|
} |
1002
|
|
|
|
|
|
|
|
1003
|
0
|
0
|
0
|
|
|
|
if ( (scalar(@errors) > 0) && (scalar(@errors) == scalar(@sessions)) ) { |
1004
|
0
|
|
|
|
|
|
$self->nagios_exit(UNKNOWN, q{Net::SNMP get_table() failed: } |
1005
|
|
|
|
|
|
|
. join(', ', @errors)); |
1006
|
|
|
|
|
|
|
} |
1007
|
|
|
|
|
|
|
|
1008
|
|
|
|
|
|
|
|
1009
|
0
|
|
|
|
|
|
return \%results; |
1010
|
|
|
|
|
|
|
} |
1011
|
|
|
|
|
|
|
|
1012
|
|
|
|
|
|
|
sub get_deltas { |
1013
|
0
|
|
|
0
|
1
|
|
my ($self, @oids) = @_; |
1014
|
|
|
|
|
|
|
|
1015
|
0
|
|
|
|
|
|
my $results = $self->get(@oids); |
1016
|
|
|
|
|
|
|
|
1017
|
0
|
|
|
|
|
|
for my $oid (keys %$results) { |
1018
|
0
|
|
|
|
|
|
my $value = $results->{$oid}; |
1019
|
0
|
|
|
|
|
|
$self->debug("get_deltas(): get_value_for(value($oid, $value)"); |
1020
|
0
|
|
|
|
|
|
$results->{$oid} = $self->get_delta_for_value($oid, $value); |
1021
|
|
|
|
|
|
|
} |
1022
|
|
|
|
|
|
|
|
1023
|
0
|
|
|
|
|
|
return $results; |
1024
|
|
|
|
|
|
|
|
1025
|
|
|
|
|
|
|
} |
1026
|
|
|
|
|
|
|
|
1027
|
|
|
|
|
|
|
sub get_delta_for_value { |
1028
|
0
|
|
|
0
|
1
|
|
my ($self, $key, $current_value) = @_; |
1029
|
|
|
|
|
|
|
|
1030
|
0
|
|
|
|
|
|
my $cache_id = $self->get_cache_key_for($key); |
1031
|
0
|
|
|
|
|
|
my ($previous_run_at, $cached_value) = $self->_get_from_cache($cache_id); |
1032
|
|
|
|
|
|
|
|
1033
|
0
|
|
|
|
|
|
my $interval = $self->opts->get('check-interval'); |
1034
|
|
|
|
|
|
|
|
1035
|
0
|
|
|
|
|
|
my $delta_function_args = { 'previous_value' => $cached_value, |
1036
|
|
|
|
|
|
|
'current_value' => $current_value, |
1037
|
|
|
|
|
|
|
'interval' => $interval, |
1038
|
|
|
|
|
|
|
'previous_run_at' => $previous_run_at, |
1039
|
|
|
|
|
|
|
'cache_id' => $cache_id, |
1040
|
|
|
|
|
|
|
'key' => $key }; |
1041
|
|
|
|
|
|
|
|
1042
|
0
|
0
|
|
|
|
|
if ($self->opts->get('snmp-debug') == 1) { |
1043
|
0
|
|
|
|
|
|
$self-> debug("get_delta_for_value($self, $delta_function_args"); |
1044
|
0
|
|
|
|
|
|
for my $v (keys %{ $delta_function_args }) { |
|
0
|
|
|
|
|
|
|
1045
|
0
|
|
|
|
|
|
my $dv = ''; |
1046
|
0
|
0
|
|
|
|
|
$dv = $delta_function_args->{$v} |
1047
|
|
|
|
|
|
|
if defined $delta_function_args->{$v}; |
1048
|
0
|
|
|
|
|
|
$self->debug(qq{get_delta_for_value: $v => '$dv'}); |
1049
|
|
|
|
|
|
|
} |
1050
|
|
|
|
|
|
|
} |
1051
|
|
|
|
|
|
|
|
1052
|
|
|
|
|
|
|
# Call delta function; could be built in, could be user-provided |
1053
|
0
|
|
|
|
|
|
my $cdf = $self->_get_compute_delta_function(); |
1054
|
|
|
|
|
|
|
|
1055
|
0
|
|
|
|
|
|
my ( $new_cache_value, $delta ) = &{ $cdf }($self, $delta_function_args); |
|
0
|
|
|
|
|
|
|
1056
|
|
|
|
|
|
|
|
1057
|
0
|
|
|
|
|
|
$self->debug(qq{get_delta_for_value: } |
1058
|
|
|
|
|
|
|
. qq{new_cache_value:$new_cache_value delta:$delta}); |
1059
|
|
|
|
|
|
|
|
1060
|
0
|
|
|
|
|
|
$self->_store_in_cache($cache_id, $new_cache_value); |
1061
|
|
|
|
|
|
|
|
1062
|
0
|
|
|
|
|
|
return $delta; |
1063
|
|
|
|
|
|
|
|
1064
|
|
|
|
|
|
|
} |
1065
|
|
|
|
|
|
|
|
1066
|
|
|
|
|
|
|
=pod |
1067
|
|
|
|
|
|
|
|
1068
|
|
|
|
|
|
|
=head3 delta_compute_function |
1069
|
|
|
|
|
|
|
|
1070
|
|
|
|
|
|
|
Default delta computation function; used if user does not provide a |
1071
|
|
|
|
|
|
|
delta compute function. This function will do the following: |
1072
|
|
|
|
|
|
|
* If no value was in the cache previous to this call, it will return |
1073
|
|
|
|
|
|
|
-0. |
1074
|
|
|
|
|
|
|
* If the current value is less than the cached value, the function will |
1075
|
|
|
|
|
|
|
return -0 and treat the case as a counter wrap. |
1076
|
|
|
|
|
|
|
* If neither of the above are true, the function will return the difference |
1077
|
|
|
|
|
|
|
between the current and previous values and store the current value in |
1078
|
|
|
|
|
|
|
the cache. |
1079
|
|
|
|
|
|
|
|
1080
|
|
|
|
|
|
|
Is this overly-simplistic? Yes :), and it is designed to be replaced by |
1081
|
|
|
|
|
|
|
your function that does a delta in a much more intelligent way. |
1082
|
|
|
|
|
|
|
|
1083
|
|
|
|
|
|
|
To replace this function with yours, subclass Nagios::Plugin::SNMP's |
1084
|
|
|
|
|
|
|
default delta_compute_function method OR pass in a reference to a function |
1085
|
|
|
|
|
|
|
via the 'process_deltas' hash passed to new(), e.g. |
1086
|
|
|
|
|
|
|
|
1087
|
|
|
|
|
|
|
my $plugin = Nagios::Plugin::SNMP->new({ |
1088
|
|
|
|
|
|
|
'delta_compute_function' => \&my_delta_function, |
1089
|
|
|
|
|
|
|
... |
1090
|
|
|
|
|
|
|
}); |
1091
|
|
|
|
|
|
|
|
1092
|
|
|
|
|
|
|
The delta computation function you create must accept the following |
1093
|
|
|
|
|
|
|
arguments: |
1094
|
|
|
|
|
|
|
* Reference to plugin instance (commonly called $self within a method) |
1095
|
|
|
|
|
|
|
* Hash reference with the following key value pairs: |
1096
|
|
|
|
|
|
|
* previous_value: Previous value (from the cache) |
1097
|
|
|
|
|
|
|
* current_value: Current value (from the user or the remote |
1098
|
|
|
|
|
|
|
SNMP agent) |
1099
|
|
|
|
|
|
|
* interval: How long check interval is in seconds |
1100
|
|
|
|
|
|
|
* previous_run_at: Unix timestamp representing the previous |
1101
|
|
|
|
|
|
|
time a value was stored |
1102
|
|
|
|
|
|
|
* key: Unique sub-key associated with this data, e.g. |
1103
|
|
|
|
|
|
|
the OID for the data. |
1104
|
|
|
|
|
|
|
|
1105
|
|
|
|
|
|
|
The function must return (as a 2-element list): |
1106
|
|
|
|
|
|
|
* Value to store in the cache |
1107
|
|
|
|
|
|
|
* Delta between the two values passed to the function |
1108
|
|
|
|
|
|
|
|
1109
|
|
|
|
|
|
|
Note that this means your function can put any additional information |
1110
|
|
|
|
|
|
|
in the value (and therefore cache) it would like as the function has |
1111
|
|
|
|
|
|
|
total control over computing the delta between the previous and current |
1112
|
|
|
|
|
|
|
values and control over what gets stored in the cache between runs of |
1113
|
|
|
|
|
|
|
the plugin. |
1114
|
|
|
|
|
|
|
|
1115
|
|
|
|
|
|
|
Example: |
1116
|
|
|
|
|
|
|
|
1117
|
|
|
|
|
|
|
sub my_better_function { |
1118
|
|
|
|
|
|
|
my ($self, $args_ref) = @_; |
1119
|
|
|
|
|
|
|
|
1120
|
|
|
|
|
|
|
my $previous_value = $args->{'previous_value'}; |
1121
|
|
|
|
|
|
|
my $current_value = $args->{'current_value'}; |
1122
|
|
|
|
|
|
|
my $interval = $args->{'interval'}; |
1123
|
|
|
|
|
|
|
my $previous_run_at = $args->{'previous_run_at'}; |
1124
|
|
|
|
|
|
|
my $key = $args->{'key'}; |
1125
|
|
|
|
|
|
|
|
1126
|
|
|
|
|
|
|
my ($value_to_store, $delta_value) = (); |
1127
|
|
|
|
|
|
|
|
1128
|
|
|
|
|
|
|
# ... code to compute ... |
1129
|
|
|
|
|
|
|
|
1130
|
|
|
|
|
|
|
return ($value_to_store, $delta_value); |
1131
|
|
|
|
|
|
|
} |
1132
|
|
|
|
|
|
|
|
1133
|
|
|
|
|
|
|
my $plugin = Nagios::Plugin::SNMP->new( |
1134
|
|
|
|
|
|
|
'shortname' => 'FOO', |
1135
|
|
|
|
|
|
|
'usage' => $usage, |
1136
|
|
|
|
|
|
|
'process_deltas' => { |
1137
|
|
|
|
|
|
|
'cache' => { |
1138
|
|
|
|
|
|
|
'type' => 'memcache', |
1139
|
|
|
|
|
|
|
... |
1140
|
|
|
|
|
|
|
} |
1141
|
|
|
|
|
|
|
'delta_compute_function' => \&my_delta_function |
1142
|
|
|
|
|
|
|
} |
1143
|
|
|
|
|
|
|
); |
1144
|
|
|
|
|
|
|
|
1145
|
|
|
|
|
|
|
=cut |
1146
|
|
|
|
|
|
|
|
1147
|
|
|
|
|
|
|
sub delta_compute_function { |
1148
|
0
|
|
|
0
|
1
|
|
my ($self, $args) = @_; |
1149
|
|
|
|
|
|
|
|
1150
|
0
|
|
|
|
|
|
my $previous_value = $args->{'previous_value'}; |
1151
|
0
|
|
|
|
|
|
my $current_value = $args->{'current_value'}; |
1152
|
0
|
|
|
|
|
|
my $interval = $args->{'interval'}; |
1153
|
0
|
|
|
|
|
|
my $previous_run_at = $args->{'previous_run_at'}; |
1154
|
0
|
|
|
|
|
|
my $key = $args->{'key'}; |
1155
|
|
|
|
|
|
|
|
1156
|
0
|
0
|
|
|
|
|
return ($current_value, q{-0}) if ! defined $previous_value; |
1157
|
0
|
0
|
|
|
|
|
return (q{-0}, q{-0}) if $current_value < $previous_value; |
1158
|
0
|
|
|
|
|
|
return ($current_value, ($current_value - $previous_value)); |
1159
|
|
|
|
|
|
|
|
1160
|
|
|
|
|
|
|
} |
1161
|
|
|
|
|
|
|
|
1162
|
|
|
|
|
|
|
=pod |
1163
|
|
|
|
|
|
|
|
1164
|
|
|
|
|
|
|
=head2 _get_compute_delta_function() |
1165
|
|
|
|
|
|
|
|
1166
|
|
|
|
|
|
|
Return user provided delta function reference if passed in or default |
1167
|
|
|
|
|
|
|
delta compute function. Validation of function is done in new(). |
1168
|
|
|
|
|
|
|
|
1169
|
|
|
|
|
|
|
=cut |
1170
|
|
|
|
|
|
|
|
1171
|
|
|
|
|
|
|
sub _get_compute_delta_function { |
1172
|
|
|
|
|
|
|
|
1173
|
0
|
|
|
0
|
|
|
my $self = shift; |
1174
|
|
|
|
|
|
|
|
1175
|
0
|
0
|
|
|
|
|
if (exists $self->{'_SNMP_PROCESS_DELTAS'}->{'callback'}) { |
1176
|
0
|
|
|
|
|
|
$self->debug("Using custom delta compute function"); |
1177
|
0
|
|
|
|
|
|
return $self->{'_SNMP_PROCESS_DELTAS'}->{'callback'}; |
1178
|
|
|
|
|
|
|
} |
1179
|
|
|
|
|
|
|
else { |
1180
|
0
|
|
|
|
|
|
$self->debug("Using built-in delta compute function"); |
1181
|
0
|
|
|
|
|
|
return \&delta_compute_function; |
1182
|
|
|
|
|
|
|
} |
1183
|
|
|
|
|
|
|
|
1184
|
|
|
|
|
|
|
} |
1185
|
|
|
|
|
|
|
|
1186
|
|
|
|
|
|
|
sub _snmp_ensure_is_connected { |
1187
|
|
|
|
|
|
|
|
1188
|
0
|
|
|
0
|
|
|
my $self = shift; |
1189
|
|
|
|
|
|
|
|
1190
|
0
|
0
|
0
|
|
|
|
if ( (! defined( $self->{'_SNMP_SESSIONS'}) ) || |
|
0
|
|
|
|
|
|
|
1191
|
|
|
|
|
|
|
( scalar(@{$self->{'_SNMP_SESSIONS'}}) == 0 ) ) { |
1192
|
|
|
|
|
|
|
|
1193
|
0
|
|
|
|
|
|
$self->connect(); |
1194
|
|
|
|
|
|
|
|
1195
|
|
|
|
|
|
|
} |
1196
|
|
|
|
|
|
|
|
1197
|
|
|
|
|
|
|
} |
1198
|
|
|
|
|
|
|
|
1199
|
|
|
|
|
|
|
sub close { |
1200
|
|
|
|
|
|
|
|
1201
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
1202
|
|
|
|
|
|
|
|
1203
|
0
|
0
|
|
|
|
|
if (defined $self->{'_SNMP_SESSIONS'}) { |
1204
|
|
|
|
|
|
|
|
1205
|
0
|
|
|
|
|
|
my @sessions = @{$self->{'_SNMP_SESSIONS'}}; |
|
0
|
|
|
|
|
|
|
1206
|
|
|
|
|
|
|
|
1207
|
0
|
|
|
|
|
|
for my $s (@sessions) { |
1208
|
0
|
|
|
|
|
|
$s->close(); |
1209
|
|
|
|
|
|
|
} |
1210
|
|
|
|
|
|
|
|
1211
|
|
|
|
|
|
|
# Ensure we release Net::SNMP memory |
1212
|
0
|
|
|
|
|
|
$self->{'_SNMP_SESSIONS'} = []; |
1213
|
|
|
|
|
|
|
|
1214
|
|
|
|
|
|
|
} |
1215
|
|
|
|
|
|
|
|
1216
|
0
|
|
|
|
|
|
return 1; |
1217
|
|
|
|
|
|
|
|
1218
|
|
|
|
|
|
|
} |
1219
|
|
|
|
|
|
|
|
1220
|
|
|
|
|
|
|
# Overloaded methods |
1221
|
|
|
|
|
|
|
|
1222
|
|
|
|
|
|
|
sub getopts { |
1223
|
|
|
|
|
|
|
|
1224
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
1225
|
|
|
|
|
|
|
|
1226
|
0
|
|
|
|
|
|
$self->SUPER::getopts(); |
1227
|
|
|
|
|
|
|
|
1228
|
|
|
|
|
|
|
# Now validate our options |
1229
|
0
|
|
|
|
|
|
$self->_snmp_validate_opts(); |
1230
|
|
|
|
|
|
|
|
1231
|
|
|
|
|
|
|
# If user requested delta processing to be done ('process_deltas' hash |
1232
|
|
|
|
|
|
|
# ref passed to new()), start a cache instance. All validation of cache |
1233
|
|
|
|
|
|
|
# options is done in new via _setup_delta_cache_options. |
1234
|
0
|
0
|
|
|
|
|
$self->_initialize_delta_cache() |
1235
|
|
|
|
|
|
|
if exists $self->{'_SNMP_PROCESS_DELTAS'}; |
1236
|
|
|
|
|
|
|
|
1237
|
|
|
|
|
|
|
# Have to show this debug message here, can't do it in new() as |
1238
|
|
|
|
|
|
|
# we haven't triggered Nagios::Plugin to do option processing yet. |
1239
|
0
|
0
|
|
|
|
|
if (defined $self->{'_SNMP_PROCESS_DELTAS'}) { |
1240
|
|
|
|
|
|
|
|
1241
|
0
|
|
|
|
|
|
my $co = $self->{'_SNMP_PROCESS_DELTAS'}; |
1242
|
|
|
|
|
|
|
|
1243
|
|
|
|
|
|
|
} |
1244
|
|
|
|
|
|
|
|
1245
|
|
|
|
|
|
|
# Start a plugin-level timer if we have one; |
1246
|
|
|
|
|
|
|
# we will silently ignore the request if the |
1247
|
|
|
|
|
|
|
# timeout option is not specified by the user. |
1248
|
0
|
|
|
|
|
|
$self->start_timer(); |
1249
|
|
|
|
|
|
|
|
1250
|
|
|
|
|
|
|
} |
1251
|
|
|
|
|
|
|
|
1252
|
|
|
|
|
|
|
=pod |
1253
|
|
|
|
|
|
|
|
1254
|
|
|
|
|
|
|
=head3 get_sys_info() |
1255
|
|
|
|
|
|
|
|
1256
|
|
|
|
|
|
|
my ($descr, $object_id) = $plugin->get_sys_info(); |
1257
|
|
|
|
|
|
|
|
1258
|
|
|
|
|
|
|
Returns the sysDescr.0 and sysObjectId.0 OIDs from the remote |
1259
|
|
|
|
|
|
|
agent, the sysObjectId.0 OID is translated to an OS family; string |
1260
|
|
|
|
|
|
|
returned will be one of: |
1261
|
|
|
|
|
|
|
|
1262
|
|
|
|
|
|
|
* hpux |
1263
|
|
|
|
|
|
|
* sunos4 |
1264
|
|
|
|
|
|
|
* solaris |
1265
|
|
|
|
|
|
|
* osf |
1266
|
|
|
|
|
|
|
* ultrix |
1267
|
|
|
|
|
|
|
* hpux10 |
1268
|
|
|
|
|
|
|
* netbsd1 |
1269
|
|
|
|
|
|
|
* freebsd |
1270
|
|
|
|
|
|
|
* irix |
1271
|
|
|
|
|
|
|
* linux |
1272
|
|
|
|
|
|
|
* bsdi |
1273
|
|
|
|
|
|
|
* openbsd |
1274
|
|
|
|
|
|
|
* win32 |
1275
|
|
|
|
|
|
|
* hpux11 |
1276
|
|
|
|
|
|
|
* unknown |
1277
|
|
|
|
|
|
|
|
1278
|
|
|
|
|
|
|
sysDescr.0 is a free-text description containing more specific |
1279
|
|
|
|
|
|
|
information on the OS being queried. |
1280
|
|
|
|
|
|
|
|
1281
|
|
|
|
|
|
|
=cut |
1282
|
|
|
|
|
|
|
|
1283
|
|
|
|
|
|
|
sub get_sys_info { |
1284
|
|
|
|
|
|
|
|
1285
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
1286
|
|
|
|
|
|
|
|
1287
|
0
|
|
|
|
|
|
my %oids = qw( |
1288
|
|
|
|
|
|
|
sysdescr .1.3.6.1.2.1.1.1.0 |
1289
|
|
|
|
|
|
|
sysobjectid .1.3.6.1.2.1.1.2.0 |
1290
|
|
|
|
|
|
|
); |
1291
|
|
|
|
|
|
|
|
1292
|
0
|
|
|
|
|
|
my $result = $self->get(values %oids); |
1293
|
|
|
|
|
|
|
|
1294
|
0
|
|
|
|
|
|
return ($OS_TYPES{$result->{$oids{'sysobjectid'}}}, |
1295
|
|
|
|
|
|
|
$result->{$oids{'sysdescr'}}); |
1296
|
|
|
|
|
|
|
|
1297
|
|
|
|
|
|
|
} |
1298
|
|
|
|
|
|
|
|
1299
|
|
|
|
|
|
|
sub _ensure_defined_results { |
1300
|
0
|
|
|
0
|
|
|
my ($host, $results_hash_ref) = @_; |
1301
|
|
|
|
|
|
|
|
1302
|
0
|
|
|
|
|
|
my @errors = (); |
1303
|
|
|
|
|
|
|
|
1304
|
0
|
|
|
|
|
|
for my $oid (sort keys %{$results_hash_ref}) { |
|
0
|
|
|
|
|
|
|
1305
|
0
|
|
|
|
|
|
my $value = $results_hash_ref->{$oid}; |
1306
|
0
|
0
|
|
|
|
|
if ($value =~ m/nosuch/msi) { |
1307
|
0
|
|
|
|
|
|
push(@errors, "${host}:${oid} $value"); |
1308
|
|
|
|
|
|
|
} |
1309
|
|
|
|
|
|
|
} |
1310
|
|
|
|
|
|
|
|
1311
|
0
|
0
|
|
|
|
|
return (scalar(@errors) == 0) ? undef : join(', ', @errors); |
1312
|
|
|
|
|
|
|
|
1313
|
|
|
|
|
|
|
} |
1314
|
|
|
|
|
|
|
|
1315
|
|
|
|
|
|
|
sub debug { |
1316
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
1317
|
0
|
0
|
|
|
|
|
return unless $self->opts->get('snmp-debug') == 1; |
1318
|
|
|
|
|
|
|
|
1319
|
0
|
|
|
|
|
|
my $msg = shift; |
1320
|
0
|
|
|
|
|
|
print STDERR scalar(localtime()) . qq{: $msg\n}; |
1321
|
|
|
|
|
|
|
|
1322
|
|
|
|
|
|
|
} |
1323
|
|
|
|
|
|
|
|
1324
|
|
|
|
|
|
|
=pod |
1325
|
|
|
|
|
|
|
|
1326
|
|
|
|
|
|
|
=head1 AUTHORS |
1327
|
|
|
|
|
|
|
|
1328
|
|
|
|
|
|
|
* Max Schubert (maxschube@cpan.org) |
1329
|
|
|
|
|
|
|
* Ryan Richins |
1330
|
|
|
|
|
|
|
* Shaofeng Yang |
1331
|
|
|
|
|
|
|
|
1332
|
|
|
|
|
|
|
=head1 Special Thanks |
1333
|
|
|
|
|
|
|
|
1334
|
|
|
|
|
|
|
Special thanks to my teammates Ryan Richins and Shaofeng Yang at Comcast |
1335
|
|
|
|
|
|
|
for their significant contributions to this module and to my managers |
1336
|
|
|
|
|
|
|
Jason Livingood and Mike Fischer at Comcast for allowing our team to |
1337
|
|
|
|
|
|
|
contribute code we have created or modified at work back to the open |
1338
|
|
|
|
|
|
|
source community. If you live in the northern Virginia area and are |
1339
|
|
|
|
|
|
|
a talented developer / systems administrator, Comcast is hiring :). |
1340
|
|
|
|
|
|
|
|
1341
|
|
|
|
|
|
|
=cut |
1342
|
|
|
|
|
|
|
|
1343
|
|
|
|
|
|
|
1; |