monitoring_protocols 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +6 -0
- data/Gemfile +18 -0
- data/Guardfile +8 -0
- data/LICENSE +22 -0
- data/Rakefile +27 -0
- data/lib/monitoring_protocols.rb +22 -0
- data/lib/monitoring_protocols/builder.rb +10 -0
- data/lib/monitoring_protocols/collectd/builder.rb +96 -0
- data/lib/monitoring_protocols/collectd/msg.rb +106 -0
- data/lib/monitoring_protocols/collectd/parser.rb +175 -0
- data/lib/monitoring_protocols/core.rb +23 -0
- data/lib/monitoring_protocols/data_struct.rb +98 -0
- data/lib/monitoring_protocols/json/builder.rb +41 -0
- data/lib/monitoring_protocols/json/parser.rb +115 -0
- data/lib/monitoring_protocols/msgpack/parser.rb +16 -0
- data/lib/monitoring_protocols/parser.rb +22 -0
- data/lib/monitoring_protocols/struct.rb +97 -0
- data/lib/monitoring_protocols/version.rb +3 -0
- data/monitoring_protocols.gemspec +19 -0
- data/specs/factories.rb +44 -0
- data/specs/spec_helper.rb +15 -0
- data/specs/unit/collectd/builder_spec.rb +58 -0
- data/specs/unit/collectd/msg_spec.rb +93 -0
- data/specs/unit/collectd/parser_spec.rb +212 -0
- data/specs/unit/core_spec.rb +19 -0
- data/specs/unit/data_struct_spec.rb +89 -0
- data/specs/unit/json/builder_spec.rb +49 -0
- data/specs/unit/json/parser_spec.rb +353 -0
- data/specs/unit/msgpack/parser_spec.rb +322 -0
- data/specs/unit/parser_spec.rb +18 -0
- data/specs/unit/struct_spec.rb +83 -0
- metadata +102 -0
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
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
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,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
|