ffwd-collectd 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 56020d48d93dc000931a007ace619ad887135b62
4
+ data.tar.gz: 62204ed7e4b07dd2caec921729cce2b9cfdafdf2
5
+ SHA512:
6
+ metadata.gz: 227f6d4c8a2e86d72f694b2460296108cd171d037f82974a0c5ec721006b2201eeecd84e7bdc443e43e4f06efcf59350b2725813104ce6f5d2af670904a271f8
7
+ data.tar.gz: 5f881f021cc072c3a200e1e9240d1bef04fea22a874c465d97e149d3eff1fbd62fa0b60e2aab00a0bc1565ba2bb628d575b030ace96418a5bac2f4653332b124
@@ -0,0 +1,83 @@
1
+ # $LICENSE
2
+ # Copyright 2013-2014 Spotify AB. All rights reserved.
3
+ #
4
+ # The contents of this file are licensed under the Apache License, Version 2.0
5
+ # (the "License"); you may not use this file except in compliance with the
6
+ # License. You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+ # License for the specific language governing permissions and limitations under
14
+ # the License.
15
+
16
+ require 'ffwd/logging'
17
+ require 'ffwd/connection'
18
+
19
+ require_relative 'parser'
20
+
21
+ module FFWD::Plugin::Collectd
22
+ class Connection < FFWD::Connection
23
+ include FFWD::Logging
24
+
25
+ def self.plugin_type
26
+ "collectd_in"
27
+ end
28
+
29
+ def initialize bind, core, types_db
30
+ @bind = bind
31
+ @core = core
32
+ @types_db = types_db
33
+ end
34
+
35
+ def receive_data(data)
36
+ Parser.parse(data) do |metric|
37
+ plugin_key = metric[:plugin]
38
+ type_key = metric[:type]
39
+
40
+ if instance = metric[:plugin_instance] and not instance.empty?
41
+ plugin_key = "#{plugin_key}-#{instance}"
42
+ end
43
+
44
+ if instance = metric[:type_instance] and not instance.empty?
45
+ type_key = "#{type_key}-#{instance}"
46
+ end
47
+
48
+ key = "#{plugin_key}/#{type_key}"
49
+
50
+ values = metric[:values]
51
+
52
+ time = metric[:time]
53
+ host = metric[:host]
54
+
55
+ # Just add a running integer to the end of the key, the 'correct'
56
+ # solution would have been to read, parse and match from a types.db.
57
+ #
58
+ # http://collectd.org/documentation/manpages/types.db.5.shtml
59
+ if values.size > 1
60
+ values.each_with_index do |v, i|
61
+ if @types_db and name = @types_db.get_name(type_key, i)
62
+ index_key = name
63
+ else
64
+ index_key = i.to_s
65
+ end
66
+
67
+ @core.input.metric(
68
+ :key => "#{key}_#{index_key}", :time => time, :value => v[1],
69
+ :host => host)
70
+ @bind.increment :received_metrics
71
+ end
72
+ else
73
+ v = values[0]
74
+ @core.input.metric(
75
+ :key => key, :time => time, :value => v[1], :host => host)
76
+ @bind.increment :received_metrics
77
+ end
78
+ end
79
+ rescue => e
80
+ log.error "Failed to receive data", e
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,159 @@
1
+ # $LICENSE
2
+ # Copyright 2013-2014 Spotify AB. All rights reserved.
3
+ #
4
+ # The contents of this file are licensed under the Apache License, Version 2.0
5
+ # (the "License"); you may not use this file except in compliance with the
6
+ # License. You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+ # License for the specific language governing permissions and limitations under
14
+ # the License.
15
+
16
+ require 'ffwd/logging'
17
+
18
+ module FFWD::Plugin::Collectd
19
+ # An parser implementation of
20
+ # https://collectd.org/wiki/index.php/Binary_protocol
21
+ module Parser
22
+ include FFWD::Logging
23
+
24
+ HOST = 0x0000
25
+ TIME = 0x0001
26
+ TIME_HR = 0x0008
27
+ PLUGIN = 0x0002
28
+ PLUGIN_INSTANCE = 0x0003
29
+ TYPE = 0x0004
30
+ TYPE_INSTANCE = 0x0005
31
+ VALUES = 0x0006
32
+ INTERVAL = 0x0007
33
+ INTERVAL_HR = 0x0009
34
+ MESSAGE = 0x0100
35
+ SEVERITY = 0x0101
36
+
37
+ COUNTER = 0
38
+ GAUGE = 1
39
+ DERIVE = 2
40
+ ABSOLUTE = 3
41
+
42
+ SB64 = 0x8000000000000000
43
+ B64 = 0x10000000000000000
44
+
45
+ FACTOR_HR = 2**30
46
+
47
+ def self.unsigned_integer data
48
+ high, low = data.unpack("NN")
49
+ ((high << 32) | low)
50
+ end
51
+
52
+ def self.signed_integer data
53
+ us = unsigned_integer(data)
54
+
55
+ if (us & SB64) == SB64
56
+ us - B64
57
+ else
58
+ us
59
+ end
60
+ end
61
+
62
+ def self.values frame, i, size
63
+ n = frame[i + 4, 2].unpack("n")[0]
64
+
65
+ result = []
66
+
67
+ types = frame[i + 6, n].unpack("C" * n)
68
+
69
+ types.each_with_index do |type, j|
70
+ o = 6 + n + (j * 8)
71
+ data = frame[i + o, 8]
72
+
73
+ case type
74
+ when COUNTER
75
+ result << [:counter, self.unsigned_integer(data)]
76
+ when GAUGE
77
+ result << [:gauge, data.unpack("E")[0]]
78
+ when DERIVE
79
+ result << [:derive, self.signed_integer(data)]
80
+ when ABSOLUTE
81
+ result << [:absolute, self.unsigned_integer(data)]
82
+ else
83
+ raise "unkonwn value type: #{type}"
84
+ end
85
+
86
+ j += 1
87
+ end
88
+
89
+ result
90
+ end
91
+
92
+ def self.string frame, i, size
93
+ frame[4 + i, (size - 5)]
94
+ end
95
+
96
+ def self.numeric frame, i, size
97
+ unsigned_integer frame[4 + i, 8]
98
+ end
99
+
100
+ def self.time_high_res frame, i, size
101
+ Time.at(numeric(frame, i, size).to_f / FACTOR_HR)
102
+ end
103
+
104
+ def self.interval_high_res frame, i, size
105
+ numeric(frame, i, size).to_f / FACTOR_HR
106
+ end
107
+
108
+ # Maintain a current frame, and yield a copy of it to the subscribing
109
+ # block.
110
+ #
111
+ # Reading a 'values' part is the indicator that a block is 'ready'.
112
+ def self.parse frame
113
+ raise "invalid frame" if frame.size < 4
114
+
115
+ current = {}
116
+
117
+ i = 0
118
+
119
+ loop do
120
+ break if i >= frame.size
121
+
122
+ type, size = frame[i,4].unpack("nn")
123
+
124
+ case type
125
+ when HOST
126
+ current[:host] = self.string(frame, i, size)
127
+ when TIME
128
+ current[:time] = Time.at(self.numeric(frame, i, size))
129
+ when TIME_HR
130
+ current[:time] = self.time_high_res(frame, i, size)
131
+ when PLUGIN
132
+ current[:plugin] = self.string(frame, i, size)
133
+ when PLUGIN_INSTANCE
134
+ current[:plugin_instance] = self.string(frame, i, size)
135
+ when TYPE
136
+ current[:type] = self.string(frame, i, size)
137
+ when TYPE_INSTANCE
138
+ current[:type_instance] = self.string(frame, i, size)
139
+ when VALUES
140
+ values = self.values(frame, i, size)
141
+ current[:values] = values
142
+ yield current
143
+ when INTERVAL
144
+ current[:interval] = self.numeric(frame, i, size).to_f
145
+ when INTERVAL_HR
146
+ current[:interval] = self.interval_high_res(frame, i, size)
147
+ when MESSAGE
148
+ current[:message] = self.string(frame, i, size)
149
+ when SEVERITY
150
+ current[:severity] = self.numeric(frame, i, size)
151
+ else
152
+ log.warning("cannot understand type: #{type}")
153
+ end
154
+
155
+ i += size
156
+ end
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,57 @@
1
+ # $LICENSE
2
+ # Copyright 2013-2014 Spotify AB. All rights reserved.
3
+ #
4
+ # The contents of this file are licensed under the Apache License, Version 2.0
5
+ # (the "License"); you may not use this file except in compliance with the
6
+ # License. You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+ # License for the specific language governing permissions and limitations under
14
+ # the License.
15
+
16
+ module FFWD::Plugin::Collectd
17
+ # A minimal implementation of a reader for collectd's types.db
18
+ #
19
+ # http://collectd.org/documentation/manpages/types.db.5.shtml
20
+ class TypesDB
21
+ def initialize database
22
+ @database = database
23
+ end
24
+
25
+ def get_name key, i
26
+ unless entry = @database[key]
27
+ return nil
28
+ end
29
+
30
+ unless type_spec = entry[i]
31
+ return nil
32
+ end
33
+
34
+ type_spec[0]
35
+ end
36
+
37
+ def self.open path
38
+ return nil unless File.file? path
39
+
40
+ database = {}
41
+
42
+ File.open(path) do |f|
43
+ f.readlines.each do |line|
44
+ next if line.start_with? "#"
45
+ parts = line.split(/[\t ]+/, 2)
46
+ next unless parts.size == 2
47
+ key, value_specs = parts
48
+ value_specs = value_specs.split(",").map(&:strip)
49
+ value_specs = value_specs.map{|s| s.split(':')}
50
+ database[key] = value_specs
51
+ end
52
+ end
53
+
54
+ new database
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,22 @@
1
+ # $LICENSE
2
+ # Copyright 2013-2014 Spotify AB. All rights reserved.
3
+ #
4
+ # The contents of this file are licensed under the Apache License, Version 2.0
5
+ # (the "License"); you may not use this file except in compliance with the
6
+ # License. You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+ # License for the specific language governing permissions and limitations under
14
+ # the License.
15
+
16
+ module FFWD
17
+ module Plugin
18
+ module Collectd
19
+ VERSION = "0.1.0"
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,50 @@
1
+ # $LICENSE
2
+ # Copyright 2013-2014 Spotify AB. All rights reserved.
3
+ #
4
+ # The contents of this file are licensed under the Apache License, Version 2.0
5
+ # (the "License"); you may not use this file except in compliance with the
6
+ # License. You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+ # License for the specific language governing permissions and limitations under
14
+ # the License.
15
+
16
+ require 'ffwd/plugin'
17
+ require 'ffwd/protocol'
18
+ require 'ffwd/logging'
19
+
20
+ require_relative 'collectd/connection'
21
+ require_relative 'collectd/types_db'
22
+
23
+ module FFWD::Plugin::Collectd
24
+ include FFWD::Plugin
25
+ include FFWD::Logging
26
+
27
+ register_plugin "collectd"
28
+
29
+ DEFAULT_HOST = "localhost"
30
+ DEFAULT_PORT = 25826
31
+ DEFAULT_TYPES_DB = "/usr/share/collectd/types.db"
32
+
33
+ def self.setup_input opts, core
34
+ opts[:host] ||= DEFAULT_HOST
35
+ opts[:port] ||= DEFAULT_PORT
36
+ opts[:types_db] ||= DEFAULT_TYPES_DB
37
+ protocol = FFWD.parse_protocol(opts[:protocol] || "udp")
38
+ types_db = TypesDB.open opts[:types_db]
39
+ protocol.bind opts, core, log, Connection, types_db
40
+ end
41
+
42
+ def self.setup_tunnel opts, core, tunnel
43
+ opts[:port] ||= DEFAULT_PORT
44
+ opts[:types_db] ||= DEFAULT_TYPES_DB
45
+ protocol = FFWD.parse_protocol(opts[:protocol] || "udp")
46
+ protocol.tunnel log, opts, Connection
47
+ types_db = TypesDB.open opts[:types_db]
48
+ protocol.bind opts, core, tunnel, log, Connection, types_db
49
+ end
50
+ end
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ffwd-collectd
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - John-John Tedro
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-02-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ffwd
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description:
28
+ email:
29
+ - udoprog@spotify.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - lib/ffwd/plugin/collectd/parser.rb
35
+ - lib/ffwd/plugin/collectd/types_db.rb
36
+ - lib/ffwd/plugin/collectd/connection.rb
37
+ - lib/ffwd/plugin/collectd/version.rb
38
+ - lib/ffwd/plugin/collectd.rb
39
+ homepage: https://github.com/spotify/ffwd
40
+ licenses:
41
+ - Apache 2.0
42
+ metadata: {}
43
+ post_install_message:
44
+ rdoc_options: []
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - '>='
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - '>='
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ requirements: []
58
+ rubyforge_project:
59
+ rubygems_version: 2.0.3
60
+ signing_key:
61
+ specification_version: 4
62
+ summary: collectd support for FFWD.
63
+ test_files: []