ganymed 0.3.4 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +1 -2
- data/README.md +6 -14
- data/ganymed.gemspec +4 -17
- data/lib/ganymed/collector.rb +6 -30
- data/lib/ganymed/collectors/cpu.rb +8 -8
- data/lib/ganymed/collectors/disk.rb +2 -2
- data/lib/ganymed/collectors/iostat.rb +2 -2
- data/lib/ganymed/collectors/load.rb +1 -1
- data/lib/ganymed/collectors/memory.rb +8 -8
- data/lib/ganymed/collectors/network.rb +6 -6
- data/lib/ganymed/collectors/process.rb +4 -4
- data/lib/ganymed/config.yml +0 -32
- data/lib/ganymed/master.rb +9 -8
- data/lib/ganymed/version.rb +1 -1
- metadata +8 -152
- data/contrib/cpuhog +0 -0
- data/contrib/cpuhog.c +0 -21
- data/lib/ganymed/collectors/uptime.rb +0 -10
- data/lib/ganymed/console.rb +0 -147
- data/lib/ganymed/event.rb +0 -97
- data/lib/ganymed/ext/array.rb +0 -38
- data/lib/ganymed/mongodb.rb +0 -44
- data/lib/ganymed/processor.rb +0 -137
- data/lib/ganymed/sampler/counter.rb +0 -19
- data/lib/ganymed/sampler/datasource.rb +0 -85
- data/lib/ganymed/sampler/derive.rb +0 -27
- data/lib/ganymed/sampler/gauge.rb +0 -20
- data/lib/ganymed/sampler.rb +0 -94
- data/lib/ganymed/websocket/authentication.rb +0 -37
- data/lib/ganymed/websocket/connection.rb +0 -71
- data/lib/ganymed/websocket/filter.rb +0 -23
- data/lib/ganymed/websocket/metadata.rb +0 -21
- data/lib/ganymed/websocket/query.rb +0 -30
- data/lib/ganymed/websocket/subscribe.rb +0 -45
- data/lib/ganymed/websocket.rb +0 -45
- data/spec/sampler/counter_spec.rb +0 -11
- data/spec/sampler/datasource_examples.rb +0 -49
- data/spec/sampler/datasource_spec.rb +0 -23
- data/spec/sampler/derive_spec.rb +0 -34
- data/spec/sampler/gauge_spec.rb +0 -35
- data/spec/sampler_spec.rb +0 -5
data/lib/ganymed/sampler.rb
DELETED
@@ -1,94 +0,0 @@
|
|
1
|
-
require 'active_support/inflector'
|
2
|
-
require 'eventmachine'
|
3
|
-
|
4
|
-
require 'ganymed'
|
5
|
-
require 'ganymed/client'
|
6
|
-
require 'ganymed/ext/array'
|
7
|
-
|
8
|
-
module Ganymed
|
9
|
-
|
10
|
-
##
|
11
|
-
# The Sampler processes samples from a UDP socket, stores these samples into
|
12
|
-
# memory and flushes a possibly interpolated and consolidated value to the
|
13
|
-
# {Processor}.
|
14
|
-
#
|
15
|
-
class Sampler
|
16
|
-
def initialize(config)
|
17
|
-
@config = config
|
18
|
-
listen!
|
19
|
-
emit!
|
20
|
-
end
|
21
|
-
|
22
|
-
def listen!
|
23
|
-
@config.sampler.listen.tap do |listen|
|
24
|
-
log.info("processing samples on udp##{listen.host}:#{listen.port}")
|
25
|
-
EM.open_datagram_socket(listen.host, listen.port, Connection) do |connection|
|
26
|
-
connection.fqdn = @config.fqdn
|
27
|
-
connection.datasources = datasources
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
def emit!
|
33
|
-
@config.client.processor.tap do |processor|
|
34
|
-
log.info("emitting consolidated samples to tcp##{processor.host}:#{processor.port}")
|
35
|
-
@processor = Ganymed::Client::Processor.connect(processor.host, processor.port)
|
36
|
-
end
|
37
|
-
|
38
|
-
# emit consolidated samples 5 times per window
|
39
|
-
interval = [1, @config.sampler.window / 5].max
|
40
|
-
EM.add_periodic_timer(interval) { flush }
|
41
|
-
end
|
42
|
-
|
43
|
-
def datasources
|
44
|
-
@datasources ||= Hash.new do |hsh, key|
|
45
|
-
klass = "Ganymed::Sampler::#{key.classify}".constantize
|
46
|
-
hsh[key] = klass.new(@config.sampler.window)
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
def flush
|
51
|
-
datasources.each do |_, datasource|
|
52
|
-
datasource.flush do |ns, origin, values|
|
53
|
-
next if values.empty?
|
54
|
-
emit(ns, origin, values)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
def emit(ns, origin, values)
|
60
|
-
@config.sampler.consolidations.each do |cf, args|
|
61
|
-
begin
|
62
|
-
@processor.event(ns, values.send(*args), {
|
63
|
-
:cf => cf,
|
64
|
-
:origin => origin,
|
65
|
-
:resolution => @config.sampler.window,
|
66
|
-
})
|
67
|
-
rescue Exception => exc
|
68
|
-
log.warn("failed to emit #{ns}@#{origin}")
|
69
|
-
log.exception(exc)
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
# @private
|
75
|
-
module Connection
|
76
|
-
attr_accessor :datasources, :fqdn
|
77
|
-
|
78
|
-
def receive_data(data)
|
79
|
-
begin
|
80
|
-
# pack format: <datasource><namespace><origin><value>
|
81
|
-
data = data.unpack("Z*Z*Z*G")
|
82
|
-
datasources[data.slice!(0)].feed(*data)
|
83
|
-
datasources['counter'].feed('ganymed.sampler.samples', fqdn, 1)
|
84
|
-
rescue Exception => exc
|
85
|
-
log.exception(exc)
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
Dir[File.join(Ganymed::LIB_DIR, 'ganymed/sampler/*.rb')].each do |f|
|
93
|
-
require f
|
94
|
-
end
|
@@ -1,37 +0,0 @@
|
|
1
|
-
require 'digest'
|
2
|
-
|
3
|
-
require 'ganymed/websocket'
|
4
|
-
|
5
|
-
module Ganymed
|
6
|
-
class Websocket
|
7
|
-
module Authentication
|
8
|
-
def self.included(base)
|
9
|
-
base.command :authenticate
|
10
|
-
end
|
11
|
-
|
12
|
-
def authenticate(data)
|
13
|
-
@authenticated = db['users'].find({
|
14
|
-
:_id => data['username'],
|
15
|
-
:password => Digest::SHA256.hexdigest(data['password'])
|
16
|
-
}).count > 0
|
17
|
-
|
18
|
-
if authenticated?
|
19
|
-
log.info("successful auth for #{data['username']} from #{peer}")
|
20
|
-
send(:state, {authenticated: true})
|
21
|
-
metadata # trigger metadata push
|
22
|
-
else
|
23
|
-
log.warn("bad auth for #{data['username']} from #{peer}")
|
24
|
-
send(:state, {authenticated: false})
|
25
|
-
end
|
26
|
-
rescue Exception => exc
|
27
|
-
log.warn("failed to authenticate #{peer}")
|
28
|
-
log.exception(exc)
|
29
|
-
send(:state, {authenticated: false})
|
30
|
-
end
|
31
|
-
|
32
|
-
def authenticated?
|
33
|
-
@authenticated ||= false
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
@@ -1,71 +0,0 @@
|
|
1
|
-
require 'em-websocket'
|
2
|
-
require 'yajl'
|
3
|
-
|
4
|
-
require 'ganymed/websocket'
|
5
|
-
require 'ganymed/websocket/authentication'
|
6
|
-
require 'ganymed/websocket/subscribe'
|
7
|
-
require 'ganymed/websocket/metadata'
|
8
|
-
require 'ganymed/websocket/query'
|
9
|
-
|
10
|
-
module Ganymed
|
11
|
-
class Websocket
|
12
|
-
class Connection < EventMachine::WebSocket::Connection
|
13
|
-
attr_accessor :config, :db
|
14
|
-
|
15
|
-
def self.command(command)
|
16
|
-
@commands ||= []
|
17
|
-
@commands << command
|
18
|
-
end
|
19
|
-
|
20
|
-
def self.commands
|
21
|
-
@commands
|
22
|
-
end
|
23
|
-
|
24
|
-
include Authentication
|
25
|
-
include Metadata
|
26
|
-
include Subscribe
|
27
|
-
include Query
|
28
|
-
|
29
|
-
def peer
|
30
|
-
sin = Socket.unpack_sockaddr_in(get_peername)
|
31
|
-
"#{sin[1]}:#{sin[0]}"
|
32
|
-
rescue
|
33
|
-
"unknown"
|
34
|
-
end
|
35
|
-
|
36
|
-
def convert(data)
|
37
|
-
if data.is_a?(Hash)
|
38
|
-
data
|
39
|
-
elsif data.is_a?(Array)
|
40
|
-
data.map {|elem| convert(elem)}
|
41
|
-
else
|
42
|
-
if data.respond_to?(:as_json)
|
43
|
-
data.as_json
|
44
|
-
elsif data.respond_to?(:to_h)
|
45
|
-
data.to_h
|
46
|
-
elsif data.respond_to?(:to_a)
|
47
|
-
data.to_a
|
48
|
-
else
|
49
|
-
data
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def send(type, data)
|
55
|
-
super(Yajl::Encoder.encode({type.to_s => convert(data)}))
|
56
|
-
end
|
57
|
-
|
58
|
-
def trigger_on_message(data)
|
59
|
-
data = Yajl::Parser.parse(data)
|
60
|
-
data.each do |command, data|
|
61
|
-
log.debug("command #{command}(#{data.inspect})")
|
62
|
-
if self.class.commands.include?(command.to_sym)
|
63
|
-
__send__(command.to_sym, data)
|
64
|
-
else
|
65
|
-
send(:error, "invalid command")
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
require 'ganymed/websocket'
|
2
|
-
|
3
|
-
module Ganymed
|
4
|
-
class Websocket
|
5
|
-
class Filter
|
6
|
-
def initialize(spec)
|
7
|
-
@spec = spec
|
8
|
-
end
|
9
|
-
|
10
|
-
def match?(event)
|
11
|
-
@spec.map do |skey, svalue|
|
12
|
-
evalue = event.send(skey.to_sym)
|
13
|
-
case svalue
|
14
|
-
when Array
|
15
|
-
svalue.include?(evalue)
|
16
|
-
else
|
17
|
-
svalue == evalue
|
18
|
-
end
|
19
|
-
end.all?
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
@@ -1,21 +0,0 @@
|
|
1
|
-
require 'digest'
|
2
|
-
|
3
|
-
require 'ganymed/websocket'
|
4
|
-
|
5
|
-
module Ganymed
|
6
|
-
class Websocket
|
7
|
-
module Metadata
|
8
|
-
def self.included(base)
|
9
|
-
base.command :metadata
|
10
|
-
end
|
11
|
-
|
12
|
-
def metadata(data=nil)
|
13
|
-
metadata = db['metadata'].find()
|
14
|
-
send(:metadata, metadata)
|
15
|
-
rescue Exception => exc
|
16
|
-
log.warn("failed to send metadata to #{peer}")
|
17
|
-
log.exception(exc)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
@@ -1,30 +0,0 @@
|
|
1
|
-
require 'ganymed/websocket'
|
2
|
-
require 'ganymed/websocket/filter'
|
3
|
-
|
4
|
-
module Ganymed
|
5
|
-
class Websocket
|
6
|
-
module Query
|
7
|
-
def self.included(base)
|
8
|
-
base.command :query
|
9
|
-
end
|
10
|
-
|
11
|
-
def query(data)
|
12
|
-
return if not authenticated?
|
13
|
-
data.each do |ns, query|
|
14
|
-
query_id = query.delete('_id')
|
15
|
-
|
16
|
-
log.debug("query #{query_id} from #{peer}: #{ns}(#{query.inspect})")
|
17
|
-
t = Time.now
|
18
|
-
|
19
|
-
events = db.collection(ns).find(query).map do |event|
|
20
|
-
Event.parse(event.merge({'n' => ns}))
|
21
|
-
end
|
22
|
-
|
23
|
-
log.debug("query #{query_id} returned #{events.length} results in #{Time.now - t}s")
|
24
|
-
|
25
|
-
send(:result, {query_id => convert(events)})
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
@@ -1,45 +0,0 @@
|
|
1
|
-
require 'ganymed/websocket'
|
2
|
-
require 'ganymed/websocket/filter'
|
3
|
-
|
4
|
-
module Ganymed
|
5
|
-
class Websocket
|
6
|
-
module Subscribe
|
7
|
-
def self.included(base)
|
8
|
-
base.command :subscribe
|
9
|
-
base.command :unsubscribe
|
10
|
-
end
|
11
|
-
|
12
|
-
def filters
|
13
|
-
@filters ||= {}
|
14
|
-
end
|
15
|
-
|
16
|
-
def subscribe(data)
|
17
|
-
return if not authenticated?
|
18
|
-
data.each do |ns, filter|
|
19
|
-
log.debug("client #{peer} subscribed to #{ns} with filter=#{filter.inspect}")
|
20
|
-
filters[ns] = Filter.new(filter)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def unsubscribe(data)
|
25
|
-
return if not authenticated?
|
26
|
-
data.each do |ns, filter|
|
27
|
-
log.debug("client #{peer} unsubscribed namespace #{ns}")
|
28
|
-
filters.delete(ns)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
def subscribed?(event)
|
33
|
-
filters.map do |ns, filter|
|
34
|
-
filter if ns == "all" or event.ns == ns
|
35
|
-
end.compact.map do |filter|
|
36
|
-
filter.match?(event)
|
37
|
-
end.any?
|
38
|
-
end
|
39
|
-
|
40
|
-
def publish(event)
|
41
|
-
send(:event, [event]) if subscribed?(event)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
data/lib/ganymed/websocket.rb
DELETED
@@ -1,45 +0,0 @@
|
|
1
|
-
require 'em-websocket'
|
2
|
-
require 'yajl'
|
3
|
-
|
4
|
-
require 'ganymed'
|
5
|
-
require 'ganymed/websocket/connection'
|
6
|
-
|
7
|
-
module Ganymed
|
8
|
-
class Websocket
|
9
|
-
attr_accessor :config, :db
|
10
|
-
|
11
|
-
def initialize(config, db)
|
12
|
-
@config, @db = config, db
|
13
|
-
@connections = []
|
14
|
-
run
|
15
|
-
end
|
16
|
-
|
17
|
-
def run
|
18
|
-
log.info("accepting WebSocket connections on tcp##{config.host}:#{config.port}")
|
19
|
-
EM.start_server(config.host, config.port, Connection, {}) do |connection|
|
20
|
-
connection.config = config
|
21
|
-
connection.db = db
|
22
|
-
|
23
|
-
connection.onopen do
|
24
|
-
log.info("new connection from #{connection.peer}")
|
25
|
-
@connections << connection
|
26
|
-
end
|
27
|
-
|
28
|
-
connection.onclose do
|
29
|
-
log.info("#{connection.peer} has closed the connection")
|
30
|
-
@connections.delete(connection)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
def each(&block)
|
36
|
-
@connections.each(&block)
|
37
|
-
end
|
38
|
-
|
39
|
-
def send(type, data)
|
40
|
-
each do |connection|
|
41
|
-
connection.send(type, data)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
@@ -1,11 +0,0 @@
|
|
1
|
-
require 'ganymed/sampler/counter'
|
2
|
-
require 'sampler/datasource_examples'
|
3
|
-
|
4
|
-
describe Ganymed::Sampler::Counter do
|
5
|
-
include_context 'DataSource'
|
6
|
-
subject { Ganymed::Sampler::Counter.new(ticks) }
|
7
|
-
|
8
|
-
let(:values) { 10.times.collect { rand } }
|
9
|
-
|
10
|
-
it_behaves_like Ganymed::Sampler::DataSource
|
11
|
-
end
|
@@ -1,49 +0,0 @@
|
|
1
|
-
require 'ganymed/sampler/datasource'
|
2
|
-
|
3
|
-
shared_context 'DataSource' do
|
4
|
-
let(:ns) { 'foo.bar' }
|
5
|
-
let(:origin) { 'example.com' }
|
6
|
-
let(:ticks) { [300, 1] }
|
7
|
-
let(:value) { rand }
|
8
|
-
end
|
9
|
-
|
10
|
-
shared_examples Ganymed::Sampler::DataSource do
|
11
|
-
%w(
|
12
|
-
add
|
13
|
-
buffer
|
14
|
-
each
|
15
|
-
feed
|
16
|
-
flush
|
17
|
-
ticks
|
18
|
-
).each do |method|
|
19
|
-
it { should respond_to method }
|
20
|
-
end
|
21
|
-
|
22
|
-
describe "#add" do
|
23
|
-
it "should add the given value to each tick buffer" do
|
24
|
-
subject.add(ns, origin, value)
|
25
|
-
ticks.each do |tick|
|
26
|
-
subject.buffer[tick][ns][origin].should include(value)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
describe "#each" do
|
32
|
-
before(:each) do
|
33
|
-
values.each do |value|
|
34
|
-
subject.add(ns, origin, value)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
it "should clear the buffer" do
|
39
|
-
subject.each(1) { |ns, origin, values| }
|
40
|
-
subject.buffer[1].should be_empty
|
41
|
-
end
|
42
|
-
|
43
|
-
it "should yield all values" do
|
44
|
-
subject.should_receive(:each).with(1).once.and_yield(ns, origin, values).once
|
45
|
-
subject.each(1) { |ns, origin, values| }
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
require 'ganymed/sampler/derive'
|
2
|
-
require 'sampler/datasource_examples'
|
3
|
-
|
4
|
-
describe Ganymed::Sampler::DataSource do
|
5
|
-
include_context 'DataSource'
|
6
|
-
subject { Ganymed::Sampler::DataSource.new(ticks) }
|
7
|
-
|
8
|
-
let(:values) { 10.times.collect { rand } }
|
9
|
-
|
10
|
-
it_behaves_like Ganymed::Sampler::DataSource
|
11
|
-
|
12
|
-
describe "#flush" do
|
13
|
-
it "is not implemented" do
|
14
|
-
expect { subject.flush(1) }.to raise_error(NotImplementedError)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
describe "#feed" do
|
19
|
-
it "is not implemented" do
|
20
|
-
expect { subject.feed(ns, origin, nil, value) }.to raise_error(NotImplementedError)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
data/spec/sampler/derive_spec.rb
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
require 'ganymed/sampler/derive'
|
2
|
-
require 'sampler/datasource_examples'
|
3
|
-
|
4
|
-
describe Ganymed::Sampler::Derive do
|
5
|
-
include_context 'DataSource'
|
6
|
-
subject { Ganymed::Sampler::Derive.new(ticks) }
|
7
|
-
|
8
|
-
let(:ts) { now = Time.now.utc; now.to_i + (now.usec * 1e-6) }
|
9
|
-
let(:values) { 10.times.collect { rand } }
|
10
|
-
|
11
|
-
it_behaves_like Ganymed::Sampler::DataSource
|
12
|
-
|
13
|
-
describe "#flush" do
|
14
|
-
before(:each) do
|
15
|
-
values.each do |value|
|
16
|
-
subject.feed(ns, origin, ts, value)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
it "should call each" do
|
21
|
-
subject.should_receive(:each).with(1).once
|
22
|
-
subject.flush(1)
|
23
|
-
end
|
24
|
-
|
25
|
-
it "should yield derived values"
|
26
|
-
end
|
27
|
-
|
28
|
-
describe "#feed" do
|
29
|
-
it "should add fed samples" do
|
30
|
-
subject.should_receive(:add).with(ns, origin, [ts, value])
|
31
|
-
subject.feed(ns, origin, ts, value)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
data/spec/sampler/gauge_spec.rb
DELETED
@@ -1,35 +0,0 @@
|
|
1
|
-
require 'ganymed/sampler/gauge'
|
2
|
-
require 'sampler/datasource_examples'
|
3
|
-
|
4
|
-
describe Ganymed::Sampler::Gauge do
|
5
|
-
include_context 'DataSource'
|
6
|
-
subject { Ganymed::Sampler::Gauge.new(ticks) }
|
7
|
-
|
8
|
-
let(:values) { 10.times.collect { rand } }
|
9
|
-
|
10
|
-
it_behaves_like Ganymed::Sampler::DataSource
|
11
|
-
|
12
|
-
describe "#flush" do
|
13
|
-
before(:each) do
|
14
|
-
values.each do |value|
|
15
|
-
subject.feed(ns, origin, nil, value)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
it "should call each" do
|
20
|
-
subject.should_receive(:each).with(1).once
|
21
|
-
subject.flush(1)
|
22
|
-
end
|
23
|
-
|
24
|
-
it "should yield all values" do
|
25
|
-
expect { |b| subject.flush(1, &b) }.to yield_with_args(ns, origin, values)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
describe "#feed" do
|
30
|
-
it "should add fed samples" do
|
31
|
-
subject.should_receive(:add).with(ns, origin, value)
|
32
|
-
subject.feed(ns, origin, nil, value)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|