ganymed 0.1.2 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|