ganymed 0.1.2 → 0.2.0
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.
- data/.gitignore +2 -0
- data/README.md +10 -9
- data/ganymed.gemspec +2 -1
- data/lib/ganymed.rb +8 -0
- data/lib/ganymed/collector.rb +113 -54
- data/lib/ganymed/collectors/cpu.rb +19 -0
- data/lib/ganymed/collectors/disk.rb +25 -0
- data/lib/ganymed/collectors/iostat.rb +16 -0
- data/lib/ganymed/collectors/load.rb +7 -0
- data/lib/ganymed/collectors/memory.rb +39 -0
- data/lib/ganymed/collectors/metadata.rb +22 -0
- data/lib/ganymed/collectors/network.rb +26 -0
- data/lib/ganymed/collectors/process.rb +16 -0
- data/lib/ganymed/collectors/uptime.rb +8 -0
- data/lib/ganymed/config.yml +4 -8
- data/lib/ganymed/event.rb +9 -4
- data/lib/ganymed/master.rb +1 -0
- data/lib/ganymed/mongodb.rb +2 -1
- data/lib/ganymed/processor.rb +30 -14
- data/lib/ganymed/sampler.rb +2 -3
- data/lib/ganymed/version.rb +2 -1
- metadata +52 -41
- data/lib/ganymed/client.rb +0 -132
- data/lib/ganymed/collector/cpu.rb +0 -32
- data/lib/ganymed/collector/disk.rb +0 -36
- data/lib/ganymed/collector/iostat.rb +0 -26
- data/lib/ganymed/collector/load.rb +0 -19
- data/lib/ganymed/collector/metadata.rb +0 -34
- data/lib/ganymed/collector/network.rb +0 -36
- data/lib/ganymed/collector/procs.rb +0 -22
- data/lib/ganymed/collector/uptime.rb +0 -16
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -2,18 +2,19 @@
|
|
2
2
|
|
3
3
|
Ganymed is an event collection daemon.
|
4
4
|
|
5
|
-
Ganymed processes system and application metrics in realtime and can
|
6
|
-
handle thousands of samples per second.
|
5
|
+
Ganymed processes system and application metrics and events in realtime and can
|
6
|
+
easily handle thousands of samples per second. Ganymed provides these services
|
7
|
+
to administrators and third-party applications:
|
7
8
|
|
8
|
-
*
|
9
|
-
consolidated based on the data source type and emitted to the *
|
9
|
+
* The *Sampler* accepts high frequency samples. these samples are then
|
10
|
+
consolidated based on the data source type and emitted to the *Processor*.
|
10
11
|
|
11
|
-
*
|
12
|
-
|
13
|
-
realtime. All samples are also stored in MongoDB for later analysis.
|
12
|
+
* The *Processor* accepts metrics (events) and stores these metrics into
|
13
|
+
MongoDB. All metrics are also published to interested parties via WebSocket.
|
14
14
|
|
15
|
-
*
|
16
|
-
to the *
|
15
|
+
* The *Collector* is a simple thread that polls various system-level metrics
|
16
|
+
that are not pushed to the *Sampler*/*Processor* from third-party
|
17
|
+
applications.
|
17
18
|
|
18
19
|
## Installation
|
19
20
|
|
data/ganymed.gemspec
CHANGED
@@ -19,11 +19,12 @@ Gem::Specification.new do |s|
|
|
19
19
|
# master
|
20
20
|
s.add_dependency "activesupport", ">= 3.2"
|
21
21
|
s.add_dependency "eventmachine", ">= 0.12.10"
|
22
|
+
s.add_dependency "ganymed-client"
|
22
23
|
s.add_dependency "madvertise-ext", ">= 0.1.2"
|
23
24
|
s.add_dependency "madvertise-logging", ">= 0.3.2"
|
24
25
|
s.add_dependency "mixlib-cli"
|
25
|
-
s.add_dependency "servolux"
|
26
26
|
s.add_dependency "RubyInline"
|
27
|
+
s.add_dependency "servolux"
|
27
28
|
|
28
29
|
# processor
|
29
30
|
s.add_dependency "msgpack"
|
data/lib/ganymed.rb
CHANGED
@@ -1,4 +1,12 @@
|
|
1
|
+
##
|
2
|
+
# Ganymed is an event collection daemon.
|
3
|
+
#
|
4
|
+
# For more information about Ganymed go to the {file:README.md README}
|
5
|
+
#
|
1
6
|
module Ganymed
|
7
|
+
# @private
|
2
8
|
GEM_ROOT = File.expand_path("../..", __FILE__).freeze
|
9
|
+
|
10
|
+
# @private
|
3
11
|
LIB_DIR = File.join(GEM_ROOT, "lib").freeze
|
4
12
|
end
|
data/lib/ganymed/collector.rb
CHANGED
@@ -6,96 +6,155 @@ require 'ganymed/client'
|
|
6
6
|
module Ganymed
|
7
7
|
|
8
8
|
##
|
9
|
-
# The Collector polls various system-level metrics
|
10
|
-
# {Processor}
|
9
|
+
# The Collector is a simple thread that polls various system-level metrics
|
10
|
+
# that are not pushed to the {Sampler}/{Processor} from third-party
|
11
|
+
# applications.
|
12
|
+
#
|
13
|
+
# The Collector features a very simple DSL to implement custom collector
|
14
|
+
# plugins. Simply create a file with a +.rb+ file extension and a +collect+
|
15
|
+
# block:
|
16
|
+
#
|
17
|
+
# collect do
|
18
|
+
# File.open('/proc/foobar').each do |line|
|
19
|
+
# parse(line)
|
20
|
+
# end
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# Copy this plugin to a location of your choice and set
|
24
|
+
# +collector.load_paths+ in your configuration to point to the containing
|
25
|
+
# directory.
|
26
|
+
#
|
27
|
+
# The collector thread will then call this plugin once per resolution tick.
|
28
|
+
# See {file:doc/configuration.md} for details.
|
29
|
+
#
|
30
|
+
# Custom intervals can also be specified for high frequency sampling:
|
31
|
+
#
|
32
|
+
# collect(0.3) do
|
33
|
+
# File.open('/proc/foobar').each do |line|
|
34
|
+
# parse(line)
|
35
|
+
# end
|
36
|
+
# end
|
11
37
|
#
|
12
38
|
class Collector
|
13
39
|
# The configuration object.
|
40
|
+
# @return [Section]
|
14
41
|
attr_reader :config
|
15
42
|
|
16
|
-
# The
|
43
|
+
# The client object.
|
44
|
+
# @return [Ganymed::Client]
|
17
45
|
attr_reader :client
|
18
46
|
|
19
47
|
# Create a new collector instance and initialize all configured collectors.
|
20
48
|
#
|
21
|
-
# @param [
|
49
|
+
# @param [Section] config The configuration object.
|
22
50
|
def initialize(config)
|
23
51
|
@config = config
|
24
|
-
@client = Client.new(
|
25
|
-
:sampler => @config.client.sampler)
|
26
|
-
|
52
|
+
@client = Client.new(@config.client)
|
27
53
|
load_collectors
|
28
|
-
@config.collectors.each do |collector|
|
29
|
-
log.info("initializing collector #{collector.klass.demodulize}")
|
30
|
-
collector.klass.constantize.new(collector, self).run
|
31
|
-
end
|
32
54
|
end
|
33
55
|
|
56
|
+
# @private
|
34
57
|
def load_collectors
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
end
|
58
|
+
collectors.each do |file|
|
59
|
+
name = File.basename(file, '.rb')
|
60
|
+
config = @config.collectors[name.to_sym] || Section.new
|
61
|
+
config[:resolution] = @config.resolution
|
40
62
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
require f
|
63
|
+
log.debug("loading collector #{name} from #{file}")
|
64
|
+
Plugin.new(config, @client).from_file(file).tap do |collector|
|
65
|
+
log.info("collecting #{name} metrics every #{collector.interval} seconds")
|
66
|
+
collector.run
|
46
67
|
end
|
47
68
|
end
|
48
69
|
end
|
49
70
|
|
71
|
+
# @private
|
72
|
+
def collectors
|
73
|
+
load_paths = [@config.collector.load_paths.tap{}].flatten.compact
|
74
|
+
load_paths << File.join(Ganymed::LIB_DIR, 'ganymed/collectors')
|
75
|
+
|
76
|
+
[].tap do |files|
|
77
|
+
load_paths.each do |load_path|
|
78
|
+
log.debug("loading collectors from #{load_path}")
|
79
|
+
Dir[File.join(load_path, '*.rb')].each do |file|
|
80
|
+
files << file
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end.sort.uniq
|
84
|
+
end
|
85
|
+
|
50
86
|
##
|
51
|
-
#
|
87
|
+
# Base class and DSL for {Collector} plugins.
|
52
88
|
#
|
53
|
-
class
|
89
|
+
class Plugin
|
90
|
+
|
91
|
+
# The configuration object.
|
92
|
+
# @return [Section]
|
93
|
+
attr_accessor :config
|
94
|
+
|
95
|
+
# Plugin interval
|
96
|
+
# @return [Fixnum,Float]
|
97
|
+
attr_accessor :interval
|
54
98
|
|
55
|
-
#
|
99
|
+
# Processor socket.
|
100
|
+
# @return [Ganymed::Client::ProcessorSocket]
|
101
|
+
attr_accessor :processor
|
102
|
+
|
103
|
+
# Sampler socket.
|
104
|
+
# @return [Ganymed::Client::SamplerSocket]
|
105
|
+
attr_accessor :sampler
|
106
|
+
|
107
|
+
# Create a new plugin instance.
|
108
|
+
#
|
109
|
+
# @param [Section] config The configuration object.
|
110
|
+
# @param [Ganymed::Client] client A client instance.
|
111
|
+
def initialize(config, client)
|
112
|
+
@config, @client = config, client
|
113
|
+
@processor = @client.processor
|
114
|
+
@sampler = @client.sampler
|
115
|
+
end
|
116
|
+
|
117
|
+
# Set the block used to collect metrics with this plugin.
|
56
118
|
#
|
57
|
-
# @param [
|
58
|
-
# @
|
59
|
-
def
|
60
|
-
@
|
61
|
-
@
|
62
|
-
@
|
119
|
+
# @param [Fixnum,Float] interval Custom plugin interval.
|
120
|
+
# @return [void]
|
121
|
+
def collect(interval=nil, &block)
|
122
|
+
@interval ||= interval
|
123
|
+
@interval ||= @config.interval.tap{}
|
124
|
+
@interval ||= @config.resolution
|
125
|
+
@collector = Proc.new(&block)
|
63
126
|
end
|
64
127
|
|
65
128
|
# Start the EventMachine timer to collect metrics at the specified
|
66
129
|
# interval.
|
130
|
+
# @return [void]
|
67
131
|
def run
|
68
|
-
|
69
|
-
|
70
|
-
collect!
|
71
|
-
rescue Exception => e
|
72
|
-
log.exception(e)
|
73
|
-
end
|
132
|
+
EM.add_periodic_timer(@interval) do
|
133
|
+
EM.defer { collect! }
|
74
134
|
end
|
75
135
|
end
|
76
136
|
|
77
137
|
# @private
|
78
|
-
def
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
else
|
84
|
-
yield
|
138
|
+
def collect!
|
139
|
+
begin
|
140
|
+
@collector.call if @collector.is_a?(Proc)
|
141
|
+
rescue Exception => exc
|
142
|
+
log.exception(exc)
|
85
143
|
end
|
86
144
|
end
|
87
145
|
|
88
|
-
#
|
89
|
-
#
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
146
|
+
# Loads a given ruby file, and runs instance_eval against it in the
|
147
|
+
# context of the current object.
|
148
|
+
#
|
149
|
+
# @param [String] filename The absolute path to a plugin file.
|
150
|
+
# @return [Plugin] The instance for easy method chaining.
|
151
|
+
def from_file(filename)
|
152
|
+
if File.exists?(filename) && File.readable?(filename)
|
153
|
+
self.instance_eval(IO.read(filename), filename, 1)
|
154
|
+
else
|
155
|
+
raise IOError, "Cannot open or read #{filename}!"
|
156
|
+
end
|
157
|
+
self
|
99
158
|
end
|
100
159
|
end
|
101
160
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
collect(0.3) do
|
2
|
+
return if not File.readable?('/proc/stat')
|
3
|
+
|
4
|
+
File.open('/proc/stat').each do |line|
|
5
|
+
next if not line =~ /^cpu /
|
6
|
+
|
7
|
+
cpu = line.chomp.split[1,7].map do |x|
|
8
|
+
x.to_i / 100
|
9
|
+
end
|
10
|
+
|
11
|
+
sampler.emit(:derive, "os.cpu.user", cpu[0])
|
12
|
+
sampler.emit(:derive, "os.cpu.nice", cpu[1])
|
13
|
+
sampler.emit(:derive, "os.cpu.system", cpu[2])
|
14
|
+
sampler.emit(:derive, "os.cpu.idle", cpu[3])
|
15
|
+
sampler.emit(:derive, "os.cpu.iowait", cpu[4])
|
16
|
+
sampler.emit(:derive, "os.cpu.irq", cpu[5])
|
17
|
+
sampler.emit(:derive, "os.cpu.softirq", cpu[6])
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'sys/filesystem'
|
2
|
+
|
3
|
+
collect do
|
4
|
+
Sys::Filesystem.mounts do |mount|
|
5
|
+
st = Sys::Filesystem.stat(mount.mount_point)
|
6
|
+
next if st.blocks == 0 or st.files == 0
|
7
|
+
|
8
|
+
case mp = mount.mount_point
|
9
|
+
when '/'
|
10
|
+
'rootfs'
|
11
|
+
when /^\/.+$/
|
12
|
+
mp[1..-1].tr('^[a-z][0-9]', '-')
|
13
|
+
else
|
14
|
+
'unknown'
|
15
|
+
end.tap do |name|
|
16
|
+
next if config.exclude.map {|e| Regexp.new(e).match(name)}.any?
|
17
|
+
|
18
|
+
block_pc = 1.0 - (st.blocks_free.to_f / st.blocks.to_f)
|
19
|
+
processor.event("os.disk.#{name}.blocks", block_pc)
|
20
|
+
|
21
|
+
files_pc = 1.0 - (st.files_free.to_f / st.files.to_f)
|
22
|
+
processor.event("os.disk.#{name}.files", block_pc)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
Struct.new("IOStat",
|
2
|
+
:major, :minor, :dev,
|
3
|
+
:rio, :rmerge, :rsect, :ruse,
|
4
|
+
:wio, :wmerge, :wsect, :wuse,
|
5
|
+
:running, :use, :aveq)
|
6
|
+
|
7
|
+
collect(0.3) do
|
8
|
+
return if not File.readable?('/proc/diskstats')
|
9
|
+
File.open('/proc/diskstats').each do |line|
|
10
|
+
ios = Struct::IOStat.new(*line.strip.split(/\s+/))
|
11
|
+
next if config.skip_numbered.tap{} and ios.dev =~ /\d+$/
|
12
|
+
next if config.exclude.map {|e| Regexp.new(e).match(ios.dev)}.any?
|
13
|
+
sampler.emit(:derive, "os.iostat.#{ios.dev}.rsect", ios.rsect)
|
14
|
+
sampler.emit(:derive, "os.iostat.#{ios.dev}.wsect", ios.wsect)
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
collect(5) do
|
2
|
+
return if not File.readable?('/proc/meminfo')
|
3
|
+
|
4
|
+
# calculate app memory from total
|
5
|
+
apps = 0
|
6
|
+
|
7
|
+
File.open('/proc/meminfo').each do |line|
|
8
|
+
key, value, unit = line.chomp.split
|
9
|
+
key.gsub!(/:/, '')
|
10
|
+
value = value.to_i * 1024
|
11
|
+
|
12
|
+
case key
|
13
|
+
when "MemTotal"
|
14
|
+
apps = value
|
15
|
+
when "MemFree"
|
16
|
+
sampler.emit(:gauge, "os.mem.unused", value)
|
17
|
+
apps -= value
|
18
|
+
when "Buffers"
|
19
|
+
sampler.emit(:gauge, "os.mem.buffers", value)
|
20
|
+
apps -= value
|
21
|
+
when "Cached"
|
22
|
+
sampler.emit(:gauge, "os.mem.cache", value)
|
23
|
+
apps -= value
|
24
|
+
when "SwapCached"
|
25
|
+
sampler.emit(:gauge, "os.mem.swap_cache", value)
|
26
|
+
apps -= value
|
27
|
+
when "Slab"
|
28
|
+
sampler.emit(:gauge, "os.mem.slab_cache", value)
|
29
|
+
apps -= value
|
30
|
+
when "PageTables"
|
31
|
+
sampler.emit(:gauge, "os.mem.page_tables", value)
|
32
|
+
apps -= value
|
33
|
+
when "Mapped"
|
34
|
+
sampler.emit(:gauge, "os.mem.mapped", value)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
sampler.emit(:gauge, "os.mem.apps", apps)
|
39
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'ohai'
|
2
|
+
|
3
|
+
collect do
|
4
|
+
Ohai::System.new.tap do |ohai|
|
5
|
+
%w(
|
6
|
+
os
|
7
|
+
kernel
|
8
|
+
hostname
|
9
|
+
keys
|
10
|
+
network
|
11
|
+
platform
|
12
|
+
uptime
|
13
|
+
virtualization
|
14
|
+
).each do |plugin|
|
15
|
+
ohai.require_plugin(plugin)
|
16
|
+
end
|
17
|
+
processor.metadata(ohai.data)
|
18
|
+
|
19
|
+
# ohai doesn't cleanup after itself
|
20
|
+
true while Process.wait(-1, Process::WNOHANG) rescue nil
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
|
2
|
+
Struct.new("NetworkStat", :dev,
|
3
|
+
:rbytes, :rpackets, :rerrs, :rdrop, :rfifo, :rframe, :rcompressed, :rmulticast,
|
4
|
+
:wbytes, :wpackets, :werrs, :wdrop, :wfifo, :wcolls, :wcarrier, :wcompressed)
|
5
|
+
|
6
|
+
collect(0.3) do
|
7
|
+
return if not File.readable?('/proc/net/dev')
|
8
|
+
|
9
|
+
File.open('/proc/net/dev') do |f|
|
10
|
+
f.each do |line|
|
11
|
+
next unless line =~ /:/
|
12
|
+
|
13
|
+
ns = Struct::NetworkStat.new(*line.strip.split(/\s+/))
|
14
|
+
ns.dev.sub!(/:$/, '')
|
15
|
+
|
16
|
+
next if config.exclude.map {|e| Regexp.new(e).match(ns.dev)}.any?
|
17
|
+
|
18
|
+
sampler.emit(:derive, "os.net.#{ns.dev}.rbytes", ns.rbytes)
|
19
|
+
sampler.emit(:derive, "os.net.#{ns.dev}.wbytes", ns.wbytes)
|
20
|
+
sampler.emit(:derive, "os.net.#{ns.dev}.rpackets", ns.rpackets)
|
21
|
+
sampler.emit(:derive, "os.net.#{ns.dev}.wpackets", ns.wpackets)
|
22
|
+
sampler.emit(:derive, "os.net.#{ns.dev}.rerrs", ns.rerrs)
|
23
|
+
sampler.emit(:derive, "os.net.#{ns.dev}.werrs", ns.werrs)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
collect(0.3) do
|
2
|
+
return if not File.readable?('/proc/stat')
|
3
|
+
File.open('/proc/stat').each do |line|
|
4
|
+
key, value = line.chomp.split
|
5
|
+
case key
|
6
|
+
when "ctxt"
|
7
|
+
sampler.emit(:derive, "os.procs.switch", value.to_i)
|
8
|
+
when "processes"
|
9
|
+
sampler.emit(:derive, "os.procs.forks", value.to_i)
|
10
|
+
when "procs_running"
|
11
|
+
sampler.emit(:gauge, "os.procs.running", value.to_i)
|
12
|
+
when "procs_blocked"
|
13
|
+
sampler.emit(:gauge, "os.procs.blocked", value.to_i)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/ganymed/config.yml
CHANGED
@@ -38,22 +38,18 @@ generic:
|
|
38
38
|
port: 1337
|
39
39
|
|
40
40
|
collectors:
|
41
|
-
|
42
|
-
- klass: Ganymed::Collector::Uptime
|
43
|
-
- klass: Ganymed::Collector::Load
|
44
|
-
- klass: Ganymed::Collector::CPU
|
45
|
-
- klass: Ganymed::Collector::Procs
|
46
|
-
- klass: Ganymed::Collector::Network
|
41
|
+
network:
|
47
42
|
exclude:
|
48
43
|
- ^lo$
|
49
44
|
- ^sit
|
50
|
-
|
45
|
+
disk:
|
51
46
|
exclude:
|
52
47
|
- ^dev$
|
53
48
|
- ^dev-shm$
|
54
49
|
- rc-init-d$
|
55
50
|
- ^run$
|
56
51
|
- ^sys-fs-cgroup$
|
57
|
-
|
52
|
+
- ^var-tmp-metro
|
53
|
+
iostat:
|
58
54
|
skip_numbered: true
|
59
55
|
exclude: []
|
data/lib/ganymed/event.rb
CHANGED
@@ -5,11 +5,11 @@ module Ganymed
|
|
5
5
|
#
|
6
6
|
# n:: namespace (e.g. +os.cpu.idle+, +nginx.requests+, etc)
|
7
7
|
# c:: a consolidation function used to build this event from multiple
|
8
|
-
# samples
|
8
|
+
# samples (_optional_)
|
9
9
|
# o:: origin (e.g. +web1.example.com+, +myapplication+)
|
10
10
|
# t:: timestamp as seconds since epoch
|
11
|
-
# r:: resolution of this event (i.e. how often is this event sent to the processor)
|
12
|
-
# v:: the value
|
11
|
+
# r:: resolution of this event (i.e. how often is this event sent to the processor, _optional_)
|
12
|
+
# v:: the event value (any object that can be serialized with BSON and JSON)
|
13
13
|
#
|
14
14
|
class Event
|
15
15
|
# The namespace of this event.
|
@@ -17,7 +17,7 @@ module Ganymed
|
|
17
17
|
attr_accessor :ns
|
18
18
|
|
19
19
|
# The consolidation function used to build this event from multiple
|
20
|
-
# samples.
|
20
|
+
# samples if any.
|
21
21
|
# @return [String]
|
22
22
|
attr_accessor :cf
|
23
23
|
|
@@ -31,6 +31,9 @@ module Ganymed
|
|
31
31
|
|
32
32
|
# The event resolution
|
33
33
|
# @return [Fixnum]
|
34
|
+
#
|
35
|
+
# @note +nil+ or +0+ will bypass range checks for MongoDB, refer to the
|
36
|
+
# {file:doc/configuration.md configuration} for details.
|
34
37
|
attr_accessor :resolution
|
35
38
|
|
36
39
|
# The actual value of this sample.
|
@@ -74,6 +77,8 @@ module Ganymed
|
|
74
77
|
end
|
75
78
|
|
76
79
|
# Validate this event.
|
80
|
+
#
|
81
|
+
# @return [void]
|
77
82
|
def validate
|
78
83
|
unless @ns =~ /^[a-z][a-z0-9_\.-]+[a-z0-9]$/
|
79
84
|
raise "invalid namespace: #{@ns.inspect}"
|
data/lib/ganymed/master.rb
CHANGED
data/lib/ganymed/mongodb.rb
CHANGED
@@ -7,6 +7,7 @@ module Ganymed
|
|
7
7
|
|
8
8
|
def initialize(config)
|
9
9
|
@config = config
|
10
|
+
@collections = {}
|
10
11
|
log.info("using MongoDB at #{config.host}:#{config.port}/#{config.database}")
|
11
12
|
end
|
12
13
|
|
@@ -22,7 +23,7 @@ module Ganymed
|
|
22
23
|
end
|
23
24
|
|
24
25
|
def collection(ns)
|
25
|
-
db[ns].tap do |col|
|
26
|
+
@collections[ns] ||= db[ns].tap do |col|
|
26
27
|
col.ensure_index([['c', ::Mongo::ASCENDING]])
|
27
28
|
col.ensure_index([['o', ::Mongo::ASCENDING]])
|
28
29
|
col.ensure_index([['t', ::Mongo::ASCENDING]])
|
data/lib/ganymed/processor.rb
CHANGED
@@ -1,18 +1,35 @@
|
|
1
1
|
require 'eventmachine'
|
2
2
|
require 'msgpack'
|
3
3
|
|
4
|
+
require 'ganymed/client'
|
4
5
|
require 'ganymed/event'
|
5
6
|
require 'ganymed/mongodb'
|
6
7
|
require 'ganymed/websocket'
|
7
8
|
|
8
9
|
module Ganymed
|
10
|
+
|
11
|
+
##
|
12
|
+
# The Processor accept {Event events} from a UDP socket and stores these
|
13
|
+
# metrics into MongoDB. All metrics are also published to interested parties
|
14
|
+
# via WebSocket.
|
15
|
+
#
|
9
16
|
class Processor
|
10
17
|
def initialize(config)
|
11
18
|
@config = config
|
12
19
|
@db = MongoDB.new(config.processor.mongodb)
|
13
20
|
@websocket = Websocket.new(config.processor.websocket, @db)
|
14
21
|
@client = Client.new(:sampler => @config.client.sampler)
|
15
|
-
listen!
|
22
|
+
cache! and listen!
|
23
|
+
end
|
24
|
+
|
25
|
+
def cache!
|
26
|
+
@metadata = Hash.new do |hsh, key|
|
27
|
+
hsh[key] = []
|
28
|
+
end
|
29
|
+
|
30
|
+
@db['metadata'].find({}, fields: [:_id, :namespaces]).each do |result|
|
31
|
+
@metadata[result['_id']] = result['namespaces']
|
32
|
+
end
|
16
33
|
end
|
17
34
|
|
18
35
|
def listen!
|
@@ -36,20 +53,19 @@ module Ganymed
|
|
36
53
|
end
|
37
54
|
|
38
55
|
def process_event(event)
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
56
|
+
if not @metadata[event.origin].include?(event.ns)
|
57
|
+
# insert origin + namespace into metadata
|
58
|
+
metadata = @db['metadata'].find_and_modify({
|
59
|
+
query: {:_id => event.origin},
|
60
|
+
update: {:$addToSet => {:namespaces => event.ns}},
|
61
|
+
upsert: true,
|
62
|
+
})
|
45
63
|
|
46
|
-
|
47
|
-
|
48
|
-
|
64
|
+
# set defaults in case find_and_modify returned an empty document
|
65
|
+
metadata['_id'] ||= event.origin
|
66
|
+
metadata['namespaces'] ||= [event.ns]
|
49
67
|
|
50
|
-
|
51
|
-
if not metadata['namespaces'].include?(event.ns)
|
52
|
-
metadata['namespaces'] |= [event.ns]
|
68
|
+
# notify websocket clients
|
53
69
|
@websocket.send(:metadata, [metadata])
|
54
70
|
end
|
55
71
|
|
@@ -59,7 +75,7 @@ module Ganymed
|
|
59
75
|
end
|
60
76
|
|
61
77
|
# skip events that are produced below the mongodb storage threshold
|
62
|
-
return if (1
|
78
|
+
return if (1...@config.resolution).include?(event.resolution)
|
63
79
|
|
64
80
|
# insert event into ns collection
|
65
81
|
@db.collection(event.ns).update({
|
data/lib/ganymed/sampler.rb
CHANGED
@@ -1,11 +1,10 @@
|
|
1
1
|
require 'active_support/inflector'
|
2
2
|
require 'eventmachine'
|
3
|
-
require 'madvertise/ext/enumerable'
|
4
|
-
require 'msgpack'
|
5
|
-
require 'socket'
|
6
3
|
require 'inline'
|
4
|
+
require 'madvertise/ext/enumerable'
|
7
5
|
|
8
6
|
require 'ganymed'
|
7
|
+
require 'ganymed/client'
|
9
8
|
|
10
9
|
module Ganymed
|
11
10
|
|
data/lib/ganymed/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ganymed
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-05-
|
12
|
+
date: 2012-05-29 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
16
|
-
requirement: &
|
16
|
+
requirement: &19204520 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '3.2'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *19204520
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: eventmachine
|
27
|
-
requirement: &
|
27
|
+
requirement: &19202420 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,21 @@ dependencies:
|
|
32
32
|
version: 0.12.10
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *19202420
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: ganymed-client
|
38
|
+
requirement: &19227700 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *19227700
|
36
47
|
- !ruby/object:Gem::Dependency
|
37
48
|
name: madvertise-ext
|
38
|
-
requirement: &
|
49
|
+
requirement: &19226300 !ruby/object:Gem::Requirement
|
39
50
|
none: false
|
40
51
|
requirements:
|
41
52
|
- - ! '>='
|
@@ -43,10 +54,10 @@ dependencies:
|
|
43
54
|
version: 0.1.2
|
44
55
|
type: :runtime
|
45
56
|
prerelease: false
|
46
|
-
version_requirements: *
|
57
|
+
version_requirements: *19226300
|
47
58
|
- !ruby/object:Gem::Dependency
|
48
59
|
name: madvertise-logging
|
49
|
-
requirement: &
|
60
|
+
requirement: &19225180 !ruby/object:Gem::Requirement
|
50
61
|
none: false
|
51
62
|
requirements:
|
52
63
|
- - ! '>='
|
@@ -54,10 +65,10 @@ dependencies:
|
|
54
65
|
version: 0.3.2
|
55
66
|
type: :runtime
|
56
67
|
prerelease: false
|
57
|
-
version_requirements: *
|
68
|
+
version_requirements: *19225180
|
58
69
|
- !ruby/object:Gem::Dependency
|
59
70
|
name: mixlib-cli
|
60
|
-
requirement: &
|
71
|
+
requirement: &19224340 !ruby/object:Gem::Requirement
|
61
72
|
none: false
|
62
73
|
requirements:
|
63
74
|
- - ! '>='
|
@@ -65,10 +76,10 @@ dependencies:
|
|
65
76
|
version: '0'
|
66
77
|
type: :runtime
|
67
78
|
prerelease: false
|
68
|
-
version_requirements: *
|
79
|
+
version_requirements: *19224340
|
69
80
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
71
|
-
requirement: &
|
81
|
+
name: RubyInline
|
82
|
+
requirement: &19223140 !ruby/object:Gem::Requirement
|
72
83
|
none: false
|
73
84
|
requirements:
|
74
85
|
- - ! '>='
|
@@ -76,10 +87,10 @@ dependencies:
|
|
76
87
|
version: '0'
|
77
88
|
type: :runtime
|
78
89
|
prerelease: false
|
79
|
-
version_requirements: *
|
90
|
+
version_requirements: *19223140
|
80
91
|
- !ruby/object:Gem::Dependency
|
81
|
-
name:
|
82
|
-
requirement: &
|
92
|
+
name: servolux
|
93
|
+
requirement: &19220580 !ruby/object:Gem::Requirement
|
83
94
|
none: false
|
84
95
|
requirements:
|
85
96
|
- - ! '>='
|
@@ -87,10 +98,10 @@ dependencies:
|
|
87
98
|
version: '0'
|
88
99
|
type: :runtime
|
89
100
|
prerelease: false
|
90
|
-
version_requirements: *
|
101
|
+
version_requirements: *19220580
|
91
102
|
- !ruby/object:Gem::Dependency
|
92
103
|
name: msgpack
|
93
|
-
requirement: &
|
104
|
+
requirement: &19234260 !ruby/object:Gem::Requirement
|
94
105
|
none: false
|
95
106
|
requirements:
|
96
107
|
- - ! '>='
|
@@ -98,10 +109,10 @@ dependencies:
|
|
98
109
|
version: '0'
|
99
110
|
type: :runtime
|
100
111
|
prerelease: false
|
101
|
-
version_requirements: *
|
112
|
+
version_requirements: *19234260
|
102
113
|
- !ruby/object:Gem::Dependency
|
103
114
|
name: mongo
|
104
|
-
requirement: &
|
115
|
+
requirement: &19231900 !ruby/object:Gem::Requirement
|
105
116
|
none: false
|
106
117
|
requirements:
|
107
118
|
- - ! '>='
|
@@ -109,10 +120,10 @@ dependencies:
|
|
109
120
|
version: '1.6'
|
110
121
|
type: :runtime
|
111
122
|
prerelease: false
|
112
|
-
version_requirements: *
|
123
|
+
version_requirements: *19231900
|
113
124
|
- !ruby/object:Gem::Dependency
|
114
125
|
name: em-websocket
|
115
|
-
requirement: &
|
126
|
+
requirement: &19229040 !ruby/object:Gem::Requirement
|
116
127
|
none: false
|
117
128
|
requirements:
|
118
129
|
- - ! '>='
|
@@ -120,10 +131,10 @@ dependencies:
|
|
120
131
|
version: '0'
|
121
132
|
type: :runtime
|
122
133
|
prerelease: false
|
123
|
-
version_requirements: *
|
134
|
+
version_requirements: *19229040
|
124
135
|
- !ruby/object:Gem::Dependency
|
125
136
|
name: yajl-ruby
|
126
|
-
requirement: &
|
137
|
+
requirement: &19257780 !ruby/object:Gem::Requirement
|
127
138
|
none: false
|
128
139
|
requirements:
|
129
140
|
- - ! '>='
|
@@ -131,10 +142,10 @@ dependencies:
|
|
131
142
|
version: '0'
|
132
143
|
type: :runtime
|
133
144
|
prerelease: false
|
134
|
-
version_requirements: *
|
145
|
+
version_requirements: *19257780
|
135
146
|
- !ruby/object:Gem::Dependency
|
136
147
|
name: sys-filesystem
|
137
|
-
requirement: &
|
148
|
+
requirement: &19255820 !ruby/object:Gem::Requirement
|
138
149
|
none: false
|
139
150
|
requirements:
|
140
151
|
- - ! '>='
|
@@ -142,10 +153,10 @@ dependencies:
|
|
142
153
|
version: '0'
|
143
154
|
type: :runtime
|
144
155
|
prerelease: false
|
145
|
-
version_requirements: *
|
156
|
+
version_requirements: *19255820
|
146
157
|
- !ruby/object:Gem::Dependency
|
147
158
|
name: ohai
|
148
|
-
requirement: &
|
159
|
+
requirement: &19252280 !ruby/object:Gem::Requirement
|
149
160
|
none: false
|
150
161
|
requirements:
|
151
162
|
- - ! '>='
|
@@ -153,7 +164,7 @@ dependencies:
|
|
153
164
|
version: 0.6.12
|
154
165
|
type: :runtime
|
155
166
|
prerelease: false
|
156
|
-
version_requirements: *
|
167
|
+
version_requirements: *19252280
|
157
168
|
description: Ganymed is an event collection daemon
|
158
169
|
email:
|
159
170
|
- bb@xnull.de
|
@@ -175,16 +186,16 @@ files:
|
|
175
186
|
- contrib/cpuhog.c
|
176
187
|
- ganymed.gemspec
|
177
188
|
- lib/ganymed.rb
|
178
|
-
- lib/ganymed/client.rb
|
179
189
|
- lib/ganymed/collector.rb
|
180
|
-
- lib/ganymed/
|
181
|
-
- lib/ganymed/
|
182
|
-
- lib/ganymed/
|
183
|
-
- lib/ganymed/
|
184
|
-
- lib/ganymed/
|
185
|
-
- lib/ganymed/
|
186
|
-
- lib/ganymed/
|
187
|
-
- lib/ganymed/
|
190
|
+
- lib/ganymed/collectors/cpu.rb
|
191
|
+
- lib/ganymed/collectors/disk.rb
|
192
|
+
- lib/ganymed/collectors/iostat.rb
|
193
|
+
- lib/ganymed/collectors/load.rb
|
194
|
+
- lib/ganymed/collectors/memory.rb
|
195
|
+
- lib/ganymed/collectors/metadata.rb
|
196
|
+
- lib/ganymed/collectors/network.rb
|
197
|
+
- lib/ganymed/collectors/process.rb
|
198
|
+
- lib/ganymed/collectors/uptime.rb
|
188
199
|
- lib/ganymed/config.yml
|
189
200
|
- lib/ganymed/event.rb
|
190
201
|
- lib/ganymed/master.rb
|
@@ -228,7 +239,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
228
239
|
version: '0'
|
229
240
|
segments:
|
230
241
|
- 0
|
231
|
-
hash: -
|
242
|
+
hash: -4122495108718523153
|
232
243
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
233
244
|
none: false
|
234
245
|
requirements:
|
@@ -237,7 +248,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
237
248
|
version: '0'
|
238
249
|
segments:
|
239
250
|
- 0
|
240
|
-
hash: -
|
251
|
+
hash: -4122495108718523153
|
241
252
|
requirements: []
|
242
253
|
rubyforge_project:
|
243
254
|
rubygems_version: 1.8.17
|
data/lib/ganymed/client.rb
DELETED
@@ -1,132 +0,0 @@
|
|
1
|
-
require 'active_support/core_ext/array/extract_options'
|
2
|
-
require 'socket'
|
3
|
-
require 'msgpack'
|
4
|
-
|
5
|
-
module Ganymed
|
6
|
-
|
7
|
-
##
|
8
|
-
# The Client connects to the {Sampler} and/or {Processor} and emits samples
|
9
|
-
# and/or {Event events} respectively.
|
10
|
-
#
|
11
|
-
class Client
|
12
|
-
|
13
|
-
# The {Processor} {ProcessorSocket socket}.
|
14
|
-
attr_reader :processor
|
15
|
-
|
16
|
-
# The {Sampler} {SamplerSocket socket}.
|
17
|
-
attr_reader :sampler
|
18
|
-
|
19
|
-
# Create a new client instance.
|
20
|
-
#
|
21
|
-
# @param [Hash] opts Client options.
|
22
|
-
# @option opts [Hash] :processor Options passed to the {ProcessorSocket}.
|
23
|
-
# @option opts [Hash] :sampler Options passed to the {SamplerSocket}.
|
24
|
-
def initialize(opts)
|
25
|
-
@processor = ProcessorSocket.new(opts[:processor]) if opts[:processor]
|
26
|
-
@sampler = SamplerSocket.new(opts[:sampler]) if opts[:sampler]
|
27
|
-
end
|
28
|
-
|
29
|
-
##
|
30
|
-
# A simple UDPSocket wrapper.
|
31
|
-
#
|
32
|
-
class Socket
|
33
|
-
|
34
|
-
# Create a new Socket instance.
|
35
|
-
#
|
36
|
-
# @param [Hash] opts Socket options.
|
37
|
-
# @option opts [String] :host Host to connect to.
|
38
|
-
# @option opts [Fixnum] :port Port to connect to.
|
39
|
-
# @option opts [String] :origin Origin of Samples/Events. Defaults to the
|
40
|
-
# fully-qualified hostname.
|
41
|
-
def initialize(opts)
|
42
|
-
@host, @port = opts[:host], opts[:port]
|
43
|
-
@socket = UDPSocket.new
|
44
|
-
@origin = opts[:origin] || ::Socket.gethostbyname(::Socket.gethostname).first
|
45
|
-
end
|
46
|
-
|
47
|
-
# Send data to the socket.
|
48
|
-
#
|
49
|
-
# @param [String] data The data to send.
|
50
|
-
# @param [Fixnum] flags Socket flags.
|
51
|
-
def send(data, flags=0)
|
52
|
-
@socket.send(data, flags, @host, @port)
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
##
|
57
|
-
# A {Socket} that emits samples to a {Sampler}.
|
58
|
-
#
|
59
|
-
class SamplerSocket < Socket
|
60
|
-
|
61
|
-
# Emit a new sample.
|
62
|
-
#
|
63
|
-
# @param [String, Symbol] ds Sample data source.
|
64
|
-
# @param [String] ns {Event} namespace.
|
65
|
-
# @param [Fixnum, Float] value Sample value.
|
66
|
-
# @param [Time] now Sample timestamp.
|
67
|
-
def emit(ds, ns, value, now=Time.now.utc)
|
68
|
-
data = [ds.to_s, ns, @origin, now.to_f, value.to_f]
|
69
|
-
send(data.pack("Z*Z*Z*GG"))
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
##
|
74
|
-
# A {Socket} that emits {Event events} to a {Processor}.
|
75
|
-
#
|
76
|
-
class ProcessorSocket < Socket
|
77
|
-
|
78
|
-
# Emit a new {Event}.
|
79
|
-
#
|
80
|
-
# @param [String] ns {Event} namespace.
|
81
|
-
# @param [Object] value {Event} value.
|
82
|
-
# @param [Hash] opts Options
|
83
|
-
# @option opts [String] cf Consolidation function used in this event.
|
84
|
-
# @option opts [Time] now {Event} timestamp.
|
85
|
-
# @option opts [Fixnum] resolution {Event} resolution.
|
86
|
-
# @option opts [String] origin {Event} origin.
|
87
|
-
def event(ns, value, opts={})
|
88
|
-
opts = {
|
89
|
-
:cf => nil,
|
90
|
-
:now => Time.now.utc,
|
91
|
-
:resolution => 0,
|
92
|
-
:origin => @origin,
|
93
|
-
}.merge(opts)
|
94
|
-
|
95
|
-
{
|
96
|
-
:_type => :event,
|
97
|
-
:n => ns,
|
98
|
-
:c => opts[:cf],
|
99
|
-
:o => opts[:origin],
|
100
|
-
:t => opts[:now].to_i,
|
101
|
-
:r => opts[:resolution].to_i,
|
102
|
-
:v => value
|
103
|
-
}.tap do |data|
|
104
|
-
send(data.to_msgpack)
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
# Emit a metadata object to the {Processor}.
|
109
|
-
#
|
110
|
-
# A metadata object contains arbitrary data related to the origin. This
|
111
|
-
# can be queried by {Websocket} clients for displaying information about
|
112
|
-
# origins.
|
113
|
-
#
|
114
|
-
# @param [Hash] data Metadata object.
|
115
|
-
# @param [Hash] opts Options
|
116
|
-
# @option opts [String] origin Metadata origin. Defaults to the FQDN.
|
117
|
-
def metadata(data, opts={})
|
118
|
-
opts = {
|
119
|
-
:origin => @origin,
|
120
|
-
}.merge(opts)
|
121
|
-
|
122
|
-
{
|
123
|
-
:_type => :metadata,
|
124
|
-
:origin => opts[:origin],
|
125
|
-
:data => data,
|
126
|
-
}.tap do |data|
|
127
|
-
send(data.to_msgpack)
|
128
|
-
end
|
129
|
-
end
|
130
|
-
end
|
131
|
-
end
|
132
|
-
end
|
@@ -1,32 +0,0 @@
|
|
1
|
-
require 'ganymed/collector'
|
2
|
-
|
3
|
-
module Ganymed
|
4
|
-
class Collector
|
5
|
-
class CPU < Base
|
6
|
-
def collect!
|
7
|
-
return if not File.readable?('/proc/stat')
|
8
|
-
File.open('/proc/stat').each do |line|
|
9
|
-
next if not line =~ /^cpu /
|
10
|
-
cpu = line.chomp.split[1,7].map do |x|
|
11
|
-
x.to_i / hz
|
12
|
-
end
|
13
|
-
@sampler.emit(:derive, "os.cpu.user", cpu[0])
|
14
|
-
@sampler.emit(:derive, "os.cpu.nice", cpu[1])
|
15
|
-
@sampler.emit(:derive, "os.cpu.system", cpu[2])
|
16
|
-
@sampler.emit(:derive, "os.cpu.idle", cpu[3])
|
17
|
-
@sampler.emit(:derive, "os.cpu.iowait", cpu[4])
|
18
|
-
@sampler.emit(:derive, "os.cpu.irq", cpu[5])
|
19
|
-
@sampler.emit(:derive, "os.cpu.softirq", cpu[6])
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def interval
|
24
|
-
@config.interval.tap{} or 0.2
|
25
|
-
end
|
26
|
-
|
27
|
-
def hz
|
28
|
-
@config.hz.tap{} or 100
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
@@ -1,36 +0,0 @@
|
|
1
|
-
require 'sys/filesystem'
|
2
|
-
|
3
|
-
module Ganymed
|
4
|
-
class Collector
|
5
|
-
class Disk < Base
|
6
|
-
def collect!
|
7
|
-
Sys::Filesystem.mounts do |mount|
|
8
|
-
st = Sys::Filesystem.stat(mount.mount_point)
|
9
|
-
next if st.blocks == 0 or st.files == 0
|
10
|
-
|
11
|
-
name = name(mount.mount_point)
|
12
|
-
next if @config.exclude.map {|e| Regexp.new(e).match(name)}.any?
|
13
|
-
|
14
|
-
block_pc = 1.0 - (st.blocks_free.to_f / st.blocks.to_f)
|
15
|
-
@processor.event("os.disk.#{name}.blocks", block_pc)
|
16
|
-
|
17
|
-
files_pc = 1.0 - (st.files_free.to_f / st.files.to_f)
|
18
|
-
@processor.event("os.disk.#{name}.files", block_pc)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
private
|
23
|
-
|
24
|
-
def name(mount)
|
25
|
-
case mount
|
26
|
-
when '/'
|
27
|
-
'rootfs'
|
28
|
-
when /^\/.+$/
|
29
|
-
mount[1..-1].tr('^[a-z][0-9]', '-')
|
30
|
-
else
|
31
|
-
'unknown'
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
@@ -1,26 +0,0 @@
|
|
1
|
-
Struct.new("IOStat",
|
2
|
-
:major, :minor, :dev,
|
3
|
-
:rio, :rmerge, :rsect, :ruse,
|
4
|
-
:wio, :wmerge, :wsect, :wuse,
|
5
|
-
:running, :use, :aveq)
|
6
|
-
|
7
|
-
module Ganymed
|
8
|
-
class Collector
|
9
|
-
class IOStat < Base
|
10
|
-
def collect!
|
11
|
-
return if not File.readable?('/proc/diskstats')
|
12
|
-
File.open('/proc/diskstats').each do |line|
|
13
|
-
ios = Struct::IOStat.new(*line.strip.split(/\s+/))
|
14
|
-
next if @config.skip_numbered.tap{} and ios.dev =~ /\d+$/
|
15
|
-
next if @config.exclude.map {|e| Regexp.new(e).match(ios.dev)}.any?
|
16
|
-
@sampler.emit(:derive, "os.iostat.#{ios.dev}.rsect", ios.rsect)
|
17
|
-
@sampler.emit(:derive, "os.iostat.#{ios.dev}.wsect", ios.wsect)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
def interval
|
22
|
-
@config.interval.tap{} or 0.2
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
require 'ganymed/collector'
|
2
|
-
|
3
|
-
module Ganymed
|
4
|
-
class Collector
|
5
|
-
class Load < Base
|
6
|
-
def collect!
|
7
|
-
return if not File.readable?('/proc/loadavg')
|
8
|
-
File.open('/proc/loadavg') do |f|
|
9
|
-
loadavg = f.read.chomp.split[0,3].map(&:to_f)
|
10
|
-
@sampler.emit(:gauge, "os.loadavg", loadavg[0])
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
def interval
|
15
|
-
@config.interval.tap{} or 0.2
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
@@ -1,34 +0,0 @@
|
|
1
|
-
require 'ohai'
|
2
|
-
|
3
|
-
require 'ganymed/collector'
|
4
|
-
|
5
|
-
module Ganymed
|
6
|
-
class Collector
|
7
|
-
class Metadata < Base
|
8
|
-
def collect!
|
9
|
-
Ohai::System.new.tap do |ohai|
|
10
|
-
%w(
|
11
|
-
os
|
12
|
-
kernel
|
13
|
-
hostname
|
14
|
-
keys
|
15
|
-
network
|
16
|
-
platform
|
17
|
-
uptime
|
18
|
-
virtualization
|
19
|
-
).each do |plugin|
|
20
|
-
ohai.require_plugin(plugin)
|
21
|
-
end
|
22
|
-
@processor.metadata(ohai.data)
|
23
|
-
|
24
|
-
# ohai doesn't cleanup after itself
|
25
|
-
true while Process.wait(-1, Process::WNOHANG) rescue nil
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
def interval
|
30
|
-
nil
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
@@ -1,36 +0,0 @@
|
|
1
|
-
# bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
|
2
|
-
Struct.new("NetworkStat", :dev,
|
3
|
-
:rbytes, :rpackets, :rerrs, :rdrop, :rfifo, :rframe, :rcompressed, :rmulticast,
|
4
|
-
:wbytes, :wpackets, :werrs, :wdrop, :wfifo, :wcolls, :wcarrier, :wcompressed)
|
5
|
-
|
6
|
-
module Ganymed
|
7
|
-
class Collector
|
8
|
-
class Network < Base
|
9
|
-
def collect!
|
10
|
-
return if not File.readable?('/proc/net/dev')
|
11
|
-
|
12
|
-
File.open('/proc/net/dev') do |f|
|
13
|
-
f.each do |line|
|
14
|
-
next unless line =~ /:/
|
15
|
-
|
16
|
-
ns = Struct::NetworkStat.new(*line.strip.split(/\s+/))
|
17
|
-
ns.dev.sub!(/:$/, '')
|
18
|
-
|
19
|
-
next if @config.exclude.map {|e| Regexp.new(e).match(ns.dev)}.any?
|
20
|
-
|
21
|
-
@sampler.emit(:derive, "os.net.#{ns.dev}.rbytes", ns.rbytes)
|
22
|
-
@sampler.emit(:derive, "os.net.#{ns.dev}.wbytes", ns.wbytes)
|
23
|
-
@sampler.emit(:derive, "os.net.#{ns.dev}.rpackets", ns.rpackets)
|
24
|
-
@sampler.emit(:derive, "os.net.#{ns.dev}.wpackets", ns.wpackets)
|
25
|
-
@sampler.emit(:derive, "os.net.#{ns.dev}.rerrs", ns.rerrs)
|
26
|
-
@sampler.emit(:derive, "os.net.#{ns.dev}.werrs", ns.werrs)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def interval
|
32
|
-
@config.interval.tap{} or 0.1
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
@@ -1,22 +0,0 @@
|
|
1
|
-
module Ganymed
|
2
|
-
class Collector
|
3
|
-
class Procs < Base
|
4
|
-
def collect!
|
5
|
-
return if not File.readable?('/proc/stat')
|
6
|
-
File.open('/proc/stat').each do |line|
|
7
|
-
next if not line =~ /^procs_/
|
8
|
-
key, value = line.chomp.split
|
9
|
-
if key == "procs_running"
|
10
|
-
@sampler.emit(:gauge, "os.procs.running", value.to_i)
|
11
|
-
elsif key == "procs_blocked"
|
12
|
-
@sampler.emit(:gauge, "os.procs.blocked", value.to_i)
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
def interval
|
18
|
-
@config.interval.tap{} or 0.2
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
@@ -1,16 +0,0 @@
|
|
1
|
-
require 'ganymed/collector'
|
2
|
-
|
3
|
-
module Ganymed
|
4
|
-
class Collector
|
5
|
-
class Uptime < Base
|
6
|
-
def collect!
|
7
|
-
return if not File.readable?('/proc/stat')
|
8
|
-
File.open('/proc/stat').each do |line|
|
9
|
-
next if not line =~ /^btime /
|
10
|
-
boottime = Time.at(line.chomp.split[1].to_i)
|
11
|
-
@processor.event("os.reboot", 1, :now => boottime)
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|