panoptimon 0.0.2
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 +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
|
+
}
|