riemann-monitors 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org
2
+ gemspec
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
@@ -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