riemann-monitors 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +2 -0
- data/LICENSE +21 -0
- data/README.adoc +75 -0
- data/Rakefile +125 -0
- data/bin/riemann-apache-status +98 -0
- data/bin/riemann-bench +71 -0
- data/bin/riemann-cloudant +58 -0
- data/bin/riemann-consul +106 -0
- data/bin/riemann-dir-files-count +55 -0
- data/bin/riemann-dir-space +55 -0
- data/bin/riemann-diskstats +95 -0
- data/bin/riemann-fd +66 -0
- data/bin/riemann-freeswitch +119 -0
- data/bin/riemann-haproxy +58 -0
- data/bin/riemann-health +289 -0
- data/bin/riemann-httpstatus +73 -0
- data/bin/riemann-kvminstance +22 -0
- data/bin/riemann-memcached +38 -0
- data/bin/riemann-net +81 -0
- data/bin/riemann-nginx-status +84 -0
- data/bin/riemann-ntp +35 -0
- data/bin/riemann-proc +131 -0
- data/bin/riemann-varnish +54 -0
- data/bin/riemann-zookeeper +41 -0
- data/data/statfields +49 -0
- data/lib/riemann-monitors/main.rb +111 -0
- data/lib/riemann-monitors/version.rb +6 -0
- data/lib/riemann-monitors.rb +8 -0
- data/project.yaml +12 -0
- data/riemann-monitors.gemspec +73 -0
- metadata +210 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ce2a724b0e9f58a3428fb402dc313167eefbd0b5
|
4
|
+
data.tar.gz: ae4f41b34dbf278b6ab92c0dd89c2f6e5cd5ccf6
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3972340ad48aa5142ba96583d98edc771881db39bd48e0f747bfbfff473003975fe60638b0de8acbf5f7ab51508e7d3dba2d7214ad857c90b9156a4a63112f1c
|
7
|
+
data.tar.gz: a862891b8187f2947d4e490eb1c0b69db6c44a108648470a454edb0266e3a6c6bcbe5f37322993ed5bbaa1c288c3618058e6fb7aea368684c09aec840101afd3
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2011 Kyle Kingsbury
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.adoc
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
= riemann-monitors
|
2
|
+
Chris Riddoch <riddochc@gmail.com>
|
3
|
+
:language: ruby
|
4
|
+
:homepage: https://github.com/riddochc/riemann-monitors
|
5
|
+
:revnumber: 0.0.1
|
6
|
+
:revdate: 2016-05-29
|
7
|
+
|
8
|
+
== Description
|
9
|
+
|
10
|
+
Tiny programs to submit events to Riemann.
|
11
|
+
|
12
|
+
This is a polite fork of riemann-tools, using my riemann-ruby-experiments gem
|
13
|
+
instead of riemann-ruby-client. As a result, events can be batched together
|
14
|
+
to be sent in one message to a Riemann server. Additionally, more control
|
15
|
+
over the TCP socket options is given, and UDP isn't supported (yet?), because
|
16
|
+
this gem primarily uses net_ruby_client.
|
17
|
+
|
18
|
+
== Requirements
|
19
|
+
|
20
|
+
* Ruby 2.0 or higher (currently only tested on 2.3)
|
21
|
+
* A riemann server to talk to
|
22
|
+
|
23
|
+
== Installation
|
24
|
+
|
25
|
+
gem install riemann-monitors
|
26
|
+
|
27
|
+
== Use
|
28
|
+
|
29
|
+
riemann-health
|
30
|
+
|
31
|
+
This defaults to connecting to a Riemann server listening on 127.0.0.1, TCP port 5555.
|
32
|
+
|
33
|
+
== Riemann-tools programs
|
34
|
+
|
35
|
+
This repository contains a number of different programs, mostly identical to those
|
36
|
+
in `riemann-tools`.
|
37
|
+
|
38
|
+
riemann-httpstatus:: Monitoring results of periodic HTTP requests.
|
39
|
+
riemann-apache-status:: Apache monitoring.
|
40
|
+
riemann-dir-files-count:: File counts.
|
41
|
+
riemann-freeswitch:: FreeSwitch monitoring.
|
42
|
+
riemann-memcached:: Monitor Memcache.
|
43
|
+
riemann-proc:: Linux process monitoring.
|
44
|
+
riemann-bench:: Load testing for Riemann.
|
45
|
+
riemann-dir-space:: Directory space monitoring.
|
46
|
+
riemann-haproxy:: Monitor HAProxy.
|
47
|
+
riemann-net:: Network interface monitoring.
|
48
|
+
riemann-varnish:: Monitor Varnish.
|
49
|
+
riemann-cloudant:: Cloudant monitoring.
|
50
|
+
riemann-diskstats:: Disk statistics.
|
51
|
+
riemann-health:: General CPU, memory, disk and load monitoring.
|
52
|
+
riemann-nginx-status:: Monitor Nginx.
|
53
|
+
riemann-zookeeper:: Monitor Zookeeper.
|
54
|
+
riemann-consul:: Monitor Consul.
|
55
|
+
riemann-fd:: Linux file descriptor use.
|
56
|
+
riemann-kvminstance:: Monitor KVM instances.
|
57
|
+
riemann-ntp:: Monitor NTP
|
58
|
+
|
59
|
+
The "stand-alone" tools offered in `riemann-tools` aren't included for now.
|
60
|
+
|
61
|
+
== Contributing
|
62
|
+
|
63
|
+
Pull requests, please.
|
64
|
+
|
65
|
+
== Contributors
|
66
|
+
|
67
|
+
git shortlog -n
|
68
|
+
|
69
|
+
== License
|
70
|
+
|
71
|
+
The MIT License
|
72
|
+
|
73
|
+
Copyright (c) 2016 Chris Riddoch
|
74
|
+
Copyright (c) 2011-2016 Kyle Kingsbury
|
75
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
# vim: syntax=ruby
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
require 'find'
|
5
|
+
require 'asciidoctor'
|
6
|
+
require 'erb'
|
7
|
+
|
8
|
+
def installed_gem_version(name)
|
9
|
+
IO.popen(["gem", "list", "-l", name], 'r') do |io|
|
10
|
+
# this regex is using lookahead/behind to match things within \( and \), non-greedily.
|
11
|
+
io.readlines.grep(/^#{name}\s/).first[/(?<=\().*?(?=\))/].split(', ').first
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def filtered_project_files()
|
16
|
+
Dir.chdir __dir__ do
|
17
|
+
Find.find(".").reject {|f| !File.file?(f) || f =~ %r{^\./(.git|tmp)} || f =~ %r{\.(so|gem)$} }.map {|f| f.sub %r{^\./}, '' }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
adoc = Asciidoctor.load_file("README.adoc")
|
22
|
+
summary = adoc.sections.find {|s| s.name == "Description" }.blocks[0].content.gsub(/\n/, ' ')
|
23
|
+
description = adoc.sections.find {|s| s.name == "Description" }.blocks[1].content.gsub(/\n/, ' ')
|
24
|
+
config = YAML.load_file(File.join(__dir__, "project.yaml"))
|
25
|
+
project = config.fetch('name', File.split(File.expand_path(__dir__)).last)
|
26
|
+
toplevel_module = config.fetch('toplevel_module') { project.capitalize }
|
27
|
+
version = adoc.attributes['revnumber']
|
28
|
+
dependencies = config.fetch('dependencies', {})
|
29
|
+
if dependencies.nil?
|
30
|
+
dependencies = {}
|
31
|
+
end
|
32
|
+
dev_dependencies = config.fetch('dev-dependencies', {})
|
33
|
+
if dev_dependencies.nil?
|
34
|
+
dev_dependencies = {}
|
35
|
+
end
|
36
|
+
license = config.fetch('license') { "LGPL-3.0" }
|
37
|
+
executables = Dir.glob(File.join(__dir__, "bin", "*")).map {|f| File.basename(f) }
|
38
|
+
#["rake", "asciidoctor", "yard", "pry", "rspec", "rspec-sequel-formatter", "#{project}-tests"]
|
39
|
+
["rake", "asciidoctor", "yard", "pry", "rspec"].each do |dep|
|
40
|
+
dev_dependencies[dep] = dev_dependencies.fetch(dep) {|d| "=#{installed_gem_version(d)}" }
|
41
|
+
end
|
42
|
+
|
43
|
+
gemspec_template = <<GEMSPEC
|
44
|
+
Gem::Specification.new do |s|
|
45
|
+
s.name = "<%= project %>"
|
46
|
+
s.version = "<%= version %>"
|
47
|
+
s.licenses = ["<%= license %>"]
|
48
|
+
s.platform = Gem::Platform::RUBY
|
49
|
+
s.summary = "<%= summary %>"
|
50
|
+
s.description = "<%= description %>"
|
51
|
+
s.authors = ["<%= adoc.author %>"]
|
52
|
+
s.email = "<%= adoc.attributes['email'] %>"
|
53
|
+
s.date = "<%= Date.today %>"
|
54
|
+
s.files = [<%= all_files.map{|f| '"' + f + '"' }.join(",\n ") %>]
|
55
|
+
s.homepage = "<%= adoc.attributes['homepage'] %>"
|
56
|
+
s.bindir = "bin"
|
57
|
+
s.executables = [<%= executables.map{|f| '"' + f + '"' }.join(",\n ") %>]
|
58
|
+
|
59
|
+
% dependencies.each_pair do |req, vers|
|
60
|
+
s.add_dependency "<%= req %>", "<%= vers %>"
|
61
|
+
% end
|
62
|
+
|
63
|
+
% dev_dependencies.each_pair do |req, vers|
|
64
|
+
s.add_development_dependency "<%= req %>", "<%= vers %>"
|
65
|
+
% end
|
66
|
+
end
|
67
|
+
GEMSPEC
|
68
|
+
|
69
|
+
task default: [:gen_version, :gemspec, :gemfile, :build]
|
70
|
+
|
71
|
+
task :gen_version do
|
72
|
+
File.open(File.join("lib", project, "version.rb"), 'w') {|f|
|
73
|
+
f.puts "module #{toplevel_module}"
|
74
|
+
major, minor, tiny = *version.split(/\./).map {|p| p.to_i }
|
75
|
+
f.puts ' VERSION = "' + version + '"'
|
76
|
+
f.puts " VERSION_MAJOR = #{major}"
|
77
|
+
f.puts " VERSION_MINOR = #{minor}"
|
78
|
+
f.puts " VERSION_TINY = #{tiny}"
|
79
|
+
f.puts "end"
|
80
|
+
}
|
81
|
+
end
|
82
|
+
|
83
|
+
task :gemspec => [:gen_version] do
|
84
|
+
files_in_git = IO.popen(["git", "ls-files"], 'r') { |io| io.readlines.map {|l| l.chomp } }
|
85
|
+
all_files = filtered_project_files()
|
86
|
+
if all_files - files_in_git
|
87
|
+
puts "Looks like there's some files uncommitted."
|
88
|
+
end
|
89
|
+
|
90
|
+
requires = all_files.grep(/\.rb$/).
|
91
|
+
map {|f| File.readlines(f).grep (/^\s*require(?!_relative)/) }.
|
92
|
+
flatten.
|
93
|
+
map {|line| line.split(/['"]/).at(1).split('/').at(0) }.
|
94
|
+
uniq
|
95
|
+
|
96
|
+
missing_deps = requires - dependencies.keys
|
97
|
+
if missing_deps.length > 0
|
98
|
+
puts "There may be some dependencies not listed for the gemspec:"
|
99
|
+
puts missing_deps.join(", ")
|
100
|
+
end
|
101
|
+
|
102
|
+
File.open(project + ".gemspec", 'w') do |f|
|
103
|
+
erb = ERB.new(gemspec_template, nil, "%<>")
|
104
|
+
f.write(erb.result(binding))
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
task :gemfile do
|
109
|
+
File.open("Gemfile", 'w') do |f|
|
110
|
+
f.puts "source 'https://rubygems.org"
|
111
|
+
f.puts "gemspec"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
task :build => [:clean, :gemspec, :gemfile] do
|
116
|
+
system "gem build #{project}.gemspec"
|
117
|
+
end
|
118
|
+
|
119
|
+
task :install => [:build] do
|
120
|
+
system "gem install ./#{project}-#{version}.gem"
|
121
|
+
end
|
122
|
+
|
123
|
+
task :clean do
|
124
|
+
rm_f "./#{project}-#{version}.gem"
|
125
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Collects Apache metrics and submits them to Riemann
|
4
|
+
# More information can be found at http://httpd.apache.org/docs/2.4/mod/mod_status.html
|
5
|
+
|
6
|
+
# Removes whitespace from 'Total Accesses' and 'Total kBytes' for output to graphite
|
7
|
+
|
8
|
+
require_relative "../lib/riemann-monitors"
|
9
|
+
|
10
|
+
class Riemann::Monitors::ApacheStatus
|
11
|
+
include Riemann::Monitors
|
12
|
+
require 'net/http'
|
13
|
+
require 'uri'
|
14
|
+
|
15
|
+
opt :uri, 'Apache Server Status URI', :default => 'http://localhost/server-status'
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
@uri = URI.parse(opts[:uri]) + '?auto'
|
19
|
+
# Sample Response with ExtendedStatus On
|
20
|
+
# Total Accesses: 20643
|
21
|
+
# Total kBytes: 36831
|
22
|
+
# CPULoad: .0180314
|
23
|
+
# Uptime: 43868
|
24
|
+
# ReqPerSec: .470571
|
25
|
+
# BytesPerSec: 859.737
|
26
|
+
# BytesPerReq: 1827.01
|
27
|
+
# BusyWorkers: 6
|
28
|
+
# IdleWorkers: 94
|
29
|
+
# Scoreboard: ___K_____K____________W_
|
30
|
+
|
31
|
+
@scoreboard_map = { '_' => 'waiting', 'S' => 'starting', 'R' => 'reading', 'W' => 'sending',
|
32
|
+
'K' => 'keepalive', 'D' => 'dns', 'C' => 'closing', 'L' => 'logging', 'G' => 'graceful',
|
33
|
+
'I' => 'idle', '.' => 'open' }
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
def get_scoreboard_metrics(response)
|
38
|
+
results = Hash.new(0)
|
39
|
+
|
40
|
+
response.slice! 'Scoreboard: '
|
41
|
+
response.each_char do |char|
|
42
|
+
results[char] += 1
|
43
|
+
end
|
44
|
+
Hash[results.map { |k, v| [@scoreboard_map[k], v] }]
|
45
|
+
end
|
46
|
+
|
47
|
+
def report_metrics(metrics)
|
48
|
+
metrics.each do |k, v|
|
49
|
+
report(
|
50
|
+
:service => "httpd #{k}",
|
51
|
+
:metric => v.to_f,
|
52
|
+
:state => 'ok',
|
53
|
+
:tags => ['httpd']
|
54
|
+
)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def get_connection
|
59
|
+
response = nil
|
60
|
+
begin
|
61
|
+
response = Net::HTTP.get(@uri)
|
62
|
+
rescue => e
|
63
|
+
report(
|
64
|
+
:service => 'httpd health',
|
65
|
+
:state => 'critical',
|
66
|
+
:description => 'Httpd connection error: #{e.class} - #{e.message}',
|
67
|
+
:tags => ['httpd']
|
68
|
+
)
|
69
|
+
else
|
70
|
+
report(
|
71
|
+
:service => 'httpd health',
|
72
|
+
:state => 'ok',
|
73
|
+
:description => 'Httpd connection status ok',
|
74
|
+
:tags => ['httpd']
|
75
|
+
)
|
76
|
+
end
|
77
|
+
response
|
78
|
+
end
|
79
|
+
|
80
|
+
def tick
|
81
|
+
unless (response = get_connection).nil?
|
82
|
+
response.each_line do |line|
|
83
|
+
metrics = Hash.new
|
84
|
+
|
85
|
+
if line =~ /Scoreboard/
|
86
|
+
metrics = get_scoreboard_metrics(line.strip)
|
87
|
+
else
|
88
|
+
key, value = line.strip.split(':')
|
89
|
+
metrics[key.gsub(/\s/, '')] = value
|
90
|
+
end
|
91
|
+
report_metrics(metrics)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
Riemann::Monitors::ApacheStatus.run
|
data/bin/riemann-bench
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Connects to a server (first arg) and populates it with a constant stream of
|
4
|
+
# events for testing.
|
5
|
+
|
6
|
+
require_relative "../lib/riemann-monitors"
|
7
|
+
require 'pp'
|
8
|
+
|
9
|
+
class Riemann::Monitors::Bench
|
10
|
+
include Riemann::Monitors
|
11
|
+
|
12
|
+
attr_accessor :client, :hosts, :services, :states
|
13
|
+
def initialize
|
14
|
+
@hosts = [nil] + (0...10).map { |i| "host#{i}" }
|
15
|
+
@hosts = %w(a b c d e f g h i j)
|
16
|
+
@services = %w(test1 test2 test3 foo bar baz xyzzy attack cat treat)
|
17
|
+
@states = {}
|
18
|
+
end
|
19
|
+
|
20
|
+
def evolve(state)
|
21
|
+
m = state[:metric] + (rand - 0.5) * 0.1
|
22
|
+
m = [[0,m].max, 1].min
|
23
|
+
|
24
|
+
s = case m
|
25
|
+
when 0...0.75
|
26
|
+
'ok'
|
27
|
+
when 0.75...0.9
|
28
|
+
'warning'
|
29
|
+
when 0.9..1.0
|
30
|
+
'critical'
|
31
|
+
end
|
32
|
+
|
33
|
+
{
|
34
|
+
:metric => m,
|
35
|
+
:state => s,
|
36
|
+
:host => state[:host],
|
37
|
+
:service => state[:service],
|
38
|
+
:description => "at #{Time.now}"
|
39
|
+
}
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
def tick
|
44
|
+
# pp @states
|
45
|
+
hosts.product(services).each do |id|
|
46
|
+
client << (states[id] = evolve(states[id]))
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def run
|
51
|
+
start
|
52
|
+
loop do
|
53
|
+
sleep 0.05
|
54
|
+
tick
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def start
|
59
|
+
hosts.product(services).each do |host, service|
|
60
|
+
states[[host, service]] = {
|
61
|
+
:metric => 0.5,
|
62
|
+
:state => 'ok',
|
63
|
+
:description => "Starting up",
|
64
|
+
:host => host,
|
65
|
+
:service => service
|
66
|
+
}
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
Riemann::Bench.new.run
|
@@ -0,0 +1,58 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Gathers load balancer statistics from Cloudant.com (shared cluster) and submits them to Riemann.
|
4
|
+
|
5
|
+
require File.expand_path('../../lib/riemann/tools', __FILE__)
|
6
|
+
|
7
|
+
class Riemann::Monitors::Cloudant
|
8
|
+
include Riemann::Monitors
|
9
|
+
require 'net/http'
|
10
|
+
require 'json'
|
11
|
+
|
12
|
+
opt :cloudant_username, "Cloudant username", :type => :string, :required => true
|
13
|
+
opt :cloudant_password, "Cloudant pasword", :type => :string, :required => true
|
14
|
+
|
15
|
+
def tick
|
16
|
+
json = JSON.parse(get_json().body)
|
17
|
+
json.each do |node|
|
18
|
+
return if node['svname'] == 'BACKEND' # this is just a sum of all nodes.
|
19
|
+
|
20
|
+
ns = "cloudant #{node['pxname']}"
|
21
|
+
cluster_name = node['tracked'].split('.')[0] # ie: meritage.cloudant.com
|
22
|
+
|
23
|
+
# report health of each node.
|
24
|
+
report(
|
25
|
+
:service => ns,
|
26
|
+
:state => (node['status'] == 'UP' ? 'ok' : 'critical'),
|
27
|
+
:tags => ['cloudant', cluster_name]
|
28
|
+
)
|
29
|
+
|
30
|
+
# report property->metric of each node.
|
31
|
+
node.each do |property, metric|
|
32
|
+
unless ['pxname', 'svname', 'status', 'tracked'].include?(property)
|
33
|
+
report(
|
34
|
+
:host => node['tracked'],
|
35
|
+
:service => "#{ns} #{property}",
|
36
|
+
:metric => metric.to_f,
|
37
|
+
:state => (node['status'] == 'UP' ? 'ok' : 'critical'),
|
38
|
+
:tags => ['cloudant', cluster_name]
|
39
|
+
)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def get_json
|
47
|
+
http = Net::HTTP.new('cloudant.com', 443)
|
48
|
+
http.use_ssl = true
|
49
|
+
http.start do |h|
|
50
|
+
get = Net::HTTP::Get.new('/api/load_balancer')
|
51
|
+
get.basic_auth opts[:cloudant_username], opts[:cloudant_password]
|
52
|
+
h.request get
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
Riemann::Monitors::Cloudant.run
|
data/bin/riemann-consul
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Reports service and node status to riemann
|
4
|
+
|
5
|
+
require File.expand_path('../../lib/riemann/tools', __FILE__)
|
6
|
+
require 'socket'
|
7
|
+
require 'net/http'
|
8
|
+
require 'uri'
|
9
|
+
require 'json'
|
10
|
+
|
11
|
+
class Riemann::Monitors::ConsulHealth
|
12
|
+
include Riemann::Monitors
|
13
|
+
|
14
|
+
opt :consul_host, "Consul API Host (default to localhost)", :default => "localhost"
|
15
|
+
opt :consul_port, "Consul API Host (default to 8500)", :default => "8500"
|
16
|
+
opt :prefix, "prefix to use for all service names when reporting", :default => "consul "
|
17
|
+
opt :minimum_services_per_node, "minimum services per node (default: 0)", :default => 0
|
18
|
+
|
19
|
+
def initialize
|
20
|
+
|
21
|
+
@hostname = opts[:consul_host]
|
22
|
+
@prefix = opts[:prefix]
|
23
|
+
@minimum_services_per_node = opts[:minimum_services_per_node]
|
24
|
+
@underlying_ip = IPSocket.getaddress(@hostname)
|
25
|
+
@consul_leader_url = URI.parse("http://" + opts[:consul_host] + ":" + opts[:consul_port] + "/v1/status/leader")
|
26
|
+
@consul_services_url = URI.parse("http://" + opts[:consul_host] + ":" + opts[:consul_port] + "/v1/catalog/services")
|
27
|
+
@consul_nodes_url = URI.parse("http://" + opts[:consul_host] + ":" + opts[:consul_port] + "/v1/catalog/nodes")
|
28
|
+
@consul_health_url_prefix = "http://" + opts[:consul_host] + ":" + opts[:consul_port] + "/v1/health/service/"
|
29
|
+
|
30
|
+
@last_services_read = Hash.new
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
def alert(hostname, service, state, metric, description)
|
35
|
+
|
36
|
+
opts = { :host => hostname,
|
37
|
+
:service => service.to_s,
|
38
|
+
:state => state.to_s,
|
39
|
+
:metric => metric,
|
40
|
+
:description => description }
|
41
|
+
|
42
|
+
report(opts)
|
43
|
+
end
|
44
|
+
|
45
|
+
def get(url)
|
46
|
+
Net::HTTP.get_response(url).body
|
47
|
+
end
|
48
|
+
|
49
|
+
def tick
|
50
|
+
|
51
|
+
leader = get(@consul_leader_url)
|
52
|
+
leader_hostname = URI.parse("http://" + leader[1..-2]).hostname
|
53
|
+
|
54
|
+
if (leader_hostname == @underlying_ip)
|
55
|
+
nodes = JSON.parse(get(@consul_nodes_url))
|
56
|
+
services = JSON.parse(get(@consul_services_url))
|
57
|
+
services_by_nodes = Hash.new
|
58
|
+
|
59
|
+
for node in nodes
|
60
|
+
node_name = node["Node"]
|
61
|
+
services_by_nodes[node_name] = 0
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
# For every service
|
66
|
+
for service in services
|
67
|
+
service_name = service[0]
|
68
|
+
health_url = URI.parse(@consul_health_url_prefix + service_name)
|
69
|
+
health_nodes = JSON.parse(get(health_url))
|
70
|
+
|
71
|
+
totalCount = 0
|
72
|
+
okCount = 0
|
73
|
+
|
74
|
+
for node in health_nodes
|
75
|
+
hostname = node["Node"]["Node"]
|
76
|
+
ok = node["Checks"].all? {|check| check["Status"] == "passing"}
|
77
|
+
alert(hostname, "#{@prefix}#{service_name}", ok ? :ok : :critical, ok ? 1 : 0, JSON.generate(node))
|
78
|
+
totalCount += 1
|
79
|
+
okCount += ok ? 1 : 0
|
80
|
+
|
81
|
+
last_services_by_nodes = services_by_nodes[hostname].to_i
|
82
|
+
services_by_nodes[hostname] = last_services_by_nodes + 1
|
83
|
+
end
|
84
|
+
|
85
|
+
if (@last_services_read[service_name] != nil)
|
86
|
+
lastOk = @last_services_read[service_name]
|
87
|
+
if (lastOk != okCount)
|
88
|
+
alert("total", "#{@prefix}#{service_name}-count", okCount >= lastOk ? :ok : :critical, okCount, "Number of passing #{service_name} is: #{okCount}/#{totalCount}, Last time it was: #{lastOk}")
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
@last_services_read[service_name] = okCount
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
# For every node
|
97
|
+
for node,count in services_by_nodes
|
98
|
+
alert(node, "#{@prefix}total-services", (count >= @minimum_services_per_node) ? :ok : :critical, count, "#{count} services in the specified node")
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
Riemann::Monitors::ConsulHealth.run
|
@@ -0,0 +1,55 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Gets the number of files present on a directory and submits it to riemann
|
4
|
+
|
5
|
+
require_relative "../lib/riemann-monitors"
|
6
|
+
|
7
|
+
class Riemann::Monitors::DirFilesCount
|
8
|
+
include Riemann::Monitors
|
9
|
+
|
10
|
+
opt :directory, "", :default => '/var/log'
|
11
|
+
opt :service_prefix, "The first part of the service name, before the directory path", :default => "dir-files-count"
|
12
|
+
opt :warning, "Dir files number warning threshold", :type => Integer
|
13
|
+
opt :critical, "Dir files number critical threshold", :type => Integer
|
14
|
+
opt :alert_on_missing, "Send a critical metric if the directory is missing?", :default => true
|
15
|
+
|
16
|
+
def initialize
|
17
|
+
@dir = opts.fetch(:directory)
|
18
|
+
@service_prefix = opts.fetch(:service_prefix)
|
19
|
+
@warning = opts.fetch(:warning, nil)
|
20
|
+
@critical = opts.fetch(:critical, nil)
|
21
|
+
@alert_on_missing = opts.fetch(:alert_on_missing)
|
22
|
+
end
|
23
|
+
|
24
|
+
def tick
|
25
|
+
if Dir.exists?(@dir)
|
26
|
+
metric = Dir.entries(@dir).size - 2
|
27
|
+
report(
|
28
|
+
:service => "#{@service_prefix} #{@dir}",
|
29
|
+
:metric => metric,
|
30
|
+
:state => state(metric),
|
31
|
+
:tags => ['dir_files_count']
|
32
|
+
)
|
33
|
+
elsif @alert_on_missing
|
34
|
+
report(
|
35
|
+
:service => "#{@service_prefix} #{@dir} missing",
|
36
|
+
:description => "#{@service_prefix} #{@dir} does not exist",
|
37
|
+
:metric => metric,
|
38
|
+
:state => 'critical',
|
39
|
+
:tags => ['dir_files_count']
|
40
|
+
)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def state(metric)
|
45
|
+
if @critical && metric > @critical
|
46
|
+
'critical'
|
47
|
+
elsif @warning && metric > @warning
|
48
|
+
'warning'
|
49
|
+
else
|
50
|
+
'ok'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
Riemann::Monitors::DirFilesCount.run
|
@@ -0,0 +1,55 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Gathers the space used by a directory and submits it to riemann
|
4
|
+
|
5
|
+
require_relative "../lib/riemann-monitors"
|
6
|
+
|
7
|
+
class Riemann::Monitors::DirSpace
|
8
|
+
include Riemann::Monitors
|
9
|
+
|
10
|
+
opt :directory, "", :default => '/var/log'
|
11
|
+
opt :service_prefix, "The first part of the service name, before the directory path", :default => "dir-space"
|
12
|
+
opt :warning, "Dir space warning threshold (in bytes)", :type => Integer
|
13
|
+
opt :critical, "Dir space critical threshold (in bytes)", :type => Integer
|
14
|
+
opt :alert_on_missing, "Send a critical metric if the directory is missing?", :default => true
|
15
|
+
|
16
|
+
def initialize
|
17
|
+
@dir = opts.fetch(:directory)
|
18
|
+
@service_prefix = opts.fetch(:service_prefix)
|
19
|
+
@warning = opts.fetch(:warning, nil)
|
20
|
+
@critical = opts.fetch(:critical, nil)
|
21
|
+
@alert_on_missing = opts.fetch(:alert_on_missing)
|
22
|
+
end
|
23
|
+
|
24
|
+
def tick
|
25
|
+
if Dir.exists?(@dir)
|
26
|
+
metric = `du '#{@dir}'`.lines.to_a.last.split("\t")[0].to_i
|
27
|
+
report(
|
28
|
+
:service => "#{@service_prefix} #{@dir}",
|
29
|
+
:metric => metric,
|
30
|
+
:state => state(metric),
|
31
|
+
:tags => ['dir_space']
|
32
|
+
)
|
33
|
+
elsif @alert_on_missing
|
34
|
+
report(
|
35
|
+
:service => "#{@service_prefix} #{@dir} missing",
|
36
|
+
:description => "#{@service_prefix} #{@dir} does not exist",
|
37
|
+
:metric => metric,
|
38
|
+
:state => 'critical',
|
39
|
+
:tags => ['dir_space']
|
40
|
+
)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def state(metric)
|
45
|
+
if @critical && metric > @critical
|
46
|
+
'critical'
|
47
|
+
elsif @warning && metric > @warning
|
48
|
+
'warning'
|
49
|
+
else
|
50
|
+
'ok'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
Riemann::Monitors::DirSpace.run
|