panoptimon 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +24 -0
- data/Gemfile +4 -0
- data/LICENSE +29 -0
- data/README.md +78 -0
- data/Rakefile +14 -0
- data/bin/panoptimon +118 -0
- data/collectors/cpu/cpu +39 -0
- data/collectors/cpu/cpu.json +1 -0
- data/collectors/disk/disk +37 -0
- data/collectors/disk/disk.json +1 -0
- data/collectors/disk/requires +1 -0
- data/collectors/disk_free/disk_free +15 -0
- data/collectors/disk_free/disk_free.json +1 -0
- data/collectors/dns/README.md +33 -0
- data/collectors/dns/dns +9 -0
- data/collectors/dns/dns.json +7 -0
- data/collectors/dns/lib/panoptimon-collector-dns/dns.rb +43 -0
- data/collectors/dns/lib/panoptimon-collector-dns.rb +2 -0
- data/collectors/dns/panoptimon-collector-dns.gemspec +4 -0
- data/collectors/files/README.md +32 -0
- data/collectors/files/files +129 -0
- data/collectors/files/files.json +14 -0
- data/collectors/files/spec/files_spec.rb +57 -0
- data/collectors/haproxy/README.md +40 -0
- data/collectors/haproxy/haproxy +16 -0
- data/collectors/haproxy/haproxy.json +3 -0
- data/collectors/haproxy/lib/panoptimon-collector-haproxy/haproxy.rb +149 -0
- data/collectors/haproxy/lib/panoptimon-collector-haproxy.rb +1 -0
- data/collectors/haproxy/notes.txt +13 -0
- data/collectors/haproxy/spec/haproxy_spec.rb +98 -0
- data/collectors/haproxy/spec/haproxy_spec.rb-get.html +22 -0
- data/collectors/haproxy/spec/haproxy_spec.rb-show_info.txt +21 -0
- data/collectors/haproxy/spec/haproxy_spec.rb-show_stat.csv +25 -0
- data/collectors/haproxy/spec/haproxy_spec.rb-show_stat2.csv +11 -0
- data/collectors/http/README.md +49 -0
- data/collectors/http/http +27 -0
- data/collectors/http/http.json +3 -0
- data/collectors/http/lib/panoptimon-collector-http/http.rb +74 -0
- data/collectors/http/lib/panoptimon-collector-http/version.rb +7 -0
- data/collectors/http/lib/panoptimon-collector-http.rb +2 -0
- data/collectors/interfaces/interfaces +33 -0
- data/collectors/interfaces/interfaces.json +1 -0
- data/collectors/iostat/iostat +53 -0
- data/collectors/iostat/iostat.json +1 -0
- data/collectors/json/README.md +27 -0
- data/collectors/json/json +37 -0
- data/collectors/json/json.json +1 -0
- data/collectors/load/load +15 -0
- data/collectors/load/load.json +1 -0
- data/collectors/memcached/memcached +55 -0
- data/collectors/memcached/memcached.json +7 -0
- data/collectors/memcached/test-notes.txt +3 -0
- data/collectors/memory/memory +33 -0
- data/collectors/memory/memory.json +1 -0
- data/collectors/mysql_status/mysql_status +52 -0
- data/collectors/mysql_status/mysql_status.json +4 -0
- data/collectors/network/network +67 -0
- data/collectors/network/network.json +18 -0
- data/collectors/nginx/README.md +32 -0
- data/collectors/nginx/lib/panoptimon-collector-nginx/nginx.rb +45 -0
- data/collectors/nginx/lib/panoptimon-collector-nginx.rb +2 -0
- data/collectors/nginx/nginx +11 -0
- data/collectors/nginx/nginx.json +3 -0
- data/collectors/nginx/panoptimon-collector-nginx.gemspec +4 -0
- data/collectors/ping/README.md +54 -0
- data/collectors/ping/ping +57 -0
- data/collectors/ping/ping.json +7 -0
- data/collectors/process/README.md +36 -0
- data/collectors/process/process +61 -0
- data/collectors/process/process.json +7 -0
- data/collectors/service/README.md +51 -0
- data/collectors/service/samples/.gitignore +1 -0
- data/collectors/service/samples/data/disconnect +11 -0
- data/collectors/service/samples/data/flappy +7 -0
- data/collectors/service/samples/data/solid +18 -0
- data/collectors/service/samples/replay +27 -0
- data/collectors/service/service +86 -0
- data/collectors/service/service.json +7 -0
- data/collectors/smtp/lib/panoptimon-collector-smtp/smtp.rb +30 -0
- data/collectors/smtp/lib/panoptimon-collector-smtp.rb +1 -0
- data/collectors/smtp/smtp +27 -0
- data/collectors/smtp/smtp.json +10 -0
- data/collectors/socket/README.md +36 -0
- data/collectors/socket/lib/panoptimon-collector-socket/socket.rb +38 -0
- data/collectors/socket/lib/panoptimon-collector-socket/tcp.rb +34 -0
- data/collectors/socket/lib/panoptimon-collector-socket/unix.rb +28 -0
- data/collectors/socket/lib/panoptimon-collector-socket.rb +3 -0
- data/collectors/socket/socket +13 -0
- data/collectors/socket/socket.json +16 -0
- data/collectors/socket/tests/tcp_spec.rb +21 -0
- data/collectors/socket/tests/unix_spec.rb +35 -0
- data/collectors/ssh/README.md +27 -0
- data/collectors/ssh/ssh +41 -0
- data/collectors/ssh/ssh.json +3 -0
- data/lib/panoptimon/collector.rb +135 -0
- data/lib/panoptimon/eventmonkeypatch/popen3.rb +40 -0
- data/lib/panoptimon/http.rb +63 -0
- data/lib/panoptimon/logger.rb +19 -0
- data/lib/panoptimon/monitor.rb +154 -0
- data/lib/panoptimon/util/string-with-as_number.rb +5 -0
- data/lib/panoptimon/util.rb +23 -0
- data/lib/panoptimon/version.rb +5 -0
- data/lib/panoptimon.rb +144 -0
- data/misc/collector_setup.rb +23 -0
- data/misc/monitor_setup.rb +25 -0
- data/misc/plugins_setup.rb +25 -0
- data/misc/riemann-cli.rb +33 -0
- data/panoptimon.gemspec +33 -0
- data/plugins/daemon_health/README.md +31 -0
- data/plugins/daemon_health/daemon_health.json +4 -0
- data/plugins/daemon_health/daemon_health.rb +34 -0
- data/plugins/daemon_health/lib/panoptimon-plugin-daemon_health/rollup.rb +64 -0
- data/plugins/daemon_health/panoptimon-plugin-daemon_health.gemspec +10 -0
- data/plugins/daemon_health/spec/moving_avg_spec.rb +24 -0
- data/plugins/email/README.md +30 -0
- data/plugins/email/email.json +3 -0
- data/plugins/email/email.rb +52 -0
- data/plugins/log_to_file/log_to_file.json +1 -0
- data/plugins/log_to_file/log_to_file.rb +8 -0
- data/plugins/log_to_logger/log_to_logger.json +3 -0
- data/plugins/log_to_logger/log_to_logger.rb +7 -0
- data/plugins/metrics_http/README.md +23 -0
- data/plugins/metrics_http/metrics_http.json +1 -0
- data/plugins/metrics_http/metrics_http.rb +17 -0
- data/plugins/riemann_stream/requires +1 -0
- data/plugins/riemann_stream/riemann_stream.json +3 -0
- data/plugins/riemann_stream/riemann_stream.rb +23 -0
- data/plugins/status_http/requires +1 -0
- data/plugins/status_http/status_http.json +1 -0
- data/plugins/status_http/status_http.rb +60 -0
- data/sample_configs/1/collectors/alls_well.json +6 -0
- data/sample_configs/1/collectors/clock/clock +12 -0
- data/sample_configs/1/collectors/clock.json +1 -0
- data/sample_configs/1/collectors/df/df.json +6 -0
- data/sample_configs/1/collectors/df/wrap_df +21 -0
- data/sample_configs/1/collectors/load.json +1 -0
- data/sample_configs/1/panoptimon.json +4 -0
- data/sample_configs/1/plugins/isup/isup.rb +3 -0
- data/sample_configs/1/plugins/isup.json +1 -0
- data/sample_configs/1/plugins/log_to_file.json +1 -0
- data/sample_configs/err_handler/collectors/fail.json +4 -0
- data/sample_configs/err_handler/collectors/noisy.json +5 -0
- data/sample_configs/err_handler/collectors/noisy_failure.json +5 -0
- data/sample_configs/err_handler/collectors/notfound.json +4 -0
- data/sample_configs/err_handler/panoptimon.json +3 -0
- data/sample_configs/err_handler/plugins/.exists +0 -0
- data/sample_configs/passthru/collectors/beep.json +5 -0
- data/sample_configs/passthru/collectors/cat/collect_this.json +1 -0
- data/sample_configs/passthru/collectors/cat.json +5 -0
- data/sample_configs/passthru/panoptimon.json +3 -0
- data/sample_configs/passthru/plugins/okcat/okcat.rb +17 -0
- data/sample_configs/passthru/plugins/okcat.json +1 -0
- data/sample_configs/plugin_error/collectors/beep.json +5 -0
- data/sample_configs/plugin_error/panoptimon.json +1 -0
- data/sample_configs/plugin_error/plugins/error_always/error_always.rb +1 -0
- data/sample_configs/plugin_error/plugins/error_always.json +1 -0
- data/sample_configs/slow/collectors/slowbeep.json +6 -0
- data/sample_configs/slow/panoptimon.json +1 -0
- data/sample_configs/slow/plugins/.exists +0 -0
- data/sample_configs/timeout_newline/collectors/slow_lf.json +8 -0
- data/sample_configs/timeout_newline/panoptimon.json +1 -0
- data/sample_configs/timeout_newline/plugins/.exists +0 -0
- data/sample_configs/timeout_not/collectors/slowly.json +7 -0
- data/sample_configs/timeout_not/panoptimon.json +1 -0
- data/sample_configs/timeout_not/plugins/.exists +0 -0
- data/spec/collector/config_spec.rb +30 -0
- data/spec/collector/initialize_spec.rb +24 -0
- data/spec/collector/metric_spec.rb +22 -0
- data/spec/passthru_spec.rb +13 -0
- data/spec/util_spec.rb +37 -0
- data/tools/link_and_enable +37 -0
- data/tools/metricify +8 -0
- metadata +319 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 2ec4c546129725a6c662ae42fab05ac37d2ef7d5
|
4
|
+
data.tar.gz: 3c52bd2b259bf5d5b43b168d6c0b078b29169681
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0baa25a867349f8702542e9232e7e7478074cb918c901033a5de65ae0b310e2cfeee7e164f4666faa6ae1b4a9abca1735d5a17f7fa27e7b4e4b4294655002652
|
7
|
+
data.tar.gz: 332e77a896f7291841125ff938d4f80004f91c164e704027cc327c3ae9113e6534d5a3da809d77f630c7a31c41b628a0d3d172aaedc86951ed5704fe61374ed9
|
data/.gitignore
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
/notes.txt
|
19
|
+
/metrics.log
|
20
|
+
/panoptimon.output
|
21
|
+
|
22
|
+
# for all your experimental needs e.g. symlinks of configs
|
23
|
+
/x/
|
24
|
+
.vagrant
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
Copyright (C) 2012 Sourcefire, Inc.
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
5
|
+
modification, are permitted provided that the following conditions are
|
6
|
+
met:
|
7
|
+
|
8
|
+
* Redistributions of source code must retain the above copyright
|
9
|
+
notice, this list of conditions and the following disclaimer.
|
10
|
+
|
11
|
+
* Redistributions in binary form must reproduce the above copyright
|
12
|
+
notice, this list of conditions and the following disclaimer in the
|
13
|
+
documentation and/or other materials provided with the distribution.
|
14
|
+
|
15
|
+
* Neither the name of *Sourcefire, Inc.* nor the names of its
|
16
|
+
contributors may be used to endorse or promote products derived from
|
17
|
+
this software without specific prior written permission.
|
18
|
+
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
20
|
+
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
21
|
+
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
22
|
+
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
23
|
+
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
24
|
+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
25
|
+
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
26
|
+
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
27
|
+
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
28
|
+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
29
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
# Panoptimon
|
2
|
+
|
3
|
+
The All-Seeing System Monitor Daemon
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
$ gem install panoptimon
|
8
|
+
|
9
|
+
## Usage
|
10
|
+
|
11
|
+
The `--config-dir` option will automatically set paths to configuration
|
12
|
+
file, collectors, and plugins (and can be referred to as '%' as shown
|
13
|
+
below.)
|
14
|
+
|
15
|
+
$ panoptimon --help
|
16
|
+
Usage: panoptimon [options]
|
17
|
+
-C, --config-dir DIR Config directory (/etc/panoptimon/)
|
18
|
+
-c, --config-file FILENAME Alternative configuration file
|
19
|
+
(%/panoptimon.json)
|
20
|
+
-D, --[no-]foreground Don't daemonize (false)
|
21
|
+
--collectors-dir DIR Collectors directory (%/collectors)
|
22
|
+
--plugins-dir DIR Plugins directory (%/plugins)
|
23
|
+
--list-collectors list all collectors found
|
24
|
+
--list-plugins list all plugins found
|
25
|
+
-o, --configure X=Y Set configuration values
|
26
|
+
--show WHAT Show/validate settings for:
|
27
|
+
'config' / collector:foo / plugin:foo
|
28
|
+
--plugin-test FILE Load and test plugin(s).
|
29
|
+
-d, --debug Enable debugging.
|
30
|
+
--verbose Enable verbose output
|
31
|
+
-v, --version Print version
|
32
|
+
--help-defaults Show default config values
|
33
|
+
-h, --help Show this message
|
34
|
+
|
35
|
+
See [the wiki](https://github.com/synthesist/panoptimon/wiki) for more
|
36
|
+
info.
|
37
|
+
|
38
|
+
## Copyright and License
|
39
|
+
|
40
|
+
This software is released under the following (BSD 3-clause) license:
|
41
|
+
|
42
|
+
Copyright (C) 2012 Sourcefire, Inc.
|
43
|
+
All rights reserved.
|
44
|
+
|
45
|
+
Redistribution and use in source and binary forms, with or without
|
46
|
+
modification, are permitted provided that the following conditions are
|
47
|
+
met:
|
48
|
+
|
49
|
+
* Redistributions of source code must retain the above copyright
|
50
|
+
notice, this list of conditions and the following disclaimer.
|
51
|
+
|
52
|
+
* Redistributions in binary form must reproduce the above copyright
|
53
|
+
notice, this list of conditions and the following disclaimer in the
|
54
|
+
documentation and/or other materials provided with the distribution.
|
55
|
+
|
56
|
+
* Neither the name of *Sourcefire, Inc.* nor the names of its
|
57
|
+
contributors may be used to endorse or promote products derived from
|
58
|
+
this software without specific prior written permission.
|
59
|
+
|
60
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
61
|
+
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
62
|
+
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
63
|
+
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
64
|
+
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
65
|
+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
66
|
+
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
67
|
+
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
68
|
+
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
69
|
+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
70
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
71
|
+
|
72
|
+
## Contributing
|
73
|
+
|
74
|
+
1. Fork it
|
75
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
76
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
77
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
78
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
require "bundler/gem_tasks"
|
3
|
+
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
|
6
|
+
desc 'Default: run specs.'
|
7
|
+
task :default => :spec
|
8
|
+
|
9
|
+
desc "Run specs"
|
10
|
+
RSpec::Core::RakeTask.new do |t|
|
11
|
+
t.pattern = "./spec/**/*_spec.rb" # don't need this, it's default.
|
12
|
+
# Put spec opts in a file named .rspec in root
|
13
|
+
end
|
14
|
+
|
data/bin/panoptimon
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Panoptimon - The All-Seeing System Monitor Daemon
|
4
|
+
|
5
|
+
# Copyright (C) 2012 Sourcefire, Inc.
|
6
|
+
|
7
|
+
require 'panoptimon'
|
8
|
+
|
9
|
+
require 'json'
|
10
|
+
require 'pathname'
|
11
|
+
|
12
|
+
require 'rubygems'
|
13
|
+
require 'daemons'
|
14
|
+
require 'eventmachine'
|
15
|
+
|
16
|
+
opts = Panoptimon.load_options(ARGV) or exit
|
17
|
+
|
18
|
+
monitor = Panoptimon::Monitor.new(config: opts)
|
19
|
+
monitor.logger.level = ::Logger::DEBUG if opts.debug
|
20
|
+
|
21
|
+
if opts.show then opts.show.each {|show, what|
|
22
|
+
|
23
|
+
getconf = ->(type, name) {
|
24
|
+
file = monitor.send("find_#{type}s").find {|f|
|
25
|
+
f.basename.to_s == "#{name}.json"}
|
26
|
+
{file => monitor.send("_load_#{type}_config", file)}
|
27
|
+
}
|
28
|
+
|
29
|
+
puts JSON.pretty_generate(
|
30
|
+
case show
|
31
|
+
when :config; {config: opts.marshal_dump}
|
32
|
+
when :collector; getconf.call(show, what)
|
33
|
+
when :plugin; getconf.call(show,what)
|
34
|
+
when %r{/}
|
35
|
+
p = Pathname.new(show.to_s)
|
36
|
+
raise "no such file '#{p}'" unless p.exist?
|
37
|
+
{p => monitor.send(
|
38
|
+
"_load_#{p.dirname.basename.to_s.sub(/s$/,'')}_config", p)}
|
39
|
+
else raise "--show '#{show}' argument invalid"
|
40
|
+
end
|
41
|
+
).sub(/^{\n /, '').sub(/\n}$/,'').gsub(/\n /, "\n")
|
42
|
+
exit
|
43
|
+
}; end
|
44
|
+
|
45
|
+
# XXX needs a real test
|
46
|
+
puts "config: ", JSON.pretty_generate(opts.marshal_dump) if opts.debug
|
47
|
+
|
48
|
+
if opts.lists
|
49
|
+
|
50
|
+
opts.lists.uniq.sort.each { |list|
|
51
|
+
does = {
|
52
|
+
collectors: ->() { monitor.find_collectors },
|
53
|
+
plugins: ->() { monitor.find_plugins },
|
54
|
+
}
|
55
|
+
puts does[list].call.tap {|x| x.push('(none)') unless x.length > 0}.
|
56
|
+
unshift("#{list}:").join("\n ");
|
57
|
+
}
|
58
|
+
|
59
|
+
exit
|
60
|
+
end
|
61
|
+
|
62
|
+
if opts.plugin_test
|
63
|
+
|
64
|
+
opts.plugin_test.each do |p|
|
65
|
+
monitor._init_plugin(monitor._load_plugin_config(Pathname.new(p)))
|
66
|
+
end
|
67
|
+
|
68
|
+
module MetricReader
|
69
|
+
def initialize (monitor)
|
70
|
+
@monitor = monitor
|
71
|
+
end
|
72
|
+
|
73
|
+
def notify_readable
|
74
|
+
line = @io.readline
|
75
|
+
begin
|
76
|
+
data = JSON.parse(line)
|
77
|
+
rescue
|
78
|
+
$stderr.puts "error parsing #{line.dump} - #{$!}"
|
79
|
+
end
|
80
|
+
EM.next_tick {
|
81
|
+
@monitor.bus_driver(Panoptimon::Metric.new(:input, data))
|
82
|
+
}
|
83
|
+
rescue EOFError
|
84
|
+
detach
|
85
|
+
EM.stop
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
$stderr.puts "Enter JSON metrics or type Ctrl-D to exit." if
|
90
|
+
$stdin.tty?
|
91
|
+
|
92
|
+
EM.run {
|
93
|
+
monitor.run
|
94
|
+
EM.watch($stdin, MetricReader, monitor).notify_readable = true
|
95
|
+
}
|
96
|
+
|
97
|
+
exit
|
98
|
+
end
|
99
|
+
|
100
|
+
########################################################################
|
101
|
+
if opts.daemonize
|
102
|
+
# TODO could possibly use webrick::daemon.start or process.daemon,
|
103
|
+
# but this has support for redirecting io to logfiles... worth it?
|
104
|
+
Daemons.daemonize(
|
105
|
+
app_name: Pathname.new($0).basename.to_s,
|
106
|
+
log_output: true)
|
107
|
+
end
|
108
|
+
|
109
|
+
# TODO maybe split config loading / init across daemonize for better
|
110
|
+
# startup diagnostics about config errors
|
111
|
+
monitor.load_plugins
|
112
|
+
monitor.load_collectors
|
113
|
+
|
114
|
+
EM.run {
|
115
|
+
monitor.run
|
116
|
+
}
|
117
|
+
|
118
|
+
# vim:ts=2:sw=2:et:sta
|
data/collectors/cpu/cpu
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$stdout.sync = true # persistent process
|
4
|
+
|
5
|
+
require 'json'
|
6
|
+
opt = ARGV[0] ? JSON::parse(ARGV[0]) : {'interval' => 1}
|
7
|
+
|
8
|
+
p = IO.popen([*%w{vmstat -n }, opt['interval'].to_s], 'r')
|
9
|
+
p.readline # scrap
|
10
|
+
optional = {stolen: 'st'}
|
11
|
+
want = {user: 'us', system: 'sy', idle: 'id', wait: 'wa'}.
|
12
|
+
merge(optional)
|
13
|
+
|
14
|
+
# index the header
|
15
|
+
head = p.readline.chomp.sub(/^\s+/, '').split(/\s+/)
|
16
|
+
head = Hash[*head.zip(0..(head.length-1)).flatten]
|
17
|
+
|
18
|
+
outs = {}; want.each {|k,v|
|
19
|
+
if head.include?(v)
|
20
|
+
outs[k] = head[v]
|
21
|
+
else
|
22
|
+
raise "missing key '#{v}' (#{k}) - expected in header" \
|
23
|
+
unless optional.include?(k)
|
24
|
+
end
|
25
|
+
}
|
26
|
+
|
27
|
+
# discard first (unprimed) output
|
28
|
+
p.readline; puts '{}'
|
29
|
+
|
30
|
+
while l = p.readline.sub(/^\s+/, '')
|
31
|
+
if l =~ /\d+\s+\d/
|
32
|
+
l = l.split(/\s+/)
|
33
|
+
puts JSON::generate(
|
34
|
+
Hash[*outs.keys.map {|k| [k, l[outs[k]]]}.flatten]
|
35
|
+
)
|
36
|
+
else
|
37
|
+
puts '{}'
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
{"interval" : 3}
|
@@ -0,0 +1,37 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'rubygems'
|
5
|
+
require 'sys/filesystem'
|
6
|
+
|
7
|
+
config = JSON::parse(ARGV[0])
|
8
|
+
|
9
|
+
$stdout.sync = true
|
10
|
+
|
11
|
+
def GB b; (b.to_f / 1024**3).round(6); end # GB significant down to 4kB
|
12
|
+
|
13
|
+
while(true) do
|
14
|
+
info =
|
15
|
+
Sys::Filesystem.mounts.
|
16
|
+
# map {|m| $stderr.puts m.name; m}.
|
17
|
+
find_all{|m| not(m.name =~ /^(devpts|udev|sysfs|tmpfs|none|proc)$/)}.
|
18
|
+
map {|m|
|
19
|
+
s = Sys::Filesystem.stat(m.mount_point)
|
20
|
+
# $stderr.puts "#{m.name} - #{s.inspect}"
|
21
|
+
[m.name, {
|
22
|
+
# block_size: s.block_size,
|
23
|
+
# blocks: s.blocks,
|
24
|
+
# blocks_available: s.blocks_available,
|
25
|
+
space_used: GB((s.blocks - s.blocks_free) * s.block_size),
|
26
|
+
space_free: GB(s.blocks_available * s.block_size),
|
27
|
+
space_priv: GB((s.blocks_free - s.blocks_available) * s.block_size),
|
28
|
+
files_used: s.files - s.files_available,
|
29
|
+
files_free: s.files_available,
|
30
|
+
# files_priv: s.files_free - s.files_available, # XXX any use?
|
31
|
+
}]
|
32
|
+
}
|
33
|
+
puts JSON::generate(Hash[*info.flatten])
|
34
|
+
|
35
|
+
break unless config.include?('interval')
|
36
|
+
sleep config['interval']
|
37
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
{"interval" : 1}
|
@@ -0,0 +1 @@
|
|
1
|
+
gem/sys-filesystem
|
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
df_cmd = 'df -kP' # or without -P on non-gnu...
|
6
|
+
info = %x{#{df_cmd}}.split(/\n/).drop(1).
|
7
|
+
map{|l| l.split(/\s+/).values_at(0,2,3,5)} # fs, used, free, mount
|
8
|
+
.find_all{|x| not(x[0] =~ /^(udev|tmpfs|none)$/)}
|
9
|
+
|
10
|
+
out = {}
|
11
|
+
info.each {|x| out[x[0]] = {
|
12
|
+
used: (x[1].to_f / 1024**2).round(6), # GB significant down to 4kB
|
13
|
+
free: (x[2].to_f / 1024**2).round(6),
|
14
|
+
}}
|
15
|
+
puts JSON::generate(out)
|
@@ -0,0 +1 @@
|
|
1
|
+
{}
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# Config
|
2
|
+
|
3
|
+
The 'hosts' section is a hash of hostnames and record types to be
|
4
|
+
queried. Valid types include 'a', 'mx', 'ns', 'ptr', 'txt', 'cname',
|
5
|
+
and 'any'. Query result types may vary from the request.
|
6
|
+
|
7
|
+
```json
|
8
|
+
|
9
|
+
{
|
10
|
+
"hosts" : {
|
11
|
+
"example.com" : ["a", "mx", "ns"],
|
12
|
+
"www.example.com" : ["cname"],
|
13
|
+
},
|
14
|
+
"nameservers" : [null, "ns.example.com"]
|
15
|
+
}
|
16
|
+
```
|
17
|
+
|
18
|
+
# Results
|
19
|
+
|
20
|
+
The results will include a count (`n`) and _info.records for each.
|
21
|
+
|
22
|
+
```json
|
23
|
+
{
|
24
|
+
"dns|ns.example.com|www.example.com|cname|n" : 1,
|
25
|
+
"dns|default|www.example.com|cname|n" : 1,
|
26
|
+
"dns|ns.example.com|www.example.com|cname|_info" : {
|
27
|
+
"records" : [ "see-also.example.com. ]
|
28
|
+
},
|
29
|
+
"dns|default|www.example.com|cname|_info" : {
|
30
|
+
"records" : [ "see-also.example.com. ]
|
31
|
+
}
|
32
|
+
}
|
33
|
+
```
|
data/collectors/dns/dns
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(__FILE__), 'lib')))
|
4
|
+
require 'rubygems'
|
5
|
+
require 'json'
|
6
|
+
require 'panoptimon-collector-dns'
|
7
|
+
|
8
|
+
client = Panoptimon::Collector::DNS.new(JSON.parse(ARGV[0], {symbolize_names: true}))
|
9
|
+
puts client.query.to_json
|
@@ -0,0 +1,43 @@
|
|
1
|
+
class Array; def to_h; Hash[self]; end; end
|
2
|
+
module Panoptimon
|
3
|
+
module Collector
|
4
|
+
class DNS
|
5
|
+
|
6
|
+
attr_accessor :options
|
7
|
+
|
8
|
+
def initialize(options={})
|
9
|
+
@options = options
|
10
|
+
end
|
11
|
+
|
12
|
+
# types: a, mx, ns, ptr, txt, cname, any
|
13
|
+
|
14
|
+
def query
|
15
|
+
hosts = @options[:hosts]
|
16
|
+
nslist = @options[:nameservers] || [nil]
|
17
|
+
nslist.map {|ns|
|
18
|
+
dns = ::Net::DNS::Resolver.new(
|
19
|
+
ns ? {nameservers: ns} : {})
|
20
|
+
[ns || 'default', hosts.map {|name,types|
|
21
|
+
# TODO allow aliased name for output?
|
22
|
+
# e.g. types.class == Hash ? ...
|
23
|
+
# collect results by type regardless of query
|
24
|
+
typed = Hash.new { |h,k|
|
25
|
+
h[k] = {n: 0, _info: {records: []}} }
|
26
|
+
types.each {|t|
|
27
|
+
dns.search(name.to_s, Net::DNS.const_get(t.upcase)).
|
28
|
+
answer.each { |rec|
|
29
|
+
stash = typed[rec.type.downcase]
|
30
|
+
stash[:n] += 1
|
31
|
+
stash[:_info][:records].push(rec.value)
|
32
|
+
}
|
33
|
+
}
|
34
|
+
[name, typed]
|
35
|
+
}.to_h]
|
36
|
+
}.to_h
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
```json
|
2
|
+
|
3
|
+
{
|
4
|
+
"paths" : {
|
5
|
+
"/tmp" : {}, // implicit name+path, just count everything + report
|
6
|
+
"/tmp swapfiles" : { // names must be unique
|
7
|
+
"path" : "/tmp", // explicit path
|
8
|
+
"match" : "^\\..*\.swp$", // regular expression match
|
9
|
+
"atime" : {"min": 1354051358}, // in absolute epoch seconds
|
10
|
+
// also max, (and relative time using -min/-max)
|
11
|
+
// also for: mtime, ctime, size (only absolute min/max)
|
12
|
+
//
|
13
|
+
// uid/gid : [35, 47], or by excluding: ["not", 0, 42]
|
14
|
+
//
|
15
|
+
// mode: "0[67][45]0" // a right-anchored regexp
|
16
|
+
// mode: "& 0111" // octal bitmask
|
17
|
+
},
|
18
|
+
"/opt" : {"no_list" : true}, // will skip stat() on contents
|
19
|
+
"/bin" : {"no_list" : true, // will only return the count,
|
20
|
+
"mtime" : {"-min" : 600}}, // but stat is needed for the check
|
21
|
+
"/vmlinuz" : {
|
22
|
+
"path" : "/",
|
23
|
+
"only" : ["vmlinuz"], // explicit list / skip readdir()
|
24
|
+
"filter" : ["symlink"], // must be a symlink
|
25
|
+
"mtime" : {"-min" : 3600}, // relative to Time.now
|
26
|
+
},
|
27
|
+
"/bin symlinks" : {"path" : "/bin/", "filter" : ["symlink"]},
|
28
|
+
"/usr/bin swapfiles" : {"path" : "/usr/bin/", "glob" : ".*.swp"}
|
29
|
+
}
|
30
|
+
}
|
31
|
+
|
32
|
+
```
|
@@ -0,0 +1,129 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'pathname'
|
5
|
+
|
6
|
+
# given a list of attributes
|
7
|
+
# returns a function which expects a file or stat object and will answer
|
8
|
+
# true if all of the attributes are true about that object
|
9
|
+
def filters (list)
|
10
|
+
ok = %w{blockdev chardev directory file pipe setgid setuid socket sticky
|
11
|
+
symlink world_readable world_writable zero}
|
12
|
+
ok = ok + ok.map {|i| '!'+i}
|
13
|
+
not_ok = list - ok
|
14
|
+
raise "unsupported: '#{not_ok}'" if not_ok.length > 0
|
15
|
+
list.map! {|i| i = i+'?';
|
16
|
+
i.sub!(/^!/, '') ? ->(f) {not(f.send(i))} : ->(f){f.send(i)}
|
17
|
+
}
|
18
|
+
return ->(f) { return list.all? {|t| t.call(f)} ? true : false }
|
19
|
+
end
|
20
|
+
|
21
|
+
class Code < Proc
|
22
|
+
attr_accessor :source
|
23
|
+
def self.new (*args, src)
|
24
|
+
src = '->(' + args.join(',') + ') {' + src + '}'
|
25
|
+
block = eval(src)
|
26
|
+
super(&block).tap {|x| x.source = src}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def minmax (prop, c) # c is hash of min|-min / max|-max
|
31
|
+
unrel = ->(k) {
|
32
|
+
relk = '-'.+(k.to_s).to_sym
|
33
|
+
c[k] ? c[k].to_i :
|
34
|
+
c[relk] ? "Time.now.to_i - " + c[relk].to_i.to_s
|
35
|
+
: nil
|
36
|
+
}
|
37
|
+
checks = {min: '>=', max: '<='}.map {|k,v|
|
38
|
+
(abs = unrel[k]) ? ["#{v} #{abs}"] : []
|
39
|
+
}.flatten
|
40
|
+
Code.new(:s,
|
41
|
+
checks.length > 1 ?
|
42
|
+
"v = s.send('#{prop}').to_i; " +
|
43
|
+
checks.map {|cmp| 'v ' + cmp}.join(' && ')
|
44
|
+
: "s.send('#{prop}').to_i " + checks[0]
|
45
|
+
)
|
46
|
+
end
|
47
|
+
|
48
|
+
# should prep the report per key (so it can be prepped once, stored and
|
49
|
+
# re-run in persistent mode)
|
50
|
+
def report (key, c)
|
51
|
+
path = Pathname.new(c[:path] || key.to_s)
|
52
|
+
|
53
|
+
sf = [] # list of checks which must all return true per stat
|
54
|
+
sf.push(filters(c[:filters])) if c[:filters]
|
55
|
+
sf.push(->(p) {
|
56
|
+
p.sub!(/^& 0/, '') ?
|
57
|
+
->(){p = p.to_i(8);
|
58
|
+
->(s) { s.mode & 0777 & p > 0 }}[]
|
59
|
+
: ->(){p = /#{p}$/
|
60
|
+
->(s) { sprintf("%04o", s.mode & 0777) =~ p }}[]
|
61
|
+
}.call(c[:mode])) if c[:mode]
|
62
|
+
[:atime, :mtime, :ctime].find_all {|k| c[k]}.each {|k|
|
63
|
+
sf.push(minmax(k, c[k]))
|
64
|
+
}
|
65
|
+
[:size].find_all {|k| c[k]}.each {|k|
|
66
|
+
raise "relative values are nonsensical for #{k}" if
|
67
|
+
c[k][:'-min'] or c[k][:'-max']
|
68
|
+
sf.push(minmax(k, c[k]))
|
69
|
+
}
|
70
|
+
[:uid, :gid].find_all {|k| c[k]}.each {|k|
|
71
|
+
l = c[k].kind_of?(Array) ? c[k] : [c[k]]
|
72
|
+
neg = l[0] == 'not' ? l.shift : nil
|
73
|
+
l = Hash[l.map {|i| [i.to_i, true]}]
|
74
|
+
sf.push(
|
75
|
+
neg ? ->(s) {not l.include?(s.send(k))}
|
76
|
+
: ->(s) {l.include?(s.send(k))}
|
77
|
+
)
|
78
|
+
}
|
79
|
+
|
80
|
+
|
81
|
+
no_stat = c[:no_list] && sf.length == 0 # OPTIMIZATION
|
82
|
+
|
83
|
+
nf = [] # list of checks which must all return true per name
|
84
|
+
if c[:skip]
|
85
|
+
nf.push(->(list){
|
86
|
+
skips = Hash[list.map {|n| [n,true]}]
|
87
|
+
return ->(f) { skips[f.to_s] ? false : true }
|
88
|
+
}.call(c[:skip]))
|
89
|
+
warn "'skip' and 'only' options nonsensical" if c[:only]
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
# TODO I think lstat is the way to go - assumes you want to know about
|
94
|
+
# symlinks more than their targets (make it an option?)
|
95
|
+
|
96
|
+
files = Hash[
|
97
|
+
c[:only] ?
|
98
|
+
c[:only].map {|f| [f, begin; path.+(f).lstat; rescue; nil; end]} :
|
99
|
+
->(got) { no_stat ?
|
100
|
+
got.map {|f| [f.to_s, nil]}
|
101
|
+
: got.map {|f| [f.to_s, path.+(f).lstat]}
|
102
|
+
}.call(
|
103
|
+
c[:glob] ?
|
104
|
+
Pathname.glob(path + c[:glob]).map {|n| n.relative_path_from(path)} :
|
105
|
+
c[:match] ?
|
106
|
+
path.children(false).find_all {|x| x.to_s =~ /#{c[:match]}/o}
|
107
|
+
: path.children(false)
|
108
|
+
)]
|
109
|
+
files.keep_if {|f,s| nf.all? {|c| c.call(f)} } if nf.length > 0
|
110
|
+
files.keep_if {|f,s| sf.all? {|c| c.call(s)} } if sf.length > 0
|
111
|
+
|
112
|
+
res = {count: files.keys.length}
|
113
|
+
return res if c[:no_list]
|
114
|
+
files.length == 0 ? res : res.merge('' => Hash[files.map {|k,v|
|
115
|
+
[k,v ? Hash[
|
116
|
+
%w{dev ino mode nlink uid gid rdev size blksize blocks}
|
117
|
+
.map {|n| [n,v.send(n)]} +
|
118
|
+
%w{atime mtime ctime}.map {|n| [n,v.send(n).to_i]}] : nil]
|
119
|
+
}])
|
120
|
+
# TODO _info => {symlinks => {filename => readlink, ...}
|
121
|
+
end
|
122
|
+
|
123
|
+
def run (opt)
|
124
|
+
p = opt[:paths] or raise "'paths' argument required!"
|
125
|
+
|
126
|
+
puts JSON.generate(Hash[p.map {|k,v| [k, report(k,v)]}])
|
127
|
+
end
|
128
|
+
|
129
|
+
run(JSON::parse(ARGV[0], {symbolize_names: true})) if __FILE__ == $0
|
@@ -0,0 +1,14 @@
|
|
1
|
+
{
|
2
|
+
"description" : "count / stat some files",
|
3
|
+
"paths" : {
|
4
|
+
"/ writable dirs" : {"path" : "/",
|
5
|
+
"only" : ["bin", "boot", "dev", "etc", "home", "lib", "usr"],
|
6
|
+
"skip" : ["tmp"],
|
7
|
+
"filters" : ["directory", "world_writable"]}
|
8
|
+
},
|
9
|
+
"-examples" : [
|
10
|
+
{"/some/dir/" : {}},
|
11
|
+
{"/some/dir/" : {"match" : "^\\..*\\.swp$"},
|
12
|
+
"1+/some/dir" : {"path" : "/some/dir", "glob" : ".*.swp"}}
|
13
|
+
]
|
14
|
+
}
|