ganymed 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. data/.gitignore +22 -0
  2. data/.rspec +1 -0
  3. data/.rvmrc +1 -0
  4. data/.yardopts +1 -0
  5. data/Gemfile +20 -0
  6. data/LICENSE +20 -0
  7. data/README.md +28 -0
  8. data/Rakefile +8 -0
  9. data/bin/ganymed +20 -0
  10. data/ganymed.gemspec +38 -0
  11. data/lib/ganymed/client.rb +132 -0
  12. data/lib/ganymed/collector/cpu.rb +30 -0
  13. data/lib/ganymed/collector/disk.rb +35 -0
  14. data/lib/ganymed/collector/iostat.rb +25 -0
  15. data/lib/ganymed/collector/load.rb +18 -0
  16. data/lib/ganymed/collector/metadata.rb +31 -0
  17. data/lib/ganymed/collector/network.rb +30 -0
  18. data/lib/ganymed/collector/uptime.rb +15 -0
  19. data/lib/ganymed/collector.rb +89 -0
  20. data/lib/ganymed/config.yml +49 -0
  21. data/lib/ganymed/event.rb +102 -0
  22. data/lib/ganymed/master.rb +150 -0
  23. data/lib/ganymed/mongodb.rb +39 -0
  24. data/lib/ganymed/processor.rb +93 -0
  25. data/lib/ganymed/sampler/counter.rb +32 -0
  26. data/lib/ganymed/sampler/datasource.rb +92 -0
  27. data/lib/ganymed/sampler/derive.rb +36 -0
  28. data/lib/ganymed/sampler/gauge.rb +24 -0
  29. data/lib/ganymed/sampler.rb +106 -0
  30. data/lib/ganymed/version.rb +3 -0
  31. data/lib/ganymed/websocket/authentication.rb +37 -0
  32. data/lib/ganymed/websocket/connection.rb +71 -0
  33. data/lib/ganymed/websocket/filter.rb +23 -0
  34. data/lib/ganymed/websocket/metadata.rb +21 -0
  35. data/lib/ganymed/websocket/query.rb +26 -0
  36. data/lib/ganymed/websocket/subscribe.rb +45 -0
  37. data/lib/ganymed/websocket.rb +45 -0
  38. data/lib/ganymed.rb +6 -0
  39. data/spec/sampler/counter_spec.rb +11 -0
  40. data/spec/sampler/datasource_examples.rb +49 -0
  41. data/spec/sampler/datasource_spec.rb +23 -0
  42. data/spec/sampler/derive_spec.rb +34 -0
  43. data/spec/sampler/gauge_spec.rb +35 -0
  44. data/spec/sampler_spec.rb +5 -0
  45. data/spec/spec.opts +1 -0
  46. data/spec/spec_helper.rb +10 -0
  47. data/tasks/reek.rake +5 -0
  48. data/tasks/rspec.rake +7 -0
  49. data/tasks/yard.rake +5 -0
  50. metadata +242 -0
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage/
10
+ doc/
11
+ ganymed.pid
12
+ ganymed.profile*
13
+ lib/bundler/man
14
+ node_modules/
15
+ old/
16
+ pkg
17
+ rdoc
18
+ spec/reports
19
+ test.js
20
+ test/tmp
21
+ test/version_tmp
22
+ tmp
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --require spec_helper --color --format documentation
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use --create ruby-1.9.3-p125@ganymed
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ --protected --no-private
data/Gemfile ADDED
@@ -0,0 +1,20 @@
1
+ source :rubygems
2
+
3
+ gemspec
4
+
5
+ group :development, :test do
6
+ gem 'bson_ext'
7
+ gem 'bundler'
8
+ gem 'debugger'
9
+ gem 'perftools.rb'
10
+ gem 'pry'
11
+ gem 'pry-doc'
12
+ gem 'redcarpet'
13
+ gem 'reek'
14
+ gem 'rspec'
15
+ gem 'ruby-prof'
16
+ gem 'ruby2ruby', '=1.3.0' # 1.3.1 is broken :(
17
+ gem 'simplecov'
18
+ gem 'syslogger'
19
+ gem 'yard'
20
+ end
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Benedikt Böhm
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,28 @@
1
+ # Ganymed
2
+
3
+ Ganymed is an event collection daemon.
4
+
5
+ Ganymed processes system and application metrics in realtime and can easily
6
+ handle thousands of samples per second.
7
+
8
+ * the *sampler* accepts high frequency samples. these samples are then
9
+ consolidated based on the data source type and emitted to the *processor*.
10
+
11
+ * the *processor* accepts low frequency data points (possibly created by the
12
+ *sampler*) and publishes these to interested parties via WebSocket in
13
+ realtime. All samples are also stored in MongoDB for later analysis.
14
+
15
+ * the *collector* will send various system metrics (cpu, memory, network, etc)
16
+ to the *sampler* and *processor*
17
+
18
+ ## Installation
19
+
20
+ TBD
21
+
22
+ ## Contributing
23
+
24
+ 1. Fork it
25
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
26
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
27
+ 4. Push to the branch (`git push origin my-new-feature`)
28
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env rake
2
+
3
+ require "bundler/setup"
4
+ require "bundler/gem_tasks"
5
+
6
+ Dir['tasks/**/*.rake'].each { |t| load t }
7
+
8
+ task :default => [:spec]
data/bin/ganymed ADDED
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ $:.unshift(File.join(File.expand_path("../..", __FILE__), 'lib'))
5
+
6
+ require 'ganymed/master'
7
+ cli = Ganymed::Master::CLI.parse_options
8
+ server = Ganymed::Master.new(cli.config)
9
+
10
+ if cli.config[:daemonize] or cli.config[:kill]
11
+ daemon = Servolux::Daemon.new(:server => server)
12
+
13
+ if cli.config[:kill]
14
+ daemon.shutdown
15
+ else
16
+ daemon.startup
17
+ end
18
+ else
19
+ server.startup
20
+ end
data/ganymed.gemspec ADDED
@@ -0,0 +1,38 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "ganymed/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "ganymed"
7
+ s.version = Ganymed::VERSION
8
+ s.authors = ["Benedikt Böhm"]
9
+ s.email = ["bb@xnull.de"]
10
+ s.homepage = "http://github.com/zenops/ganymed"
11
+ s.summary = %q{Ganymed is an event collection daemon}
12
+ s.description = %q{Ganymed is an event collection daemon}
13
+
14
+ s.files = `git ls-files`.split("\n")
15
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
17
+ s.require_paths = ["lib"]
18
+
19
+ # master
20
+ s.add_dependency "activesupport", ">= 3.2"
21
+ s.add_dependency "eventmachine", ">= 0.12.10"
22
+ s.add_dependency "madvertise-ext", ">= 0.1.2"
23
+ s.add_dependency "madvertise-logging", ">= 0.3.2"
24
+ s.add_dependency "mixlib-cli"
25
+ s.add_dependency "servolux"
26
+
27
+ # processor
28
+ s.add_dependency "msgpack"
29
+ s.add_dependency "mongo", ">= 1.6"
30
+
31
+ # websocket
32
+ s.add_dependency "em-websocket"
33
+ s.add_dependency "yajl-ruby"
34
+
35
+ # collector
36
+ s.add_dependency "sys-filesystem"
37
+ s.add_dependency "ohai", ">= 0.6.12"
38
+ end
@@ -0,0 +1,132 @@
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_i + (now.usec * 1e-6), 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 [Fixnum, Float] 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 [String] origin {Event} origin.
86
+ # @option opts [Array] modifiers Event modifiers.
87
+ def event(ns, value, opts={})
88
+ opts = {
89
+ :cf => nil,
90
+ :now => Time.now.utc,
91
+ :origin => @origin,
92
+ :modifiers => [],
93
+ }.merge(opts)
94
+
95
+ {
96
+ :_type => :event,
97
+ :n => ns,
98
+ :m => opts[:modifiers],
99
+ :c => opts[:cf],
100
+ :o => opts[:origin],
101
+ :t => opts[:now].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
@@ -0,0 +1,30 @@
1
+ require 'ganymed/collector'
2
+
3
+ module Ganymed
4
+ class Collector
5
+ class CPU < Base
6
+ def collect!
7
+ File.open('/proc/stat') do |f|
8
+ cpu = f.readline.chomp.split[1,7].map do |x|
9
+ x.to_i / hz
10
+ end
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
20
+
21
+ def interval
22
+ @config.interval.tap{} or 0.2
23
+ end
24
+
25
+ def hz
26
+ @config.hz.tap{} or 100
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,35 @@
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
+
13
+ block_pc = 1.0 - (st.blocks_free.to_f / st.blocks.to_f)
14
+ @processor.event("os.disk.#{name}.blocks", block_pc)
15
+
16
+ files_pc = 1.0 - (st.files_free.to_f / st.files.to_f)
17
+ @processor.event("os.disk.#{name}.files", block_pc)
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def name(mount)
24
+ case mount
25
+ when '/'
26
+ 'rootfs'
27
+ when /^\/.+$/
28
+ mount[1..-1].tr('^[a-z][0-9]', '-')
29
+ else
30
+ 'unknown'
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,25 @@
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
+ File.open('/proc/diskstats') do |f|
12
+ f.each do |line|
13
+ ios = Struct::IOStat.new(*line.strip.split(/\s+/))
14
+ @sampler.emit(:derive, "os.iostat.#{ios.dev}.rsect", ios.rsect)
15
+ @sampler.emit(:derive, "os.iostat.#{ios.dev}.wsect", ios.wsect)
16
+ end
17
+ end
18
+ end
19
+
20
+ def interval
21
+ @config.interval.tap{} or 0.2
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,18 @@
1
+ require 'ganymed/collector'
2
+
3
+ module Ganymed
4
+ class Collector
5
+ class Load < Base
6
+ def collect!
7
+ File.open('/proc/loadavg') do |f|
8
+ loadavg = f.read.chomp.split[0,3].map(&:to_f)
9
+ @sampler.emit(:gauge, "os.loadavg", loadavg[0])
10
+ end
11
+ end
12
+
13
+ def interval
14
+ @config.interval.tap{} or 0.2
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,31 @@
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
+ end
24
+ end
25
+
26
+ def interval
27
+ nil
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,30 @@
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
+ File.open('/proc/net/dev') do |f|
11
+ f.each do |line|
12
+ next unless line =~ /:/
13
+ ns = Struct::NetworkStat.new(*line.strip.split(/\s+/))
14
+ ns.dev.sub!(/:$/, '')
15
+ @sampler.emit(:derive, "os.net.#{ns.dev}.rbytes", ns.rbytes)
16
+ @sampler.emit(:derive, "os.net.#{ns.dev}.wbytes", ns.wbytes)
17
+ @sampler.emit(:derive, "os.net.#{ns.dev}.rpackets", ns.rpackets)
18
+ @sampler.emit(:derive, "os.net.#{ns.dev}.wpackets", ns.wpackets)
19
+ @sampler.emit(:derive, "os.net.#{ns.dev}.rerrs", ns.rerrs)
20
+ @sampler.emit(:derive, "os.net.#{ns.dev}.werrs", ns.werrs)
21
+ end
22
+ end
23
+ end
24
+
25
+ def interval
26
+ @config.interval.tap{} or 0.1
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,15 @@
1
+ require 'ganymed/collector'
2
+
3
+ module Ganymed
4
+ class Collector
5
+ class Uptime < Base
6
+ def collect!
7
+ File.open('/proc/uptime') do |f|
8
+ uptime = f.read.chomp.split.first.to_f
9
+ boottime = Time.now - uptime
10
+ @processor.event("os.reboot", 1, :now => boottime)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,89 @@
1
+ require 'eventmachine'
2
+
3
+ require 'ganymed'
4
+ require 'ganymed/client'
5
+
6
+ module Ganymed
7
+
8
+ ##
9
+ # The Collector polls various system-level metrics that are not pushed to the
10
+ # {Processor} automatically.
11
+ #
12
+ class Collector
13
+ # The configuration object.
14
+ attr_reader :config
15
+
16
+ # The {Client} object.
17
+ attr_reader :client
18
+
19
+ # Create a new collector instance and initialize all configured collectors.
20
+ #
21
+ # @param [Configuration] config The configuration object.
22
+ def initialize(config)
23
+ @config = config
24
+ @client = Client.new(:processor => @config.collector.processor,
25
+ :sampler => @config.collector.sampler)
26
+
27
+ @config.collectors.each do |collector|
28
+ log.info("initializing collector #{collector.klass.demodulize}")
29
+ collector.klass.constantize.new(collector, self).run
30
+ end
31
+ end
32
+
33
+ ##
34
+ # A base class for collectors.
35
+ #
36
+ class Base
37
+
38
+ # Create a new data collector instance.
39
+ #
40
+ # @param [Configuration] config The configuration object.
41
+ # @param [Collector] collector The collector instance.
42
+ def initialize(config, collector)
43
+ @config, @collector = config, collector
44
+ @processor = @collector.client.processor
45
+ @sampler = @collector.client.sampler
46
+ end
47
+
48
+ # Start the EventMachine timer to collect metrics at the specified
49
+ # interval.
50
+ def run
51
+ timer do
52
+ begin
53
+ collect!
54
+ rescue Exception => e
55
+ log.exception(e)
56
+ end
57
+ end
58
+ end
59
+
60
+ # @private
61
+ def timer
62
+ if interval
63
+ EM.add_periodic_timer(interval) do
64
+ EM.defer { yield }
65
+ end
66
+ else
67
+ yield
68
+ end
69
+ end
70
+
71
+ # The interval used to collect metrics. Either taken from the
72
+ # configuration file or overriden by subclasses.
73
+ def interval
74
+ @config.interval.tap{} or @collector.config.resolution
75
+ end
76
+
77
+ # This method must be implemented by subclasses to collect the desired
78
+ # metrics. This function is called every #interval seconds after the
79
+ # timer has been initialized by the #run method.
80
+ def collect!
81
+ raise NotImplementedError
82
+ end
83
+ end
84
+ end
85
+ end
86
+
87
+ Dir[File.join(Ganymed::LIB_DIR, 'ganymed/collector/*.rb')].each do |f|
88
+ require f
89
+ end
@@ -0,0 +1,49 @@
1
+ generic:
2
+ pid_file: /tmp/ganymed.pid
3
+
4
+ resolution: 300
5
+
6
+ profiling:
7
+ perftools: false
8
+ gcprofiler: false
9
+ rubyprof: false
10
+
11
+ processor:
12
+ enabled: true
13
+ listen:
14
+ host: 0.0.0.0
15
+ port: 1336
16
+ mongodb:
17
+ host: localhost
18
+ port: 27017
19
+ database: ganymed
20
+ websocket:
21
+ host: 0.0.0.0
22
+ port: 1338
23
+
24
+ sampler:
25
+ enabled: true
26
+ listen:
27
+ host: 127.0.0.1
28
+ port: 1337
29
+ emit:
30
+ host: 127.0.0.1
31
+ port: 1336
32
+
33
+ collector:
34
+ enabled: true
35
+ processor:
36
+ host: 127.0.0.1
37
+ port: 1336
38
+ sampler:
39
+ host: 127.0.0.1
40
+ port: 1337
41
+
42
+ collectors:
43
+ - klass: Ganymed::Collector::Metadata
44
+ - klass: Ganymed::Collector::Uptime
45
+ - klass: Ganymed::Collector::Load
46
+ - klass: Ganymed::Collector::CPU
47
+ - klass: Ganymed::Collector::Network
48
+ - klass: Ganymed::Collector::Disk
49
+ - klass: Ganymed::Collector::IOStat