sensu-plugins-graphite-boutetnico 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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