sensu-plugins-graphite-boutetnico 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +1 -0
- data/LICENSE +22 -0
- data/README.md +126 -0
- data/bin/check-graphite-data.rb +115 -0
- data/bin/check-graphite-hosts.rb +97 -0
- data/bin/check-graphite-replication.rb +234 -0
- data/bin/check-graphite-stats.rb +158 -0
- data/bin/check-graphite.rb +542 -0
- data/bin/handler-graphite-event.rb +86 -0
- data/bin/handler-graphite-notify.rb +41 -0
- data/bin/handler-graphite-occurrences.rb +39 -0
- data/bin/handler-graphite-status.rb +42 -0
- data/bin/mutator-graphite.rb +44 -0
- data/lib/sensu-plugins-graphite.rb +1 -0
- data/lib/sensu-plugins-graphite/graphite_proxy/options.rb +119 -0
- data/lib/sensu-plugins-graphite/graphite_proxy/proxy.rb +106 -0
- data/lib/sensu-plugins-graphite/version.rb +9 -0
- metadata +275 -0
@@ -0,0 +1,86 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# Sensu - Graphite Event Handler
|
4
|
+
#
|
5
|
+
# This handler takes events and POSTs them to a Graphite events URI.
|
6
|
+
#
|
7
|
+
# For configuration see: graphite_event.json
|
8
|
+
#
|
9
|
+
# See here for more details:
|
10
|
+
#
|
11
|
+
# * https://code.launchpad.net/~lucio.torre/graphite/add-events/+merge/69142
|
12
|
+
#
|
13
|
+
# Author: Rob Wilson <roobert@gmail.com>
|
14
|
+
#
|
15
|
+
# Released under the same terms as Sensu (the MIT license); see LICENSE
|
16
|
+
# for details
|
17
|
+
|
18
|
+
require 'sensu-handler'
|
19
|
+
require 'net/http'
|
20
|
+
require 'net/https'
|
21
|
+
require 'uri'
|
22
|
+
require 'json'
|
23
|
+
|
24
|
+
class GraphiteEvent < Sensu::Handler
|
25
|
+
option :tags_as_array,
|
26
|
+
description: 'send tags as array - for post graphite 1.0 compatibility.',
|
27
|
+
short: '-t',
|
28
|
+
long: '--tags-as-array',
|
29
|
+
boolean: true,
|
30
|
+
required: false,
|
31
|
+
default: false
|
32
|
+
|
33
|
+
def post_event(uri, body)
|
34
|
+
uri = URI.parse(uri)
|
35
|
+
req = Net::HTTP::Post.new(uri.path)
|
36
|
+
sock = Net::HTTP.new(uri.host, uri.port)
|
37
|
+
sock.use_ssl = uri.scheme == 'https'
|
38
|
+
req.body = body
|
39
|
+
|
40
|
+
req.basic_auth(uri.user, uri.password) if uri.user
|
41
|
+
|
42
|
+
sock.start { |http| http.request(req) }
|
43
|
+
end
|
44
|
+
|
45
|
+
def event_status
|
46
|
+
case @event['check']['status']
|
47
|
+
when 0
|
48
|
+
'ok'
|
49
|
+
when 1
|
50
|
+
'warning'
|
51
|
+
when 2
|
52
|
+
'critical'
|
53
|
+
else
|
54
|
+
'unknown'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def handle
|
59
|
+
tags = [
|
60
|
+
'sensu',
|
61
|
+
'event',
|
62
|
+
event_status,
|
63
|
+
@event['client']['name'],
|
64
|
+
@event['check']['name']
|
65
|
+
]
|
66
|
+
|
67
|
+
tags += settings['graphite_event']['tags'] if settings['graphite_event']['tags']
|
68
|
+
|
69
|
+
body = {
|
70
|
+
'what' => 'sensu_event',
|
71
|
+
'tags' => config[:tags_as_array] ? [tags.join(',')] : tags.join(','),
|
72
|
+
'data' => event_status,
|
73
|
+
'when' => Time.now.to_i
|
74
|
+
}
|
75
|
+
|
76
|
+
uri = settings['graphite_event']['server_uri']
|
77
|
+
|
78
|
+
begin
|
79
|
+
post_event(uri, body.to_json)
|
80
|
+
rescue => e
|
81
|
+
bail "failed to send event to #{uri}: #{e}"
|
82
|
+
end
|
83
|
+
|
84
|
+
puts "sent event to Graphite: #{body.to_json}"
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# Released under the same terms as Sensu (the MIT license); see LICENSE
|
4
|
+
# for details
|
5
|
+
#
|
6
|
+
# This will send a 1 to a Graphite metric when an event is created and 0 when it's resolved
|
7
|
+
# See http://imansson.wordpress.com/2012/11/26/why-sensu-is-a-monitoring-router-some-cool-handlers/
|
8
|
+
#
|
9
|
+
# Config by default is graphite_notify, but can be called with a specific json config
|
10
|
+
# using the -j option. This allows multiple graphite handlers to be configured.
|
11
|
+
|
12
|
+
require 'sensu-handler'
|
13
|
+
require 'simple-graphite'
|
14
|
+
|
15
|
+
class Resolve < Sensu::Handler
|
16
|
+
option :json_config,
|
17
|
+
description: 'Config Name',
|
18
|
+
short: '-j JsonConfig',
|
19
|
+
long: '--json_config JsonConfig',
|
20
|
+
required: false,
|
21
|
+
default: 'graphite_notify'
|
22
|
+
|
23
|
+
def handle
|
24
|
+
json_config = config[:json_config]
|
25
|
+
port = settings[json_config]['port'] ? settings[json_config]['port'].to_s : '2003'
|
26
|
+
graphite = Graphite.new(host: settings[json_config]['host'], port: port)
|
27
|
+
return unless graphite
|
28
|
+
|
29
|
+
prop = @event['action'] == 'create' ? 1 : 0
|
30
|
+
message = "#{settings[json_config]['prefix']}.#{@event['client']['name'].tr('.', '_')}.#{@event['check']['name']}"
|
31
|
+
message += " #{prop} #{graphite.time_now + rand(100)}"
|
32
|
+
begin
|
33
|
+
graphite.push_to_graphite do |graphite_socket|
|
34
|
+
graphite_socket.puts message
|
35
|
+
end
|
36
|
+
rescue Errno::ETIMEDOUT
|
37
|
+
error_msg = "Can't connect to #{settings[json_config]['host']}:#{port} and send message #{message}'"
|
38
|
+
raise error_msg
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# Copyright 2013 vimov, LLC. <a.gameel@vimov.com>
|
4
|
+
#
|
5
|
+
# Released under the same terms as Sensu (the MIT license); see LICENSE
|
6
|
+
# for details.
|
7
|
+
|
8
|
+
require 'sensu-handler'
|
9
|
+
|
10
|
+
class GraphiteOccurrences < Sensu::Handler
|
11
|
+
# override filters from Sensu::Handler. not appropriate for metric handlers
|
12
|
+
def filter; end
|
13
|
+
|
14
|
+
def handle
|
15
|
+
hostname = @event['client']['name']
|
16
|
+
# #YELLOW
|
17
|
+
check_name = @event['check']['name'].gsub(%r{[ \.]}, '_')
|
18
|
+
value = @event['action'] == 'create' ? @event['occurrences'] : 0
|
19
|
+
now = Time.now.to_i
|
20
|
+
|
21
|
+
# Get Graphite-like format for Sensu events here
|
22
|
+
check_occurrences = "sensu.#{hostname}.#{check_name} #{value} #{now}"
|
23
|
+
|
24
|
+
graphite_server = settings['graphite']['server']
|
25
|
+
graphite_port = settings['graphite']['port']
|
26
|
+
|
27
|
+
begin
|
28
|
+
timeout(3) do
|
29
|
+
sock = TCPSocket.new(graphite_server, graphite_port)
|
30
|
+
sock.puts check_occurrences
|
31
|
+
sock.close
|
32
|
+
end
|
33
|
+
rescue Timeout::Error
|
34
|
+
puts 'Graphite -- timed out while sending check occurrence'
|
35
|
+
rescue => e
|
36
|
+
puts "Graphite -- failed to send check occurrence: #{e}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# Released under the same terms as Sensu (the MIT license); see LICENSE
|
4
|
+
# for details
|
5
|
+
#
|
6
|
+
# This will send the check status (0,1,2,3) to a graphite metric when a check event state changes
|
7
|
+
# Based on handler-graphite-notify.rb.
|
8
|
+
# Config by default is graphite_status but can be called with a specific json config
|
9
|
+
# using the -j option. This allows multiple graphite handlers to be configured.
|
10
|
+
|
11
|
+
require 'sensu-handler'
|
12
|
+
require 'simple-graphite'
|
13
|
+
|
14
|
+
class Resolve < Sensu::Handler
|
15
|
+
option :json_config,
|
16
|
+
description: 'Config Name',
|
17
|
+
short: '-j JsonConfig',
|
18
|
+
long: '--json_config JsonConfig',
|
19
|
+
required: false,
|
20
|
+
default: 'graphite_status'
|
21
|
+
# override filters from Sensu::Handler. not appropriate for metric handlers
|
22
|
+
def filter; end
|
23
|
+
|
24
|
+
def handle
|
25
|
+
json_config = config[:json_config]
|
26
|
+
port = settings[json_config]['port'] ? settings[json_config]['port'].to_s : '2003'
|
27
|
+
graphite = Graphite.new(host: settings[json_config]['host'], port: port)
|
28
|
+
return unless graphite
|
29
|
+
|
30
|
+
prop = @event['check']['status']
|
31
|
+
message = "#{settings[json_config]['prefix']}.#{@event['client']['name'].tr('.', '_')}.#{@event['check']['name']}"
|
32
|
+
message += " #{prop} #{graphite.time_now + rand(100)}"
|
33
|
+
begin
|
34
|
+
graphite.push_to_graphite do |graphite_socket|
|
35
|
+
graphite_socket.puts message
|
36
|
+
end
|
37
|
+
rescue Errno::ETIMEDOUT
|
38
|
+
error_msg = "Can't connect to #{settings[json_config]['host']}:#{port} and send message #{message}'"
|
39
|
+
raise error_msg
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# Graphite
|
4
|
+
# ===
|
5
|
+
#
|
6
|
+
# DESCRIPTION:
|
7
|
+
# This mutator is an extension of the OnlyCheckOutput mutator, but
|
8
|
+
# modified for Graphite metrics. This mutator only sends event output
|
9
|
+
# (so you don't need to use OnlyCheckOutput) and it also modifies
|
10
|
+
# the format of the hostname in the output if present.
|
11
|
+
#
|
12
|
+
# Note however that using this mutator as a mutator command can be very
|
13
|
+
# expensive, as Sensu has to spawn a new Ruby process to launch this script
|
14
|
+
# for each result of a metrics check. Consider instead producing the correct
|
15
|
+
# metric names from your plugin and sending them directly to Graphite via the
|
16
|
+
# socket handler.
|
17
|
+
# See https://groups.google.com/d/msg/sensu-users/1hkRSvL48ck/8Dhl98lR24kJ
|
18
|
+
# for more information.
|
19
|
+
#
|
20
|
+
# OUTPUT:
|
21
|
+
# Sensu event output with all dots changed to underlines in host name
|
22
|
+
# If -r or --reverse parameter given script put hostname in reverse order
|
23
|
+
# for better Graphite tree view
|
24
|
+
#
|
25
|
+
# PLATFORM:
|
26
|
+
# all
|
27
|
+
#
|
28
|
+
# DEPENDENCIES:
|
29
|
+
# none
|
30
|
+
#
|
31
|
+
# Copyright 2013 Peter Kepes <https://github.com/kepes>
|
32
|
+
#
|
33
|
+
# Released under the same terms as Sensu (the MIT license); see LICENSE
|
34
|
+
# for details.
|
35
|
+
require 'json'
|
36
|
+
|
37
|
+
# parse event
|
38
|
+
event = JSON.parse(STDIN.read, symbolize_names: true)
|
39
|
+
|
40
|
+
if ARGV[0] == '-r' || ARGV[0] == '--reverse'
|
41
|
+
puts event[:check][:output].gsub(event[:client][:name], event[:client][:name].split('.').reverse.join('.'))
|
42
|
+
else
|
43
|
+
puts event[:check][:output].gsub(event[:client][:name], event[:client][:name].tr('.', '_'))
|
44
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'sensu-plugins-graphite/version'
|
@@ -0,0 +1,119 @@
|
|
1
|
+
module SensuPluginsGraphite
|
2
|
+
module GraphiteProxy
|
3
|
+
module Options
|
4
|
+
def self.included(base)
|
5
|
+
add_default_options(base)
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.add_default_options(base)
|
9
|
+
default_options.each do |name, vals|
|
10
|
+
base.send(:option, name, vals)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.default_options
|
15
|
+
{
|
16
|
+
target: {
|
17
|
+
description: 'Graphite data target',
|
18
|
+
short: '-t TARGET',
|
19
|
+
long: '--target TARGET',
|
20
|
+
required: true
|
21
|
+
},
|
22
|
+
|
23
|
+
server: {
|
24
|
+
description: 'Server host and port',
|
25
|
+
short: '-s SERVER:PORT',
|
26
|
+
long: '--server SERVER:PORT',
|
27
|
+
required: true
|
28
|
+
},
|
29
|
+
|
30
|
+
username: {
|
31
|
+
description: 'Username for basic HTTP authentication',
|
32
|
+
short: '-u USERNAME',
|
33
|
+
long: '--user USERNAME',
|
34
|
+
required: false
|
35
|
+
},
|
36
|
+
|
37
|
+
password: {
|
38
|
+
description: 'User password for basic HTTP authentication',
|
39
|
+
short: '-p PASSWORD',
|
40
|
+
long: '--pass PASSWORD',
|
41
|
+
required: false
|
42
|
+
},
|
43
|
+
|
44
|
+
passfile: {
|
45
|
+
description: 'Password file path for basic HTTP authentication',
|
46
|
+
short: '-P PASSWORDFILE',
|
47
|
+
long: '--passfile PASSWORDFILE',
|
48
|
+
required: false
|
49
|
+
},
|
50
|
+
|
51
|
+
no_ssl_verify: {
|
52
|
+
description: 'Do not verify SSL certs',
|
53
|
+
short: '-v',
|
54
|
+
long: '--nosslverify'
|
55
|
+
},
|
56
|
+
|
57
|
+
help: {
|
58
|
+
description: 'Show this message',
|
59
|
+
short: '-h',
|
60
|
+
long: '--help'
|
61
|
+
},
|
62
|
+
|
63
|
+
auth: {
|
64
|
+
description: 'Add an auth token to the HTTP request, in the form of "Name: Value",
|
65
|
+
e.g. --auth yourapitokenvaluegoeshere',
|
66
|
+
short: '-A TOKEN',
|
67
|
+
long: '--auth TOKEN'
|
68
|
+
},
|
69
|
+
|
70
|
+
name: {
|
71
|
+
description: 'Name used in responses',
|
72
|
+
short: '-n NAME',
|
73
|
+
long: '--name NAME',
|
74
|
+
default: 'graphite check'
|
75
|
+
},
|
76
|
+
|
77
|
+
hostname_sub: {
|
78
|
+
description: 'Character used to replace periods (.) in hostname (default: _)',
|
79
|
+
long: '--host-sub CHARACTER'
|
80
|
+
},
|
81
|
+
|
82
|
+
from: {
|
83
|
+
description: 'Get samples starting from FROM (default: -10mins)',
|
84
|
+
short: '-f FROM',
|
85
|
+
long: '--from FROM',
|
86
|
+
default: '-10mins'
|
87
|
+
},
|
88
|
+
|
89
|
+
until: {
|
90
|
+
description: 'Get samples ending at UNTIL (default: now)',
|
91
|
+
short: '-l UNTIL',
|
92
|
+
long: '--until UNTIL',
|
93
|
+
default: 'now'
|
94
|
+
},
|
95
|
+
|
96
|
+
warning: {
|
97
|
+
description: 'Generate warning if number of hosts is below received value',
|
98
|
+
short: '-w VALUE',
|
99
|
+
long: '--warn VALUE',
|
100
|
+
proc: proc(&:to_f)
|
101
|
+
},
|
102
|
+
|
103
|
+
critical: {
|
104
|
+
description: 'Generate critical if number of hosts is below received value',
|
105
|
+
short: '-c VALUE',
|
106
|
+
long: '--critical VALUE',
|
107
|
+
proc: proc(&:to_f)
|
108
|
+
},
|
109
|
+
|
110
|
+
below: {
|
111
|
+
description: 'Alert if number of hosts is below specified thresholds',
|
112
|
+
short: '-b',
|
113
|
+
long: '--below'
|
114
|
+
}
|
115
|
+
}
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
|
3
|
+
module SensuPluginsGraphite
|
4
|
+
module GraphiteProxy
|
5
|
+
class ProxyError < StandardError
|
6
|
+
def initialize(msg)
|
7
|
+
super msg
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class Proxy
|
12
|
+
attr_accessor :config
|
13
|
+
|
14
|
+
def initialize(config)
|
15
|
+
self.config = config
|
16
|
+
end
|
17
|
+
|
18
|
+
def formatted_target
|
19
|
+
if config[:target].include?('$')
|
20
|
+
require 'socket'
|
21
|
+
formatted = Socket.gethostbyname(Socket.gethostname).first.gsub('.', config[:hostname_sub] || '_')
|
22
|
+
config[:target].gsub('$', formatted)
|
23
|
+
else
|
24
|
+
CGI.escape config[:target]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def request_auth_options(given_opts)
|
29
|
+
url_opts = {}
|
30
|
+
|
31
|
+
url_opts[:ssl_verify_mode] = OpenSSL::SSL::VERIFY_NONE if given_opts[:no_ssl_verify]
|
32
|
+
|
33
|
+
if given_opts[:username]
|
34
|
+
pass = derive_password(given_opts)
|
35
|
+
url_opts[:http_basic_authentication] = [given_opts[:username], pass.chomp]
|
36
|
+
end # we don't have both username and password trying without
|
37
|
+
|
38
|
+
url_opts['Authorization'] = "Bearer #{given_opts[:auth]}" if given_opts[:auth]
|
39
|
+
|
40
|
+
url_opts
|
41
|
+
end
|
42
|
+
|
43
|
+
def derive_password(given_opts)
|
44
|
+
if given_opts[:passfile]
|
45
|
+
File.open(given_opts[:passfile]).readline
|
46
|
+
elsif given_opts[:password]
|
47
|
+
given_opts[:password]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def format_output(data)
|
52
|
+
output = {}
|
53
|
+
|
54
|
+
data.each do |raw|
|
55
|
+
unless raw['datapoints'].empty?
|
56
|
+
line = output_line(raw)
|
57
|
+
output[line['target']] = line
|
58
|
+
end
|
59
|
+
end
|
60
|
+
output
|
61
|
+
end
|
62
|
+
|
63
|
+
def output_line(raw)
|
64
|
+
raw['datapoints'].delete_if { |v| v.first.nil? }
|
65
|
+
target = raw['target']
|
66
|
+
data = raw['datapoints'].map(&:first)
|
67
|
+
start = raw['datapoints'].first.last
|
68
|
+
dend = raw['datapoints'].last.last
|
69
|
+
step = ((dend - start) / raw['datapoints'].size.to_f).ceil
|
70
|
+
|
71
|
+
{ 'target' => target, 'data' => data, 'start' => start, 'end' => dend, 'step' => step }
|
72
|
+
end
|
73
|
+
|
74
|
+
# grab data from graphite
|
75
|
+
def retrieve_data!
|
76
|
+
unless @raw_data
|
77
|
+
begin
|
78
|
+
unless config[:server].start_with?('https://', 'http://')
|
79
|
+
config[:server].prepend('http://')
|
80
|
+
end
|
81
|
+
|
82
|
+
url = "#{config[:server]}/render?format=json&target=#{formatted_target}&from=#{config[:from]}"
|
83
|
+
|
84
|
+
handle = open(url, request_auth_options(config))
|
85
|
+
|
86
|
+
@raw_data = handle.gets
|
87
|
+
json_data = JSON.parse(@raw_data)
|
88
|
+
format_output(json_data)
|
89
|
+
rescue OpenURI::HTTPError
|
90
|
+
raise ProxyError, 'Failed to connect to Graphite server'
|
91
|
+
rescue NoMethodError, JSON::ParserError
|
92
|
+
raise ProxyError, 'No data for time period and/or target'
|
93
|
+
rescue Errno::ECONNREFUSED
|
94
|
+
raise ProxyError, 'Connection refused when connecting to Graphite server'
|
95
|
+
rescue Errno::ECONNRESET
|
96
|
+
raise ProxyError, 'Connection reset by peer when connecting to Graphite server'
|
97
|
+
rescue EOFError
|
98
|
+
raise ProxyError, 'End of file error when reading from Graphite server'
|
99
|
+
rescue => e
|
100
|
+
raise ProxyError, "An unknown error occurred: #{e.inspect}"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|