ganymed 0.1.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.
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