sensu-plugins-influxdb-q 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 204941a95c0a3db4f5cb7813379e21286d713ea0
4
+ data.tar.gz: 3d6258e8b813733bd458df70c6013f1442315553
5
+ SHA512:
6
+ metadata.gz: 931e1310fbedad9d6eb5b2e97da449a97161f2b334c312281405e30442cc7c9cc9116ffd13ae985b001a65fefd0d4b487c2f285d8c12be5c706450ca50a7affe
7
+ data.tar.gz: 810408932d59d1ee8dd0ce0bd77b378cd128df0164b9df836bdaae821e389bf289f4d280c4b2d9ac0d5a0ed632170a40cd9b91769c7252807e33df3315cff630
data/CHANGELOG.md ADDED
@@ -0,0 +1,10 @@
1
+ # Change Log
2
+ This project adheres to [Semantic Versioning](http://semver.org/).
3
+
4
+ This CHANGELOG follows the format listed at [Keep A Changelog](http://keepachangelog.com/)
5
+
6
+ ## Unreleased
7
+
8
+ ## [0.0.1] - 2016-01-07
9
+ ### Added
10
+ - Initial release
data/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ Copyright (c) 2015 Matteo Cerutti
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+
data/README.md ADDED
@@ -0,0 +1,54 @@
1
+ # Sensu plugin for running InfluxDB queries
2
+
3
+ A sensu plugin that extends Sensu with the ability to run queries against InfluxDB.
4
+
5
+ This is generally useful when you have to evaluate an issue using some analytics functions (e.g. moving average, derivative etc.).
6
+
7
+ The plugin discovers all active sensu clients via the Sensu REST API and then runs for each client the InfluxDB query filtering by host.
8
+ The plugin then generates multiple OK/WARN/CRIT/UNKNOWN events via the sensu client socket (https://sensuapp.org/docs/latest/clients#client-socket-input),
9
+ making sure to override the client source (in the check result) so that the check shows up as if it was triggered by the original sensu client.
10
+
11
+ The plugin is inspired by https://github.com/sensu-plugins/sensu-plugins-influxdb.
12
+
13
+ ## Usage
14
+
15
+ The plugin accepts the following command line options:
16
+
17
+ ```
18
+ Usage: check-influxdb-q.rb (options)
19
+ --check-name <CHECK_NAME> Check name (default: %{name}-%{tags.instance}-%{tags.type})
20
+ -c, --crit <EXPR> Critical expression (e.g. value >= 10)
21
+ --database <DATABASE> InfluxDB database (default: collectd)
22
+ --debug Enable debug mode
23
+ --dryrun Do not send events to sensu client socket
24
+ --host <HOST> InfluxDB host (default: localhost)
25
+ --host-field <FIELD> InfluxDB measurement host field (default: host)
26
+ -j, --json-path <PATH> JSON path for value matching (docs at http://goessner.net/articles/JsonPath) (required)
27
+ -m, --msg <MESSAGE> Message to use for OK/WARNING/CRITICAL, supports variable interpolation (e.g. %{tags.instance}) (required)
28
+ --port <PORT> InfluxDB port (default: 8086)
29
+ -q, --query <QUERY> Query to execute [e.g. SELECT DERIVATIVE(LAST(value), 1m) AS value FROM interface_rx WHERE type = 'if_errors' AND time > now() - 5m group by time(1m), instance, type fill(none)] (required)
30
+ --use-ssl InfluxDB SSL (default: false)
31
+ -w, --warn <EXPR> Warning expression (e.g. value >= 5)
32
+ ```
33
+
34
+ ## Example
35
+
36
+ A typical example could be the monitoring of interface errors when using the 'interface' collectd plugin.
37
+
38
+
39
+ ```
40
+ check-influxdb-q.rb -q "SELECT DERIVATIVE(LAST(value), 1m) AS value FROM interface_rx WHERE type = 'if_errors' AND time > now() - 5m group by time(1m), instance, type fill(none)" -j '$.values[-1].value' -w 'value > 0' --msg "Number of errors on interface %{instance} - %{name}"
41
+ ```
42
+
43
+ An handy feature is the ability to interpolate the query result hash attributes into the --check-name and --msg command line flags.
44
+
45
+ So, for query above the result might look like the following (run with --debug to inspect the result):
46
+
47
+ ```
48
+ {"name"=>"interface_rx", "tags"=>{"instance"=>"ens255f0", "type"=>"if_errors"}, "values"=>[{"time"=>"2016-01-07T21:43:00Z", "value"=>0}, {"time"=>"2016-01-07T21:44:00Z", "value"=>0}, {"time"=>"2016-01-07T21:45:00Z", "value"=>0}, {"time"=>"2016-01-07T21:46:00Z", "value"=>0}, {"time"=>"2016-01-07T21:47:00Z", "value"=>0}]}
49
+ ```
50
+
51
+ In this case, you can interpolate the following variables: %{name}, %{tags.instance}, %{tags.type}, %{values.0.value} ..
52
+
53
+ ## Author
54
+ Matteo Cerutti - <matteo.cerutti@hotmail.co.uk>
@@ -0,0 +1,234 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # check-influxdb-q.rb
4
+ #
5
+ # Inspired by: https://github.com/sensu-plugins/sensu-plugins-influxdb/blob/master/bin/check-influxdb.rb
6
+ #
7
+ # Author: Matteo Cerutti <matteo.cerutti@hotmail.co.uk>
8
+ #
9
+
10
+ require 'net/http'
11
+ require 'json'
12
+ require 'sensu-plugin/utils'
13
+ require 'sensu-plugin/check/cli'
14
+ require 'influxdb'
15
+ require 'jsonpath'
16
+ require 'dentaku'
17
+
18
+ class Hash
19
+ def symbolize_recursive
20
+ {}.tap do |h|
21
+ self.each do |key, value|
22
+ h[key.to_sym] = case value
23
+ when Hash
24
+ value.symbolize_recursive
25
+ when Array
26
+ value.map { |v| v.symbolize_recursive }
27
+ else
28
+ value
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ class CheckInfluxDbQ < Sensu::Plugin::Check::CLI
36
+ include Sensu::Plugin::Utils
37
+
38
+ option :query,
39
+ :description => "Query to execute [e.g. SELECT DERIVATIVE(LAST(value), 1m) AS value FROM interface_rx WHERE type = 'if_errors' AND time > now() - 5m group by time(1m), instance, type fill(none)]",
40
+ :short => "-q <QUERY>",
41
+ :long => "--query <QUERY>",
42
+ :required => true
43
+
44
+ option :json_path,
45
+ :description => "JSON path for value matching (docs at http://goessner.net/articles/JsonPath)",
46
+ :short => "-j <PATH>",
47
+ :long => "--json-path <PATH>",
48
+ :required => true
49
+
50
+ option :check_name,
51
+ :description => "Check name (default: %{name}-%{tags.instance}-%{tags.type})",
52
+ :long => "--check-name <CHECK_NAME>",
53
+ :default => "%{name}-%{tags.instance}-%{tags.type}"
54
+
55
+ option :msg,
56
+ :description => "Message to use for OK/WARNING/CRITICAL, supports variable interpolation (e.g. %{tags.instance})",
57
+ :short => "-m <MESSAGE>",
58
+ :long => "--msg <MESSAGE>",
59
+ :required => true
60
+
61
+ option :database,
62
+ :description => "InfluxDB database (default: collectd)",
63
+ :long => "--database <DATABASE>",
64
+ :default => "collectd"
65
+
66
+ option :host,
67
+ :description => "InfluxDB host (default: localhost)",
68
+ :long => "--host <HOST>",
69
+ :default => "localhost"
70
+
71
+ option :port,
72
+ :description => "InfluxDB port (default: 8086)",
73
+ :long => "--port <PORT>",
74
+ :proc => proc { |i| i.to_i },
75
+ :default => 8086
76
+
77
+ option :use_ssl,
78
+ :description => "InfluxDB SSL (default: false)",
79
+ :long => "--use-ssl",
80
+ :default => false,
81
+ :boolean => true
82
+
83
+ option :host_field,
84
+ :description => "InfluxDB measurement host field (default: host)",
85
+ :long => "--host-field <FIELD>",
86
+ :default => "host"
87
+
88
+ option :warn,
89
+ :description => "Warning expression (e.g. value >= 5)",
90
+ :short => "-w <EXPR>",
91
+ :long => "--warn <EXPR>",
92
+ :default => nil
93
+
94
+ option :crit,
95
+ :description => "Critical expression (e.g. value >= 10)",
96
+ :short => "-c <EXPR>",
97
+ :long => "--crit <EXPR>",
98
+ :default => nil
99
+
100
+ option :debug,
101
+ :description => "Enable debug mode",
102
+ :long => "--debug",
103
+ :boolean => true,
104
+ :default => false
105
+
106
+ option :dryrun,
107
+ :description => "Do not send events to sensu client socket",
108
+ :long => "--dryrun",
109
+ :boolean => true,
110
+ :default => false
111
+
112
+ def initialize()
113
+ super
114
+
115
+ cfg = {
116
+ :database => config[:database],
117
+ :host => config[:host],
118
+ :port => config[:port],
119
+ :use_ssl => config[:use_ssl]
120
+ }
121
+
122
+ @calculator = Dentaku::Calculator.new
123
+ @json_path = JsonPath.new(config[:json_path])
124
+ @influxdb = InfluxDB::Client.new(cfg)
125
+
126
+ # get list of hosts
127
+ @clients = get_clients()
128
+ end
129
+
130
+ def send_client_socket(data)
131
+ if config[:dryrun]
132
+ puts data.inspect
133
+ else
134
+ sock = UDPSocket.new
135
+ sock.send(data + "\n", 0, "127.0.0.1", 3030)
136
+ end
137
+ end
138
+
139
+ def send_ok(check_name, source, msg)
140
+ event = {"name" => check_name, "source" => source, "status" => 0, "output" => "OK: #{msg}", "handler" => config[:handler]}
141
+ send_client_socket(event.to_json)
142
+ end
143
+
144
+ def send_warning(check_name, source, msg)
145
+ event = {"name" => check_name, "source" => source, "status" => 1, "output" => "WARNING: #{msg}", "handler" => config[:handler]}
146
+ send_client_socket(event.to_json)
147
+ end
148
+
149
+ def send_critical(check_name, source, msg)
150
+ event = {"name" => check_name, "source" => source, "status" => 2, "output" => "CRITICAL: #{msg}", "handler" => config[:handler]}
151
+ send_client_socket(event.to_json)
152
+ end
153
+
154
+ def send_unknown(check_name, source, msg)
155
+ event = {"name" => check_name, "source" => source, "status" => 3, "output" => "UNKNOWN: #{msg}", "handler" => config[:handler]}
156
+ send_client_socket(event.to_json)
157
+ end
158
+
159
+ def api_request(method, path, &blk)
160
+ if not settings.has_key?('api')
161
+ raise "api.json settings not found."
162
+ end
163
+ http = Net::HTTP.new(settings['api']['host'], settings['api']['port'])
164
+ req = net_http_req_class(method).new(path)
165
+ if settings['api']['user'] && settings['api']['password']
166
+ req.basic_auth(settings['api']['user'], settings['api']['password'])
167
+ end
168
+ yield(req) if block_given?
169
+ http.request(req)
170
+ end
171
+
172
+ def interpolate(string, hash)
173
+ string.gsub(/%\{([^\}]*)\}/).each { |match| match[/^%{(.*)}$/, 1].split('.').inject(hash) { |h, k| h[(k.to_s == k.to_i.to_s) ? k.to_i : k.to_sym] } }
174
+ end
175
+
176
+ def get_clients()
177
+ clients = []
178
+
179
+ if req = api_request(:GET, "/clients")
180
+ if req.code == '200'
181
+ JSON.parse(req.body).each do |client|
182
+ clients << client['name']
183
+ end
184
+ end
185
+ end
186
+
187
+ clients
188
+ end
189
+
190
+ def run()
191
+ problems = 0
192
+
193
+ @clients.each do |client|
194
+ query = config[:query].gsub(" WHERE ", " WHERE #{config[:host_field]} = '#{client}' AND ")
195
+ puts "* Query: #{query.inspect}" if config[:debug]
196
+ begin
197
+ records = @influxdb.query(query)
198
+ records.each do |record|
199
+ puts " - Result: #{record.inspect}" if config[:debug]
200
+
201
+ value = @json_path.on(record).first
202
+
203
+ record_s = record.symbolize_recursive
204
+ check_name = "influxdb-q-#{interpolate(config[:check_name], record_s)}"
205
+ msg = interpolate(config[:msg], record_s)
206
+
207
+ if value != nil
208
+ if config[:crit] and @calculator.evaluate(config[:crit], value: value)
209
+ send_critical(check_name, client, "#{msg} - Value: #{value} (#{config[:crit]})")
210
+ elsif config[:warn] and @calculator.evaluate(config[:warn], value: value)
211
+ send_warning(check_name, client, "#{msg} - Value: #{value} (#{config[:warn]})")
212
+ else
213
+ send_ok(check_name, client, "#{msg} - Value: #{value}")
214
+ end
215
+ else
216
+ send_unknown(check_name, client, "#{msg} - Value: N/A")
217
+ end
218
+ end
219
+ puts if config[:debug]
220
+ rescue
221
+ STDERR.puts($!)
222
+ problems += 1
223
+ end
224
+ end
225
+
226
+ if problems > 0
227
+ message("Failed to run query for #{problems} clients")
228
+ critical if config[:crit]
229
+ warning
230
+ else
231
+ ok("Query executed successfully for #{@clients.size} clients")
232
+ end
233
+ end
234
+ end
@@ -0,0 +1,9 @@
1
+ module SensuPluginsInfluxDbQ
2
+ module Version
3
+ MAJOR = 0
4
+ MINOR = 0
5
+ PATCH = 1
6
+
7
+ VER_STRING = [MAJOR, MINOR, PATCH].compact.join('.')
8
+ end
9
+ end
@@ -0,0 +1 @@
1
+ require 'sensu-plugins-influxdb-q/version'
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sensu-plugins-influxdb-q
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Matteo Cerutti
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-01-07 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.0
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.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: dentaku
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '='
32
+ - !ruby/object:Gem::Version
33
+ version: 2.0.5
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '='
39
+ - !ruby/object:Gem::Version
40
+ version: 2.0.5
41
+ - !ruby/object:Gem::Dependency
42
+ name: influxdb
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '='
46
+ - !ruby/object:Gem::Version
47
+ version: 0.2.3
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '='
53
+ - !ruby/object:Gem::Version
54
+ version: 0.2.3
55
+ - !ruby/object:Gem::Dependency
56
+ name: jsonpath
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '='
60
+ - !ruby/object:Gem::Version
61
+ version: 0.5.8
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '='
67
+ - !ruby/object:Gem::Version
68
+ version: 0.5.8
69
+ description: This plugin provides facilities for running queries against InfluxDB
70
+ email: "<matteo.cerutti@hotmail.co.uk>"
71
+ executables:
72
+ - check-influxdb-q.rb
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - CHANGELOG.md
77
+ - LICENSE
78
+ - README.md
79
+ - bin/check-influxdb-q.rb
80
+ - lib/sensu-plugins-influxdb-q.rb
81
+ - lib/sensu-plugins-influxdb-q/version.rb
82
+ homepage: https://github.com/m4ce/sensu-plugins-influxdb-q
83
+ licenses:
84
+ - MIT
85
+ metadata:
86
+ maintainer: "@m4ce"
87
+ development_status: active
88
+ production_status: stable
89
+ release_draft: 'false'
90
+ release_prerelease: 'false'
91
+ post_install_message: You can use the embedded Ruby by setting EMBEDDED_RUBY=true
92
+ in /etc/default/sensu
93
+ rdoc_options: []
94
+ require_paths:
95
+ - lib
96
+ required_ruby_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: 1.9.3
101
+ required_rubygems_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ requirements: []
107
+ rubyforge_project:
108
+ rubygems_version: 2.4.5.1
109
+ signing_key:
110
+ specification_version: 4
111
+ summary: Sensu plugins for monitoring using InfluxDB query language
112
+ test_files: []