sensu-plugin-collectd 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/Gemfile +3 -0
- data/README.md +120 -0
- data/Rakefile +2 -0
- data/bin/check-collectd-socket-metric.rb +467 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/sensu-plugin-collectd/version.rb +11 -0
- data/lib/sensu-plugin-collectd.rb +1 -0
- data/sensu-plugin-collectd.gemspec +29 -0
- metadata +109 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: dd0018fa8411d7d0d1d4c4c2f3f3318cd1d2049e
|
4
|
+
data.tar.gz: 27988bf80e4aa802886b2221448670e354998a8d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: bedd741b536d70648592a5fc9a41b6a0fae81a646ad96df59178f4c081d5ecec5490f2dbdfafa3e542a13a1c67117e77ad423dec2ee462acf844b0fcc75d95fc
|
7
|
+
data.tar.gz: 6eda7d5fdffd59bc5bf0ffdaf524b3bff9250a3394ca8d4275e64b88a910216a9c2e9b46e3a6706f3a6f36e828e07d5f96316045b45d366784c5ad523ee5933e
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
# sensu-plugin-collectd
|
2
|
+
Sensu plugin that reads a particular metric from collectd, using the collectd
|
3
|
+
socket, and ensures that it is bellow the given critical and warning thresholds.
|
4
|
+
If it is not, it will generate either a critical or warning alert in sensu.
|
5
|
+
Otherwise, it will generate a sensu ok.
|
6
|
+
|
7
|
+
See:
|
8
|
+
https://github.com/sensu-plugins
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
Use the latest version of the `check-collectd-socket-metric.rb` from releases.
|
12
|
+
See the [sensu installation instructions](http://sensu-plugins.io/docs/installation_instructions.html)
|
13
|
+
|
14
|
+
## Collectd: what you need to know to use this plugin
|
15
|
+
To use this sensu check you need to be at least vaguely familiar with collectd. You can check [the docs](https://collectd.org/documentation.shtml). What you will need to know is the name of the metric, and the metric from the list you want to alert on. If you want to see all the metrics available in a machine:
|
16
|
+
|
17
|
+
```
|
18
|
+
~$ collectdctl LISTVAL
|
19
|
+
server-name-1-ip-172-1-2-3/collectd-cache/cache_size
|
20
|
+
server-name-1-ip-172-1-2-3/collectd-write_queue/derive-dropped
|
21
|
+
server-name-1-ip-172-1-2-3/collectd-write_queue/queue_length
|
22
|
+
server-name-1-ip-172-1-2-3/contextswitch/contextswitch
|
23
|
+
server-name-1-ip-172-1-2-3/cpu-0/cpu-idle
|
24
|
+
server-name-1-ip-172-1-2-3/cpu-0/cpu-interrupt
|
25
|
+
server-name-1-ip-172-1-2-3/cpu-0/cpu-nice
|
26
|
+
server-name-1-ip-172-1-2-3/cpu-0/cpu-softirq
|
27
|
+
server-name-1-ip-172-1-2-3/cpu-0/cpu-steal
|
28
|
+
server-name-1-ip-172-1-2-3/cpu-0/cpu-system
|
29
|
+
server-name-1-ip-172-1-2-3/cpu-0/cpu-user
|
30
|
+
server-name-1-ip-172-1-2-3/cpu-0/cpu-wait
|
31
|
+
server-name-1-ip-172-1-2-3/cpu-1/cpu-idle
|
32
|
+
server-name-1-ip-172-1-2-3/cpu-1/cpu-interrupt
|
33
|
+
server-name-1-ip-172-1-2-3/cpu-1/cpu-nice
|
34
|
+
server-name-1-ip-172-1-2-3/cpu-1/cpu-softirq
|
35
|
+
server-name-1-ip-172-1-2-3/cpu-1/cpu-steal
|
36
|
+
server-name-1-ip-172-1-2-3/cpu-1/cpu-system
|
37
|
+
server-name-1-ip-172-1-2-3/cpu-1/cpu-user
|
38
|
+
server-name-1-ip-172-1-2-3/cpu-1/cpu-wait
|
39
|
+
|
40
|
+
[...]
|
41
|
+
```
|
42
|
+
|
43
|
+
To query a particular metric:
|
44
|
+
```
|
45
|
+
~$ collectdctl GETVAL server-name-1-ip-172-1-2-3//processes-logstash/ps_count
|
46
|
+
processes=1.000000e+00
|
47
|
+
threads=7.900000e+01
|
48
|
+
```
|
49
|
+
|
50
|
+
If you want to alert on `server-name-1-ip-172-1-2-3/collectd-cache/cache_size`, youwill need to pass down the metric `collectd-cache/cache_size`
|
51
|
+
|
52
|
+
## Usage
|
53
|
+
You can run the following to display the help
|
54
|
+
```
|
55
|
+
check-collectd-socket-metric.rb -h
|
56
|
+
```
|
57
|
+
|
58
|
+
You will need to provide a warning threshold and a critical threshold, a metric ID or a regular expresion to match the metric. Optionally, you can also provide a value for the metric (if the metric id will return a list of metrics). But default it will alert on `value`
|
59
|
+
|
60
|
+
### Mandatory parameters
|
61
|
+
1. Metric id (-m --metric) or regular expression (-r --regexp). One, and only one of them must be provided.
|
62
|
+
|
63
|
+
If you want to alert on `server-name-1-ip-172-1-2-3/collectd-cache/cache_size`, youwill need to pass down the metric `-m collectd-cache/cache_size`
|
64
|
+
|
65
|
+
Alternative, a very simple regular expresion can be used: you can use `*` as a wildcard to match serveral metrics, and alert on the highest value of all of them. Only `*` as a wildcard is supported and tested. Anything else can produce unexpected behaviour. You can find an example at the end of this document. Please, ensure you test the regular expression (manually run the check) before starting to use it.
|
66
|
+
|
67
|
+
2. Warning threshold (-w --warning): warning threshold to alert on. If the metric is higher than the warning threshold, the plugin will send a warning to sensu.
|
68
|
+
|
69
|
+
3. Critical threshold (-c --critical): critical threshold to alert on. If the metric is higher than the critical threshold, the plugin will send a critical to sensu. It will only send a critical, and not a critical and a warning.
|
70
|
+
|
71
|
+
### Optional parameters
|
72
|
+
|
73
|
+
1. Path to the collectd socket (-s --socket). By default `/var/run/collectd-unisock`. If it is in a different location, this option can be used to change it.
|
74
|
+
2. Timeout (-t --timeout). 20 seconds by default. The check will timeout if it runs more than the timeout limit, sending a critical to sensu. It is provided in seconds.
|
75
|
+
3. Metric value from the metric list (-d --data_name). By default, it is `value`. If there are multiple values and we need to select one, we can use this option.
|
76
|
+
|
77
|
+
|
78
|
+
|
79
|
+
### Examples
|
80
|
+
* Alerting on a single metric
|
81
|
+
If you want to alert on `server-name-1-ip-172-1-2-3/collectd-cache/cache_size`, you will need to pass down the metric `collectd-cache/cache_size`
|
82
|
+
```bash
|
83
|
+
collectd_plugin.rb -m /uptime/uptime -c 200400 -w 190000
|
84
|
+
```
|
85
|
+
We can expect an output like:
|
86
|
+
```bash
|
87
|
+
CheckCollectdSocket CRITICAL: host-name_1-ip-172-1-2-3/uptime/uptime[value] = = 192291.00 is over the warning limit (190000.00)
|
88
|
+
```
|
89
|
+
|
90
|
+
* Alerting in a set of metrics
|
91
|
+
If for example, we have 8 CPUs, and we want to alert if any of them goes above certain usage, we can use the regexp option:
|
92
|
+
```bash
|
93
|
+
collectd_plugin.rb -r "cpu-*/cpu-idle" -c 99 -w 80
|
94
|
+
```
|
95
|
+
We can expect an output like:
|
96
|
+
```bash
|
97
|
+
CheckCollectdSocket WARNING: clickhouse-server-shard_1-ip-172-26-161-164/cpu-4/cpu-idle[value] = 97.93 is over the warning limit (80.00)
|
98
|
+
```
|
99
|
+
|
100
|
+
*. Specifying a different metric value
|
101
|
+
If we have a ps_count metric that will return processes and threads, we can alert on the number of threads by setting the data_name:
|
102
|
+
```bash
|
103
|
+
collectd_plugin.rb -m processes-collectd/ps_count -c 10 -w 8 -d threads
|
104
|
+
```
|
105
|
+
It will return something like this:
|
106
|
+
```bash
|
107
|
+
CheckCollectdSocket CRITICAL: clickhouse-server-shard_1-ip-172-26-161-164/processes-collectd/ps_count[threads] = 12.00 is over the critical limit (10.00)
|
108
|
+
```
|
109
|
+
|
110
|
+
*. Changing the socket path
|
111
|
+
If the collectd socket is in a different location to the default, we can set it:
|
112
|
+
```bash
|
113
|
+
collectd_plugin.rb -m processes-collectd/ps_count -c 10 -w 8 -d threads -s /var/run/collectd-unisock
|
114
|
+
```
|
115
|
+
|
116
|
+
*. Changing the default timeout
|
117
|
+
We can set a custom timeout for the check with --timeout (seconds)
|
118
|
+
```bash
|
119
|
+
collectd_plugin.rb -m processes-collectd/ps_count -c 10 -w 8 -d threads -t 10
|
120
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,467 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# check-collectd-socket-metric.rb
|
4
|
+
#
|
5
|
+
# DESCRIPTION:
|
6
|
+
# This plugin retrieves a metric from the collectd socket, and verifies
|
7
|
+
# that it is within the provided thresholds (critical and warning)
|
8
|
+
#
|
9
|
+
# OUTPUT:
|
10
|
+
# plain text
|
11
|
+
#
|
12
|
+
# PLATFORMS:
|
13
|
+
# Linux
|
14
|
+
#
|
15
|
+
# DEPENDENCIES:
|
16
|
+
# gem: sensu-plugin
|
17
|
+
# gem: rspec (for the tests)
|
18
|
+
#
|
19
|
+
# USAGE:
|
20
|
+
# check-collectd-socket-metric.rb (-m <metric_id> | -r <metric id regexp>)
|
21
|
+
# -w <warning threshold> -c <critical threshold> [-d <metric value>]
|
22
|
+
# [-t <timeout>] [-s <socket path>]
|
23
|
+
#
|
24
|
+
# LICENSE:
|
25
|
+
# Copyright: Maria Pilar Gomez Moya (mp.gomezmoya@gmail.com) and Bartosz Lassak
|
26
|
+
# Released under the same terms as Sensu (the MIT license); see LICENSE
|
27
|
+
# for details.
|
28
|
+
#
|
29
|
+
|
30
|
+
require 'sensu-plugin/check/cli'
|
31
|
+
require 'net/http'
|
32
|
+
require 'socket'
|
33
|
+
require 'timeout'
|
34
|
+
|
35
|
+
# Encapsulates the interaction with the IOBuffer so we can have something
|
36
|
+
# we can mock in the tests, something that we can open and close (effectively
|
37
|
+
# clearing the buffer) and is more decaupled from the main class
|
38
|
+
class SocketWrapper
|
39
|
+
attr_reader :socket_path
|
40
|
+
|
41
|
+
def initialize(
|
42
|
+
socket_path
|
43
|
+
)
|
44
|
+
@socket = nil
|
45
|
+
@socket_path = socket_path
|
46
|
+
end
|
47
|
+
|
48
|
+
def open
|
49
|
+
@socket = Net::BufferedIO.new(UNIXSocket.new(@socket_path))
|
50
|
+
end
|
51
|
+
|
52
|
+
def close
|
53
|
+
if @socket != nil
|
54
|
+
@socket.close
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def readline
|
59
|
+
return @socket.readline
|
60
|
+
end
|
61
|
+
|
62
|
+
def write(line)
|
63
|
+
return @socket.write(line)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Class with all the logic, separate from the actual Sensu Check, so it can be
|
68
|
+
# properly tested
|
69
|
+
class CheckCollectdComponent
|
70
|
+
def initialize(
|
71
|
+
socket,
|
72
|
+
critical,
|
73
|
+
warning,
|
74
|
+
metric,
|
75
|
+
regexp,
|
76
|
+
data_name,
|
77
|
+
timeout,
|
78
|
+
cli_handler
|
79
|
+
)
|
80
|
+
@socket = socket
|
81
|
+
@critical = critical
|
82
|
+
@warning = warning
|
83
|
+
@metric = metric
|
84
|
+
@regexp = regexp
|
85
|
+
@data_name = data_name
|
86
|
+
@timeout = timeout
|
87
|
+
@cli_handler = cli_handler
|
88
|
+
@is_regexp = false
|
89
|
+
end
|
90
|
+
|
91
|
+
def critical(message)
|
92
|
+
if @cli_handler != nil
|
93
|
+
@cli_handler.critical message
|
94
|
+
end
|
95
|
+
if @socket != nil
|
96
|
+
@socket.close
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def warning(message)
|
101
|
+
if @cli_handler != nil
|
102
|
+
@cli_handler.warning message
|
103
|
+
end
|
104
|
+
if @socket != nil
|
105
|
+
@socket.close
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def ok(message)
|
110
|
+
@cli_handler.ok message
|
111
|
+
@socket.close
|
112
|
+
end
|
113
|
+
|
114
|
+
def isFloat(string_option)
|
115
|
+
# We want to know if the option passed down in command line can be
|
116
|
+
# safely converted to a valid float. If not, we want to catch the
|
117
|
+
# exception and return an error, finishing the script.
|
118
|
+
begin
|
119
|
+
return !!Float(string_option)
|
120
|
+
rescue
|
121
|
+
return false
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def formatedFloatString(float_number)
|
126
|
+
return "#{float_number.round(2)}"
|
127
|
+
end
|
128
|
+
|
129
|
+
# Verifies that the class attributes are valid: that they are not nil,
|
130
|
+
# and they are the expected data type (so we are not trying to use
|
131
|
+
# strings in the place of integers)
|
132
|
+
def validateArguments()
|
133
|
+
# XXX extract error messages to different file
|
134
|
+
# NULL checks
|
135
|
+
if @metric == nil and @regexp == nil
|
136
|
+
return false, "Metric and regexp can't be both empty"
|
137
|
+
end
|
138
|
+
if @socket == nil
|
139
|
+
return false, "The socket can't be empty"
|
140
|
+
end
|
141
|
+
if @critical == nil
|
142
|
+
return false, "Critical can't be empty"
|
143
|
+
end
|
144
|
+
if @warning == nil
|
145
|
+
return false, "Warning can't be empty"
|
146
|
+
end
|
147
|
+
if @data_name == nil
|
148
|
+
return false, "Data name can't be empty"
|
149
|
+
end
|
150
|
+
if @timeout == nil
|
151
|
+
return false, "Timeout can't be empty"
|
152
|
+
end
|
153
|
+
if @cli_handler == nil
|
154
|
+
return false, "The cli handler can't be empty"
|
155
|
+
end
|
156
|
+
# Validate data type
|
157
|
+
if !self.isFloat(@critical)
|
158
|
+
return false, "Critical has to be a number"
|
159
|
+
elsif @critical.to_f < 0
|
160
|
+
return false, "Critical has to be a positive number"
|
161
|
+
end
|
162
|
+
if !self.isFloat(@warning)
|
163
|
+
return false, "Warning has to be a number"
|
164
|
+
elsif @warning.to_f < 0
|
165
|
+
return false, "Warning has to be a positive number"
|
166
|
+
end
|
167
|
+
if !self.isFloat(@timeout)
|
168
|
+
return false, "Timeout has to be a number"
|
169
|
+
elsif @timeout.to_f < 0
|
170
|
+
return false, "Timeout has to be a positive number"
|
171
|
+
end
|
172
|
+
if @metric != nil and !(@metric.instance_of? String)
|
173
|
+
return false, "Metric has to be a string"
|
174
|
+
end
|
175
|
+
if @regexp != nil and !(@regexp.instance_of? String)
|
176
|
+
return false, "Regexp has to be a string"
|
177
|
+
end
|
178
|
+
# Validate that metric and regexp are not both set at the same time
|
179
|
+
if (@metric != nil and @metric != "") and (@regexp != nil and @regexp != "")
|
180
|
+
return false, "Only one of the options, metric or regexp, can be provided"
|
181
|
+
end
|
182
|
+
return true, ""
|
183
|
+
end
|
184
|
+
|
185
|
+
# Formats arguments and ensures that they have the correct data type
|
186
|
+
def formatArguments()
|
187
|
+
self.prependSlashInMetric
|
188
|
+
@critical = @critical.to_f
|
189
|
+
@warning = @warning.to_f
|
190
|
+
@timeout = @timeout.to_f
|
191
|
+
if @regexp != nil and @regexp != ""
|
192
|
+
@is_regexp = true
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
# Reads the first line of the results of either GETVAL or LISTVAL
|
197
|
+
# and parses the number of lines that need to be read
|
198
|
+
#
|
199
|
+
# Returns the number of lines to be read in the socket
|
200
|
+
def getNumResultsInSocket(line)
|
201
|
+
# The first line will be the number of results in the format
|
202
|
+
# "XXX Values found". We need that number to loop and parse every single entry
|
203
|
+
# end
|
204
|
+
return (line.strip.split(" ")[0]).to_i
|
205
|
+
end
|
206
|
+
|
207
|
+
# Ensures that the metric string starts with "/" so the metric_id is
|
208
|
+
# constructer correctly
|
209
|
+
def prependSlashInMetric
|
210
|
+
if @metric != nil and @metric != ""
|
211
|
+
if @metric[0] != "/"
|
212
|
+
@metric = "/" + @metric
|
213
|
+
end
|
214
|
+
end
|
215
|
+
if @regexp != nil and @regexp != ""
|
216
|
+
if @regexp[0] != "/"
|
217
|
+
@regexp = "/" + @regexp
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
# Concatenates hostname and metric to get the metric id to pass down to
|
223
|
+
# the collectd GETVAL
|
224
|
+
#
|
225
|
+
# Returns the fully constructed metric id
|
226
|
+
def buildMetricId(socket)
|
227
|
+
socket.readline
|
228
|
+
hostname = socket.readline.strip.split(" ")[1].strip.split("/")[0]
|
229
|
+
if @is_regexp
|
230
|
+
return hostname + @regexp
|
231
|
+
else
|
232
|
+
return hostname + @metric
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
# Reads the results of the GETVAL from the socket, and builds a map
|
237
|
+
# with all the values returned for the given metric id
|
238
|
+
#
|
239
|
+
# Returns a key value map with the values list
|
240
|
+
def buildValueHash(socket)
|
241
|
+
line = socket.readline
|
242
|
+
if line == "ERROR: Server error: No such value."
|
243
|
+
return nil
|
244
|
+
end
|
245
|
+
num_lines = self.getNumResultsInSocket(line)
|
246
|
+
values = Hash.new
|
247
|
+
for i in 1..num_lines
|
248
|
+
rawline = socket.readline
|
249
|
+
line = rawline.to_s.split("=")
|
250
|
+
values[:"#{line[0]}"] = "%f" % line[1].to_f
|
251
|
+
end
|
252
|
+
return values
|
253
|
+
end
|
254
|
+
|
255
|
+
def getMetricId
|
256
|
+
@socket.write("LISTVAL\n")
|
257
|
+
metric_id = buildMetricId(@socket)
|
258
|
+
@socket.close
|
259
|
+
@socket.open
|
260
|
+
return metric_id
|
261
|
+
end
|
262
|
+
|
263
|
+
def getMetric(metric_id)
|
264
|
+
query = "GETVAL #{metric_id}\n"
|
265
|
+
@socket.write(query)
|
266
|
+
return self.buildValueHash(@socket)
|
267
|
+
end
|
268
|
+
|
269
|
+
def escapeMetricRegexp(metric_regexp)
|
270
|
+
return metric_regexp.to_s.gsub("/", "\\/").gsub("*", ".*")
|
271
|
+
end
|
272
|
+
|
273
|
+
def findMetricMatches()
|
274
|
+
@socket.write("LISTVAL\n")
|
275
|
+
rawline = @socket.readline
|
276
|
+
num_lines = self.getNumResultsInSocket(rawline)
|
277
|
+
metrics = Array.new
|
278
|
+
metric_id_regex = self.escapeMetricRegexp(@regexp)
|
279
|
+
metric_id_regex = ".*?#{metric_id_regex}"
|
280
|
+
for i in 1..num_lines
|
281
|
+
metric = @socket.readline.strip.split(" ")[1]
|
282
|
+
if metric.match(metric_id_regex)
|
283
|
+
metrics.push(metric)
|
284
|
+
end
|
285
|
+
end
|
286
|
+
@socket.close
|
287
|
+
@socket.open
|
288
|
+
return metrics
|
289
|
+
end
|
290
|
+
|
291
|
+
def getMaxMetric(metric_ids)
|
292
|
+
all_metrics = Array.new
|
293
|
+
values = nil
|
294
|
+
highest_metric = ""
|
295
|
+
for metric_id in metric_ids
|
296
|
+
new_values = self.getMetric(metric_id)
|
297
|
+
if values == nil
|
298
|
+
highest_metric = metric_id
|
299
|
+
values = new_values
|
300
|
+
elsif new_values[:"#{@data_name}"] > values[:"#{@data_name}"]
|
301
|
+
highest_metric = metric_id
|
302
|
+
values = new_values
|
303
|
+
end
|
304
|
+
end
|
305
|
+
return highest_metric, values
|
306
|
+
end
|
307
|
+
|
308
|
+
# Runs the sensu check, sending the calling sensu with either critical,
|
309
|
+
# warning or ok.
|
310
|
+
#
|
311
|
+
# XXX use exceptions in case of error instead of generating a critical and
|
312
|
+
# just returning
|
313
|
+
def run_check
|
314
|
+
begin
|
315
|
+
@socket.open
|
316
|
+
rescue => e
|
317
|
+
self.critical "Tried to access UNIX domain socket (#{@socket.socket_path}) but failed: #{e}"
|
318
|
+
return
|
319
|
+
end
|
320
|
+
|
321
|
+
begin
|
322
|
+
values = nil
|
323
|
+
metric_id = ""
|
324
|
+
# If we are querying an specific metric...
|
325
|
+
if not @is_regexp
|
326
|
+
# Read a metric
|
327
|
+
metric_id = self.getMetricId
|
328
|
+
# If the metric hasn't been found, error
|
329
|
+
if metric_id == nil
|
330
|
+
# Should never happen. We may end up in the rescue if we have any trouble
|
331
|
+
# with the socket, and that would be the only case where the metric could
|
332
|
+
# be null.
|
333
|
+
self.critical "Failed to build the metric id for #{@metric}"
|
334
|
+
return
|
335
|
+
end
|
336
|
+
values = self.getMetric(metric_id)
|
337
|
+
# If we are matching a regular expression:w
|
338
|
+
else
|
339
|
+
metrics_ids = self.findMetricMatches
|
340
|
+
metric_id, values = getMaxMetric(metrics_ids)
|
341
|
+
end
|
342
|
+
@socket.close
|
343
|
+
if values == nil
|
344
|
+
self.critical "The metric #{@metric} does not exist in this host."
|
345
|
+
return
|
346
|
+
end
|
347
|
+
rescue => e
|
348
|
+
self.critical "An error occured while trying to use the socket (#{@socket.socket_path}) : #{e}"
|
349
|
+
return
|
350
|
+
end
|
351
|
+
# Check critical threshold
|
352
|
+
current_value = values[:"#{@data_name}"]
|
353
|
+
if current_value == nil or current_value == ""
|
354
|
+
self.critical "Metric value #{@data_name} not found in the list"
|
355
|
+
return
|
356
|
+
end
|
357
|
+
current_value = current_value.to_f
|
358
|
+
if current_value.to_f > @critical
|
359
|
+
self.critical "#{metric_id}[#{@data_name}] = #{'%.2f' % current_value} is over the critical limit (#{'%.2f' % @critical})"
|
360
|
+
return
|
361
|
+
end
|
362
|
+
if current_value > @warning
|
363
|
+
self.warning "#{metric_id}[#{@data_name}] = #{'%.2f' % current_value} is over the warning limit (#{'%.2f' % @warning})"
|
364
|
+
return
|
365
|
+
end
|
366
|
+
if @is_regexp
|
367
|
+
self.ok "Everything matching #{@regexp} is within threshold"
|
368
|
+
else
|
369
|
+
self.ok "#{metric_id} is within threshold"
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
# Main function. It will validate the class attribute, ensure they have
|
374
|
+
# the correct format, and run the check with a timeout.
|
375
|
+
def run
|
376
|
+
# First, validate that the arguments passed to the handler are valid.
|
377
|
+
# If the are not, print an error and exit
|
378
|
+
is_valid, error_message = self.validateArguments
|
379
|
+
if !is_valid
|
380
|
+
puts "ERROR: #{error_message}"
|
381
|
+
self.warning("Wrong check: #{error_message}")
|
382
|
+
return
|
383
|
+
end
|
384
|
+
# Ensure correct data types and format
|
385
|
+
self.formatArguments
|
386
|
+
# Run the check with a timeout
|
387
|
+
begin
|
388
|
+
Timeout::timeout(@timeout.to_i) do
|
389
|
+
self.run_check
|
390
|
+
end
|
391
|
+
rescue => e
|
392
|
+
@socket.close
|
393
|
+
@cli_handler.critical "#{@metric} timed out"
|
394
|
+
end
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
398
|
+
# Sensu check. The only thing it does is initialize the socket,
|
399
|
+
# parses the command lines arguments and instantiates the component,
|
400
|
+
# that will do the actual run()
|
401
|
+
class CheckCollectdSocket < Sensu::Plugin::Check::CLI
|
402
|
+
option :socket,
|
403
|
+
description: 'Supervisor UNIX domain socket (optional)',
|
404
|
+
short: '-s SOCKET',
|
405
|
+
long: '--socket SOCKET',
|
406
|
+
default: '/var/run/collectd-unixsock'
|
407
|
+
|
408
|
+
option :critical,
|
409
|
+
description: 'Value over wich the metric generate a critical alert',
|
410
|
+
short: '-c <critical threshold>',
|
411
|
+
long: '--crtical <critical threshold>'
|
412
|
+
|
413
|
+
option :warning,
|
414
|
+
description: 'Value over wich the metric generate a warning alert',
|
415
|
+
short: '-w <warning threshold>',
|
416
|
+
long: '--warning <warning threshold>'
|
417
|
+
|
418
|
+
option :metric,
|
419
|
+
description: 'Metric we want to evaluate, for example, \"processes-carbon-clickhouse/ps_count\"',
|
420
|
+
short: '-m <metric id>',
|
421
|
+
long: '--metric <metric id>',
|
422
|
+
default: nil
|
423
|
+
|
424
|
+
option :data_name,
|
425
|
+
description: 'When expect a list of values in that metric, data name must be given. Default is \"value\"',
|
426
|
+
short: '-d <data name>',
|
427
|
+
long: '--data_name <data name>',
|
428
|
+
default: 'value'
|
429
|
+
|
430
|
+
option :timeout,
|
431
|
+
description: 'Socket timeout connection',
|
432
|
+
short: '-t <seconds>',
|
433
|
+
long: '--timeout <seconds>',
|
434
|
+
default: 20
|
435
|
+
|
436
|
+
option :regexp,
|
437
|
+
description: 'Regular expresion to match serveral metrics ids. Cannot be used with the \"metric\" option',
|
438
|
+
short: '-r <regular expression for the metric id>',
|
439
|
+
long: '--regexp <regular expression for the metric id>',
|
440
|
+
default: nil
|
441
|
+
|
442
|
+
option :help,
|
443
|
+
description: 'Show help',
|
444
|
+
short: '-h',
|
445
|
+
long: '--help'
|
446
|
+
|
447
|
+
def run
|
448
|
+
if config[:help]
|
449
|
+
puts opt_parser
|
450
|
+
exit
|
451
|
+
end
|
452
|
+
socket = SocketWrapper.new(config[:socket])
|
453
|
+
component = CheckCollectdComponent.new(socket,
|
454
|
+
config[:critical],
|
455
|
+
config[:warning],
|
456
|
+
config[:metric],
|
457
|
+
config[:regexp],
|
458
|
+
config[:data_name],
|
459
|
+
config[:timeout],
|
460
|
+
self)
|
461
|
+
begin
|
462
|
+
component.run
|
463
|
+
rescue => e
|
464
|
+
critical "Unexpected exception when trying to read #{config[:socket]} (#{e})"
|
465
|
+
end
|
466
|
+
end
|
467
|
+
end
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "sensu/plugin/collectd"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'sensu-plugin-collectd/version'
|
@@ -0,0 +1,29 @@
|
|
1
|
+
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "sensu-plugin-collectd/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "sensu-plugin-collectd"
|
8
|
+
spec.version = Sensu::Plugin::Collectd::VER_STRING
|
9
|
+
spec.authors = ["Pilar Gomez"]
|
10
|
+
spec.email = ["mp.gomezmoya@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = 'Sensu plugin to pull metrics from the collectd socket'
|
13
|
+
spec.homepage = "https://github.com/mpgomez/sensu-plugin-collectd"
|
14
|
+
|
15
|
+
# Specify which files should be added to the gem when it is released.
|
16
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
17
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
18
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
19
|
+
end
|
20
|
+
spec.bindir = "exe"
|
21
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
|
+
spec.require_paths = ["lib"]
|
23
|
+
|
24
|
+
spec.add_runtime_dependency "sensu-plugin", "~> 1.2"
|
25
|
+
|
26
|
+
spec.add_development_dependency "bundler", "~> 2.0"
|
27
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
28
|
+
spec.add_development_dependency "rspec", "~> 3.4"
|
29
|
+
end
|
metadata
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sensu-plugin-collectd
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Pilar Gomez
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-03-25 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: sensu-plugin
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.2'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.2'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '10.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '10.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.4'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.4'
|
69
|
+
description:
|
70
|
+
email:
|
71
|
+
- mp.gomezmoya@gmail.com
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- ".gitignore"
|
77
|
+
- Gemfile
|
78
|
+
- README.md
|
79
|
+
- Rakefile
|
80
|
+
- bin/check-collectd-socket-metric.rb
|
81
|
+
- bin/console
|
82
|
+
- bin/setup
|
83
|
+
- lib/sensu-plugin-collectd.rb
|
84
|
+
- lib/sensu-plugin-collectd/version.rb
|
85
|
+
- sensu-plugin-collectd.gemspec
|
86
|
+
homepage: https://github.com/mpgomez/sensu-plugin-collectd
|
87
|
+
licenses: []
|
88
|
+
metadata: {}
|
89
|
+
post_install_message:
|
90
|
+
rdoc_options: []
|
91
|
+
require_paths:
|
92
|
+
- lib
|
93
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '0'
|
98
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0'
|
103
|
+
requirements: []
|
104
|
+
rubyforge_project:
|
105
|
+
rubygems_version: 2.6.14.1
|
106
|
+
signing_key:
|
107
|
+
specification_version: 4
|
108
|
+
summary: Sensu plugin to pull metrics from the collectd socket
|
109
|
+
test_files: []
|