monitoring_protocols 0.0.4

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ea3d4b5a1ff69111426666e3e6c9365db1794ef2
4
+ data.tar.gz: f1792e58cbf6b9e78cc40d0c1fda80607d3d8914
5
+ SHA512:
6
+ metadata.gz: 87bc1ec3055c2d0424db2992e6195d8c14bcf06c555785d2fdee3dd74f4944afc4013b0a33235290708bbda1bfd8f35a0b1f337f8b3ef0e23c0562edad355a6d
7
+ data.tar.gz: 8155810e85f50e3d7f88b6bddbfbe998b2842a20c69fd258e7758723f56073cc8de1f0630893ff027063ef3f9480b8289734871289ece389f1f62587993eb29f
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ Gemfile.lock
5
+ coverage/
6
+ doc/
data/Gemfile ADDED
@@ -0,0 +1,18 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'rake'
6
+
7
+ group(:test) do
8
+ gem 'eetee', '~> 0.0.9'
9
+ # gem 'eetee', path: '/Users/Schmurfy/Dev/personal/gems/eetee'
10
+ gem 'mocha', '~> 0.12.0'
11
+ gem 'factory_girl'
12
+ gem 'rb-blink1'
13
+
14
+ gem 'simplecov'
15
+ gem 'guard', '~> 1.8.1'
16
+ gem 'rb-fsevent'
17
+ gem 'growl'
18
+ end
data/Guardfile ADDED
@@ -0,0 +1,8 @@
1
+
2
+ require 'eetee'
3
+ require 'blink1'
4
+
5
+ guard 'eetee', blink1: true do
6
+ watch(%r{^lib/monitoring_protocols/(.+)\.rb$}) { |m| "specs/unit/#{m[1]}_spec.rb" }
7
+ watch(%r{specs/.+\.rb$})
8
+ end
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Julien Ammous
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,27 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require "bundler/gem_tasks"
4
+
5
+ task :default => :test
6
+
7
+ task :test do
8
+
9
+ # do not generate coverage report under travis
10
+ unless ENV['TRAVIS']
11
+
12
+ require 'simplecov'
13
+ SimpleCov.command_name "E.T."
14
+ SimpleCov.start do
15
+ add_filter ".*_spec"
16
+ add_filter "/specs/helpers/"
17
+ add_filter "/example/"
18
+ end
19
+ end
20
+
21
+ require 'eetee'
22
+
23
+ runner = EEtee::Runner.new
24
+ runner.run_pattern('specs/**/*_spec.rb')
25
+ runner.report_results()
26
+
27
+ end
@@ -0,0 +1,22 @@
1
+ require File.expand_path('../monitoring_protocols/core', __FILE__)
2
+
3
+ require File.expand_path('../monitoring_protocols/version', __FILE__)
4
+ require File.expand_path('../monitoring_protocols/struct', __FILE__)
5
+ require File.expand_path('../monitoring_protocols/parser', __FILE__)
6
+ require File.expand_path('../monitoring_protocols/builder', __FILE__)
7
+
8
+ require File.expand_path('../monitoring_protocols/collectd/msg', __FILE__)
9
+ require File.expand_path('../monitoring_protocols/collectd/builder', __FILE__)
10
+ require File.expand_path('../monitoring_protocols/collectd/parser', __FILE__)
11
+
12
+ require File.expand_path('../monitoring_protocols/json/parser', __FILE__)
13
+ require File.expand_path('../monitoring_protocols/json/builder', __FILE__)
14
+
15
+ require File.expand_path('../monitoring_protocols/msgpack/parser', __FILE__)
16
+
17
+
18
+ module MonitoringProtocols
19
+ def self.factory_file
20
+ File.expand_path('../../specs/factories.rb', __FILE__)
21
+ end
22
+ end
@@ -0,0 +1,10 @@
1
+ module MonitoringProtocols
2
+
3
+ class Builder
4
+
5
+ def initialize(points)
6
+ @points = points
7
+ end
8
+
9
+ end
10
+ end
@@ -0,0 +1,96 @@
1
+ module MonitoringProtocols
2
+ module Collectd
3
+
4
+ class Builder
5
+ # part type
6
+ HOST = 0x0000
7
+ TIME = 0x0001
8
+ PLUGIN = 0x0002
9
+ PLUGIN_INSTANCE = 0x0003
10
+ TYPE = 0x0004
11
+ TYPE_INSTANCE = 0x0005
12
+ VALUES = 0x0006
13
+ INTERVAL = 0x0007
14
+ MESSAGE = 0x0100
15
+ SEVERITY = 0x0101
16
+
17
+ # data type
18
+ COUNTER = 0
19
+ GAUGE = 1
20
+ DERIVE = 2
21
+ ABSOLUTE = 3
22
+
23
+
24
+ attr_accessor :host, :time, :interval
25
+ attr_accessor :plugin, :plugin_instance
26
+ attr_accessor :type, :type_instance
27
+
28
+ # Encode a string (type 0, null terminated string)
29
+ def self.string(type, str)
30
+ str += "\000"
31
+ str_size = str.respond_to?(:bytesize) ? str.bytesize : str.size
32
+ [type, 4 + str_size].pack("nn") + str
33
+ end
34
+
35
+ # Encode an integer
36
+ def self.number(type, num)
37
+ [type, 12].pack("nn") + [num >> 32, num & 0xffffffff].pack("NN")
38
+ end
39
+
40
+ def self.values(types = [], values = [])
41
+ # values part header
42
+ ret = [VALUES, 4 + 2 + (values.size * 9), values.size].pack('nnn')
43
+
44
+ # types
45
+ ret << types.pack('C*')
46
+
47
+ # and the values
48
+ values.each.with_index do |v, i|
49
+ case types[i]
50
+ when COUNTER, ABSOLUTE, DERIVE
51
+ ret << [v >> 32, v & 0xffffffff].pack("NN")
52
+
53
+ when GAUGE
54
+ ret << [v].pack('E')
55
+
56
+ else
57
+ raise "unknown type: #{types[i]}"
58
+ end
59
+ end
60
+
61
+ ret
62
+ end
63
+
64
+ def initialize
65
+ @values = []
66
+ @values_type = []
67
+ end
68
+
69
+ def add_value(type, value)
70
+ raise(ArgumentError, "unknown type: #{type}") unless self.class.const_defined?(type.to_s.upcase)
71
+ data_type = self.class.const_get(type.to_s.upcase)
72
+
73
+ @values_type << data_type
74
+ @values << value
75
+ end
76
+
77
+ def build_packet
78
+ @pkt = self.class.string(HOST, @host)
79
+ @pkt << self.class.number(TIME, @time)
80
+ @pkt << self.class.number(INTERVAL, @interval)
81
+ @pkt << self.class.string(PLUGIN, @plugin)
82
+ @pkt << self.class.string(PLUGIN_INSTANCE, @plugin_instance)
83
+ @pkt << self.class.string(TYPE, @type)
84
+ @pkt << self.class.string(TYPE_INSTANCE, @type_instance)
85
+
86
+ @pkt << self.class.values(@values_type, @values)
87
+
88
+ @pkt
89
+ end
90
+
91
+ end
92
+
93
+ end
94
+
95
+ register_builder(:collectd, Collectd::Builder)
96
+ end
@@ -0,0 +1,106 @@
1
+ module MonitoringProtocols
2
+ module Collectd
3
+
4
+ # from collectd source code
5
+ # plugin.h
6
+ #
7
+ ### notification:
8
+ # int severity;
9
+ # cdtime_t time;
10
+ # char message[NOTIF_MAX_MSG_LEN];
11
+ # char host[DATA_MAX_NAME_LEN];
12
+ # char plugin[DATA_MAX_NAME_LEN];
13
+ # char plugin_instance[DATA_MAX_NAME_LEN];
14
+ # char type[DATA_MAX_NAME_LEN];
15
+ # char type_instance[DATA_MAX_NAME_LEN];
16
+ #
17
+ ### data
18
+ # value_t *values;
19
+ # int values_len;
20
+ # cdtime_t time;
21
+ # cdtime_t interval;
22
+ # char host[DATA_MAX_NAME_LEN];
23
+ # char plugin[DATA_MAX_NAME_LEN];
24
+ # char plugin_instance[DATA_MAX_NAME_LEN];
25
+ # char type[DATA_MAX_NAME_LEN];
26
+ # char type_instance[DATA_MAX_NAME_LEN];
27
+ #
28
+ class NetworkMessage < NetworkMessage
29
+ properties(
30
+ :time,
31
+ :host,
32
+ :plugin,
33
+ :plugin_instance,
34
+ :type,
35
+ :type_instance,
36
+
37
+ # data
38
+ :interval,
39
+ :values,
40
+
41
+ # notification
42
+ :message,
43
+ :severity
44
+ )
45
+
46
+ def notification?
47
+ !self.message.nil?
48
+ end
49
+
50
+ def data?
51
+ self.message.nil?
52
+ end
53
+
54
+ def value(index = 0)
55
+ values[index]
56
+ end
57
+
58
+ def plugin_display
59
+ if plugin_instance && !plugin_instance.empty?
60
+ "#{plugin}/#{plugin_instance}"
61
+ else
62
+ plugin
63
+ end
64
+ end
65
+
66
+ def type_display
67
+ if type_instance && !type_instance.empty?
68
+ "#{type}/#{type_instance}"
69
+ else
70
+ type
71
+ end
72
+ end
73
+ ##
74
+ # return a unique id for the measured data.
75
+ #
76
+ def measure_id
77
+ "#{host}-#{plugin_display}-#{type_display}"
78
+ end
79
+
80
+ def convert_content
81
+ common = {
82
+ time: time,
83
+ host: host,
84
+ app_name: plugin,
85
+ res_name: type,
86
+ metric_name: type_instance,
87
+ }
88
+
89
+ if data?
90
+ ret = DataPoint.new(common.merge!(
91
+ value: value
92
+ ))
93
+ else
94
+ ret = Notification.new(common.merge!(
95
+ severity: severity,
96
+ message: message
97
+ ))
98
+ end
99
+
100
+ [ret]
101
+ end
102
+
103
+ end
104
+
105
+ end
106
+ end
@@ -0,0 +1,175 @@
1
+ module MonitoringProtocols
2
+ module Collectd
3
+
4
+ class Parser < Parser
5
+
6
+ # part type
7
+ HOST = 0x0000
8
+ TIME = 0x0001
9
+ TIME_HR = 0X0008
10
+ PLUGIN = 0x0002
11
+ PLUGIN_INSTANCE = 0x0003
12
+ TYPE = 0x0004
13
+ TYPE_INSTANCE = 0x0005
14
+ VALUES = 0x0006
15
+ INTERVAL = 0x0007
16
+ INTERVAL_HR = 0X0009
17
+ MESSAGE = 0x0100
18
+ SEVERITY = 0x0101
19
+
20
+ PART_TYPE_AS_STRING = {
21
+ HOST => 'host',
22
+ TIME => 'time',
23
+ TIME_HR => 'time_hr',
24
+ PLUGIN => 'plugin',
25
+ PLUGIN_INSTANCE => 'plugin_instance',
26
+ TYPE => 'type',
27
+ TYPE_INSTANCE => 'type_instance',
28
+ VALUES => 'values',
29
+ INTERVAL => 'interval',
30
+ INTERVAL_HR => 'interval_hr',
31
+ MESSAGE => 'message',
32
+ SEVERITY => 'severity'
33
+ }.freeze
34
+
35
+ STR_FIELDS = [HOST, PLUGIN, PLUGIN_INSTANCE, TYPE, TYPE_INSTANCE, MESSAGE]
36
+ INT_FIELDS = [TIME, TIME_HR, INTERVAL, INTERVAL_HR, SEVERITY]
37
+
38
+ COUNTER = 0x00
39
+ GAUGE = 0x01
40
+ DERIVE = 0x02
41
+ ABSOLUTE = 0x03
42
+
43
+ def self.parse_part_header(buffer)
44
+ type, length, rest = buffer.unpack('nna*')
45
+ [type, length - 4, rest]
46
+ end
47
+
48
+ INT64_MAX = (1 << 63)
49
+ INT64_SIGN_BIT = (1 << 64)
50
+
51
+ # uint to int
52
+ # "val = val - #{1 << nbits} if (val >= #{1 << (nbits - 1)})"
53
+ def self.parse_int64(buffer, signed = false)
54
+ # [v>>32, v & 0xffffffff].pack("NN")}.join
55
+
56
+ hi, lo, buffer = buffer.unpack("NNa*")
57
+ n = (hi << 32 | lo)
58
+
59
+ if signed && (n >= INT64_MAX)
60
+ n = n - INT64_SIGN_BIT
61
+ end
62
+
63
+ [n, buffer]
64
+ end
65
+
66
+ def self.parse_part(buffer)
67
+ type, length, buffer = parse_part_header(buffer)
68
+ case
69
+ when INT_FIELDS.include?(type) then val, buffer = parse_int64(buffer)
70
+ when STR_FIELDS.include?(type) then val, buffer = buffer.unpack("Z#{length}a*")
71
+ when type == VALUES then val, buffer = parse_part_values(length, buffer)
72
+ else
73
+ val, buffer = buffer.unpack("a#{length}a*")
74
+ raise ParseError, "unknown part: #{type}, data: #{val.inspect}"
75
+ end
76
+
77
+ # just convert to seconds
78
+ if (type == TIME_HR)
79
+ type = TIME
80
+ val = (val >> 30)
81
+ end
82
+
83
+ if (type == INTERVAL_HR)
84
+ type = INTERVAL
85
+ val = (val >> 30)
86
+ p [:interval, val]
87
+ end
88
+
89
+ [
90
+ PART_TYPE_AS_STRING[type],
91
+ val,
92
+ buffer
93
+ ]
94
+ end
95
+
96
+ def self.parse_part_values(length, buffer)
97
+ # first we need to read the types of all the values
98
+ values_count, buffer = buffer.unpack("na*")
99
+ *types, buffer = buffer.unpack("C#{values_count}a*")
100
+ values = types.map! do |type|
101
+ case type
102
+ when COUNTER, ABSOLUTE then val, buffer = parse_int64(buffer)
103
+ when GAUGE then val, buffer = buffer.unpack("Ea*")
104
+ when DERIVE then val, buffer = parse_int64(buffer, true)
105
+ end
106
+
107
+ val
108
+ end
109
+
110
+ [values, buffer]
111
+ end
112
+
113
+ COPY_FIELDS = [
114
+ :time,
115
+ :host,
116
+ :plugin,
117
+ :plugin_instance,
118
+ :type,
119
+ :type_instance,
120
+ :interval
121
+ ].freeze
122
+
123
+ def self.parse_packet(buffer, initial_values = {})
124
+ packet = NetworkMessage.new(initial_values, COPY_FIELDS)
125
+
126
+ begin
127
+ type, value, buffer = parse_part(buffer)
128
+ packet.send("#{type}=", value)
129
+ end until packet.message || packet.values
130
+
131
+ [packet, buffer]
132
+ end
133
+
134
+ def self.parse(buffer)
135
+ packets = []
136
+ last_packet = {}
137
+
138
+ # 4 = part header size
139
+ while buffer.bytesize >= 4
140
+ packet, buffer = parse_packet(buffer, last_packet)
141
+ packets << packet
142
+
143
+ last_packet = packet if packet.data?
144
+ end
145
+
146
+ packets
147
+ end
148
+
149
+ # def initialize
150
+ # @buffer = ""
151
+ # @last_packet = {}
152
+ # end
153
+
154
+ # def feed(data)
155
+ # ret = []
156
+
157
+ # @buffer << data
158
+ # if @buffer.bytesize >= 4
159
+ # pkt, @buffer = self.class.parse_packet(@buffer, @last_packet)
160
+ # if pkt.data?
161
+ # ret << pkt
162
+ # @last_packet = pack
163
+ # end
164
+ # end
165
+
166
+ # ret
167
+ # end
168
+
169
+ end
170
+
171
+
172
+ end
173
+
174
+ register_parser(:collectd, Collectd::Parser)
175
+ end