vayacondios-client 0.1.2 → 0.1.6
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.
data/lib/vayacondios-client.rb
CHANGED
@@ -16,5 +16,7 @@ require 'gorillib/string/constantize'
|
|
16
16
|
require 'gorillib/string/inflections'
|
17
17
|
|
18
18
|
require 'vayacondios/client/http_client'
|
19
|
+
require 'vayacondios/client/cube_client'
|
20
|
+
require 'vayacondios/client/zabbix_client'
|
19
21
|
require 'vayacondios/client/notifier'
|
20
22
|
require 'vayacondios/client/configliere'
|
@@ -0,0 +1,39 @@
|
|
1
|
+
class Vayacondios
|
2
|
+
class CubeClient
|
3
|
+
include Gorillib::Model
|
4
|
+
|
5
|
+
field :host, String, :default => 'localhost'
|
6
|
+
field :port, Integer, :default => 6000
|
7
|
+
|
8
|
+
class Error < StandardError; end
|
9
|
+
|
10
|
+
def uri
|
11
|
+
return @uri if @uri
|
12
|
+
|
13
|
+
uri_str = "http://#{host}:#{port}/1.0"
|
14
|
+
@uri ||= URI(uri_str)
|
15
|
+
end
|
16
|
+
|
17
|
+
def event(topic, document = {})
|
18
|
+
request(:post, File.join(uri.path, 'event'), MultiJson.dump(document))
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def request(method, path, document=nil)
|
24
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
25
|
+
|
26
|
+
params = [method.to_sym, path]
|
27
|
+
params += [document, {'Content-Type' => 'application/json'}] unless document.nil?
|
28
|
+
|
29
|
+
response = http.send *params
|
30
|
+
|
31
|
+
if Net::HTTPSuccess === response
|
32
|
+
MultiJson.load(response.body) rescue response.body
|
33
|
+
else
|
34
|
+
raise Error.new("Error (#{response.code}) while #{method.to_s == 'get' ? 'fetching' : 'inserting'} document: " + response.body)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
@@ -4,42 +4,29 @@ require 'multi_json'
|
|
4
4
|
class Vayacondios
|
5
5
|
class Client
|
6
6
|
class ItemSet
|
7
|
-
def initialize host, port, organization, topic, id
|
8
|
-
@host
|
9
|
-
@port
|
10
|
-
|
11
|
-
@
|
7
|
+
def initialize host, port, organization=nil, topic=nil, id=nil
|
8
|
+
@host = host
|
9
|
+
@port = port
|
10
|
+
@organization = organization
|
11
|
+
@topic = topic
|
12
|
+
@id = id
|
12
13
|
end
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
when :fetch then
|
18
|
-
req = Net::HTTP::Get.new(@path)
|
19
|
-
when :create then
|
20
|
-
(req = Net::HTTP::Put.new(@path)).body = MultiJson.encode(ary)
|
21
|
-
when :update then
|
22
|
-
(req = Net::HTTP::Put.new(@path, {"x-method" => "PATCH"})).body = MultiJson.encode(ary)
|
23
|
-
when :remove then
|
24
|
-
(req = Net::HTTP::Delete.new(@path)).body = MultiJson.encode(ary)
|
25
|
-
end
|
26
|
-
req
|
15
|
+
def fetch organization=nil, topic=nil, id=nil
|
16
|
+
resp = execute_request(_req(:fetch, nil, organization, topic, id)) and
|
17
|
+
resp["contents"]
|
27
18
|
end
|
28
19
|
|
29
|
-
def
|
30
|
-
execute_request(_req(:
|
20
|
+
def update ary, organization=nil, topic=nil, id=nil
|
21
|
+
execute_request(_req(:update, ary, organization, topic, id))
|
31
22
|
end
|
32
23
|
|
33
|
-
def
|
34
|
-
execute_request(_req(:
|
24
|
+
def create ary, organization=nil, topic=nil, id=nil
|
25
|
+
execute_request(_req(:create, ary, organization, topic, id))
|
35
26
|
end
|
36
27
|
|
37
|
-
def
|
38
|
-
execute_request(_req(:
|
39
|
-
end
|
40
|
-
|
41
|
-
def remove ary
|
42
|
-
execute_request(_req(:remove, ary))
|
28
|
+
def remove ary, organization=nil, topic=nil, id=nil
|
29
|
+
execute_request(_req(:remove, ary, organization, topic, id))
|
43
30
|
end
|
44
31
|
|
45
32
|
|
@@ -52,6 +39,34 @@ class Vayacondios
|
|
52
39
|
result = MultiJson.decode(resp) unless resp.nil? or resp.empty?
|
53
40
|
(result.respond_to?(:has_key?) and result.has_key? "error") ? nil : result
|
54
41
|
end
|
42
|
+
|
43
|
+
def path organization, topic, id
|
44
|
+
if ((the_organization = (organization || @organization)).nil? ||
|
45
|
+
(the_topic = (topic || @topic )).nil? ||
|
46
|
+
(the_id = (id || @id )).nil?)
|
47
|
+
raise ArgumentError.new("must provide organization, topic, and id!")
|
48
|
+
end
|
49
|
+
|
50
|
+
['/v1', the_organization, 'itemset', the_topic, the_id].join("/")
|
51
|
+
end
|
52
|
+
|
53
|
+
# This is the only private method that is tested.
|
54
|
+
def _req type, ary=nil, organization=nil, topic=nil, id=nil
|
55
|
+
|
56
|
+
the_path = path(organization, topic, id)
|
57
|
+
headers = {"content-type" => "application/json"}
|
58
|
+
headers.merge!("x-method" => "PATCH") if type == :update
|
59
|
+
|
60
|
+
case type
|
61
|
+
when :fetch then Net::HTTP::Get
|
62
|
+
when :create then Net::HTTP::Put
|
63
|
+
when :update then Net::HTTP::Put
|
64
|
+
when :remove then Net::HTTP::Delete
|
65
|
+
else raise ArgumentError.new("invalid type: #{type}")
|
66
|
+
end.new(the_path, headers).tap do |req|
|
67
|
+
req.body = MultiJson.encode(contents: ary) unless type == :fetch
|
68
|
+
end
|
69
|
+
end
|
55
70
|
end
|
56
71
|
end
|
57
72
|
end
|
@@ -56,11 +56,34 @@ class Vayacondios
|
|
56
56
|
end
|
57
57
|
end
|
58
58
|
|
59
|
+
class CubeNotifier < Notifier
|
60
|
+
def initialize(options={})
|
61
|
+
@client = Vayacondios::CubeClient.receive(options)
|
62
|
+
end
|
63
|
+
def notify topic, cargo={}
|
64
|
+
prepped = prepare(cargo)
|
65
|
+
client.event(topic, prepped)
|
66
|
+
nil
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class ZabbixNotifier < Notifier
|
71
|
+
def initialize options={}
|
72
|
+
@client = Vayacondios::ZabbixClient.receive(options)
|
73
|
+
end
|
74
|
+
def notify(topic, cargo={})
|
75
|
+
prepped = prepare(cargo)
|
76
|
+
client.insert(topic, prepped)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
59
80
|
class NotifierFactory
|
60
81
|
def self.receive(attrs = {})
|
61
82
|
type = attrs[:type]
|
62
83
|
case type
|
63
84
|
when 'http' then HttpNotifier.new(attrs)
|
85
|
+
when 'cube' then CubeNotifier.new(attrs)
|
86
|
+
when 'zabbix' then ZabbixNotifier.new(attrs)
|
64
87
|
when 'log' then LogNotifier.new(attrs)
|
65
88
|
when 'none','null' then NullNotifier.new(attrs)
|
66
89
|
else
|
@@ -80,7 +103,7 @@ class Vayacondios
|
|
80
103
|
def self.included klass
|
81
104
|
if klass.ancestors.include? Gorillib::Model
|
82
105
|
klass.class_eval do
|
83
|
-
field :notifier, Vayacondios::NotifierFactory, default: Vayacondios.default_notifier
|
106
|
+
field :notifier, Vayacondios::NotifierFactory, default: Vayacondios.default_notifier, :doc => "Notifier used to notify out of band data"
|
84
107
|
|
85
108
|
def receive_notifier params
|
86
109
|
params.merge!(log: try(:log)) if params[:type] == 'log'
|
@@ -0,0 +1,148 @@
|
|
1
|
+
require 'socket'
|
2
|
+
|
3
|
+
class Vayacondios
|
4
|
+
|
5
|
+
# Used for sending events to a Zabbix server.
|
6
|
+
#
|
7
|
+
# An 'event' from Vayacondios' perspective is an arbitrary Hash.
|
8
|
+
#
|
9
|
+
# An 'event' from Zabbix's perspective is a tuple of values:
|
10
|
+
#
|
11
|
+
# * time
|
12
|
+
# * host
|
13
|
+
# * key
|
14
|
+
# * value
|
15
|
+
#
|
16
|
+
# This client will accept a Vayacondios event and internally
|
17
|
+
# translate it into a set of Zabbix events.
|
18
|
+
#
|
19
|
+
# @example A CPU monitoring notification
|
20
|
+
#
|
21
|
+
# notify "foo-server.example.com", cpu: {
|
22
|
+
# util: {
|
23
|
+
# user: 0.20,
|
24
|
+
# idle: 0.70,
|
25
|
+
# sys: 0.10
|
26
|
+
# },
|
27
|
+
# load: 1.3
|
28
|
+
# }
|
29
|
+
#
|
30
|
+
# would get turned into the following events when written to Zabbix:
|
31
|
+
#
|
32
|
+
# @example The CPU monitoring notification translated to Zabbix events
|
33
|
+
#
|
34
|
+
# [
|
35
|
+
# { host: "foo-server.example.com", key: "cpu.util.user", value: 0.20 }
|
36
|
+
# { host: "foo-server.example.com", key: "cpu.util.idle", value: 0.70 },
|
37
|
+
# { host: "foo-server.example.com", key: "cpu.util.sys", value: 0.10 },
|
38
|
+
# { host: "foo-server.example.com", key: "cpu.load", value: 1.3 }
|
39
|
+
# ]
|
40
|
+
#
|
41
|
+
# Zabbix will interpret the time as the time it receives each event.
|
42
|
+
#
|
43
|
+
# The following links provide details on the protocol used by Zabbix
|
44
|
+
# to receive events:
|
45
|
+
#
|
46
|
+
# * https://www.zabbix.com/forum/showthread.php?t=20047&highlight=sender
|
47
|
+
# * https://gist.github.com/1170577
|
48
|
+
# * http://spin.atomicobject.com/2012/10/30/collecting-metrics-from-ruby-processes-using-zabbix-trappers/?utm_source=rubyflow&utm_medium=ao&utm_campaign=collecting-metrics-zabix
|
49
|
+
class ZabbixClient
|
50
|
+
include Gorillib::Builder
|
51
|
+
|
52
|
+
attr_accessor :socket
|
53
|
+
|
54
|
+
field :host, String, :default => 'localhost', :doc => "Host for the Zabbix server"
|
55
|
+
field :port, Integer, :default => 10051, :doc => "Port for the Zabbix server"
|
56
|
+
|
57
|
+
# Insert events to a Zabbix server.
|
58
|
+
#
|
59
|
+
# The `topic` will be used as the name of the Zabbix host to
|
60
|
+
# associate event data to.
|
61
|
+
#
|
62
|
+
# As per the documentation for the [Zabbix sender
|
63
|
+
# protocol](https://www.zabbix.com/wiki/doc/tech/proto/zabbixsenderprotocol),
|
64
|
+
# a new TCP connection will be created for each event.
|
65
|
+
#
|
66
|
+
# @param [String] topic
|
67
|
+
# @param [Hash] cargo
|
68
|
+
# Array<Hash>] text
|
69
|
+
def insert topic, cargo={}
|
70
|
+
self.socket = TCPSocket.new(host, port)
|
71
|
+
send_request(topic, cargo)
|
72
|
+
handle_response
|
73
|
+
self.socket.close
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
# :nodoc
|
79
|
+
def send_request topic, cargo
|
80
|
+
socket.write(payload(topic, cargo))
|
81
|
+
end
|
82
|
+
|
83
|
+
# :nodoc
|
84
|
+
def handle_response
|
85
|
+
header = socket.recv(5)
|
86
|
+
if header == "ZBXD\1"
|
87
|
+
data_header = socket.recv(8)
|
88
|
+
length = data_header[0,4].unpack("i")[0]
|
89
|
+
response = MultiJson.load(socket.recv(length))
|
90
|
+
puts response["info"]
|
91
|
+
else
|
92
|
+
puts "Invalid response: #{header}"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# :nodoc
|
97
|
+
def payload topic, cargo={}
|
98
|
+
body = body_for(topic, cargo)
|
99
|
+
header_for(body) + body
|
100
|
+
end
|
101
|
+
|
102
|
+
# :nodoc
|
103
|
+
def body_for topic, cargo={}
|
104
|
+
MultiJson.dump({request: "sender data", data: zabbix_events_from(topic, cargo) })
|
105
|
+
end
|
106
|
+
|
107
|
+
# :nodoc
|
108
|
+
def header_for body
|
109
|
+
length = body.bytesize
|
110
|
+
"ZBXD\1".encode("ascii") + [length].pack("i") + "\x00\x00\x00\x00"
|
111
|
+
end
|
112
|
+
|
113
|
+
# :nodoc
|
114
|
+
def zabbix_events_from topic, cargo, scope=''
|
115
|
+
events = []
|
116
|
+
case cargo
|
117
|
+
when Hash
|
118
|
+
cargo.each_pair do |key, value|
|
119
|
+
events += zabbix_events_from(topic, value, new_scope(scope, key))
|
120
|
+
end
|
121
|
+
when Array
|
122
|
+
cargo.each_with_index do |item, index|
|
123
|
+
events += zabbix_events_from(topic, item, new_scope(scope, index))
|
124
|
+
end
|
125
|
+
else
|
126
|
+
events << event_body(topic, scope, cargo)
|
127
|
+
end
|
128
|
+
events
|
129
|
+
end
|
130
|
+
|
131
|
+
# :nodoc
|
132
|
+
def new_scope(current_scope, new_scope)
|
133
|
+
[current_scope, new_scope].map(&:to_s).reject(&:empty?).join('.')
|
134
|
+
end
|
135
|
+
|
136
|
+
# :nodoc
|
137
|
+
def event_body topic, scope, cargo
|
138
|
+
value = case cargo
|
139
|
+
when Hash then cargo[:value]
|
140
|
+
when Array then cargo.first
|
141
|
+
else cargo
|
142
|
+
end
|
143
|
+
{ host: topic, key: scope, value: value }
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
data/spec/client/itemset_spec.rb
CHANGED
@@ -16,28 +16,28 @@ describe Vayacondios::Client::ItemSet do
|
|
16
16
|
itemset = Vayacondios::Client::ItemSet.new("foohost", 9999, "fooorg", "footopic", "fooid")
|
17
17
|
ary = ["foo", "bar", "baz"]
|
18
18
|
|
19
|
-
#
|
19
|
+
# testing internals here to avoid shimming up HTTP libraries.
|
20
20
|
|
21
21
|
it "generates a put request without a patch header when asked to create" do
|
22
|
-
req = itemset._req
|
22
|
+
req = itemset.instance_eval{_req(:create, ary)}
|
23
23
|
|
24
24
|
req.method.should eql('PUT')
|
25
|
-
req.body.should eql(ary
|
25
|
+
req.body.should eql(MultiJson.encode(contents: ary))
|
26
26
|
req.path.should eql('/v1/fooorg/itemset/footopic/fooid')
|
27
27
|
req.each_header.to_a.should_not include(["x_method", "PATCH"])
|
28
28
|
end
|
29
29
|
|
30
30
|
it "generates a put request with a patch header when asked to update" do
|
31
|
-
req = itemset._req
|
31
|
+
req = itemset.instance_eval{_req(:update, ary)}
|
32
32
|
|
33
33
|
req.method.should eql('PUT')
|
34
|
-
req.body.should eql(ary
|
34
|
+
req.body.should eql(MultiJson.encode(contents: ary))
|
35
35
|
req.path.should eql('/v1/fooorg/itemset/footopic/fooid')
|
36
36
|
req.each_header.to_a.should include(["x-method", "PATCH"])
|
37
37
|
end
|
38
38
|
|
39
39
|
it "generates a get request when asked to fetch" do
|
40
|
-
req = itemset._req
|
40
|
+
req = itemset.instance_eval{_req(:fetch)}
|
41
41
|
|
42
42
|
req.method.should eql('GET')
|
43
43
|
req.body.should be_nil
|
@@ -45,10 +45,10 @@ describe Vayacondios::Client::ItemSet do
|
|
45
45
|
end
|
46
46
|
|
47
47
|
it "generates a delete request when asked to remove" do
|
48
|
-
req = itemset._req
|
48
|
+
req = itemset.instance_eval{_req(:remove, ary)}
|
49
49
|
|
50
50
|
req.method.should eql('DELETE')
|
51
|
-
req.body.should eql(ary
|
51
|
+
req.body.should eql(MultiJson.encode(contents: ary))
|
52
52
|
req.path.should eql('/v1/fooorg/itemset/footopic/fooid')
|
53
53
|
end
|
54
54
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: vayacondios-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.6
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -12,7 +12,7 @@ authors:
|
|
12
12
|
autorequire:
|
13
13
|
bindir: bin
|
14
14
|
cert_chain: []
|
15
|
-
date:
|
15
|
+
date: 2013-03-06 00:00:00.000000000 Z
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
18
18
|
name: configliere
|
@@ -35,23 +35,23 @@ dependencies:
|
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
36
36
|
none: false
|
37
37
|
requirements:
|
38
|
-
- -
|
38
|
+
- - ! '>='
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: 1.3.6
|
41
41
|
type: :runtime
|
42
42
|
prerelease: false
|
43
43
|
version_requirements: !ruby/object:Gem::Requirement
|
44
44
|
none: false
|
45
45
|
requirements:
|
46
|
-
- -
|
46
|
+
- - ! '>='
|
47
47
|
- !ruby/object:Gem::Version
|
48
|
-
version:
|
48
|
+
version: 1.3.6
|
49
49
|
- !ruby/object:Gem::Dependency
|
50
50
|
name: gorillib
|
51
51
|
requirement: !ruby/object:Gem::Requirement
|
52
52
|
none: false
|
53
53
|
requirements:
|
54
|
-
- -
|
54
|
+
- - ! '>='
|
55
55
|
- !ruby/object:Gem::Version
|
56
56
|
version: 0.4.2
|
57
57
|
type: :runtime
|
@@ -59,7 +59,7 @@ dependencies:
|
|
59
59
|
version_requirements: !ruby/object:Gem::Requirement
|
60
60
|
none: false
|
61
61
|
requirements:
|
62
|
-
- -
|
62
|
+
- - ! '>='
|
63
63
|
- !ruby/object:Gem::Version
|
64
64
|
version: 0.4.2
|
65
65
|
- !ruby/object:Gem::Dependency
|
@@ -119,9 +119,11 @@ extra_rdoc_files: []
|
|
119
119
|
files:
|
120
120
|
- lib/vayacondios-client.rb
|
121
121
|
- lib/vayacondios/client/configliere.rb
|
122
|
+
- lib/vayacondios/client/cube_client.rb
|
122
123
|
- lib/vayacondios/client/http_client.rb
|
123
124
|
- lib/vayacondios/client/itemset.rb
|
124
125
|
- lib/vayacondios/client/notifier.rb
|
126
|
+
- lib/vayacondios/client/zabbix_client.rb
|
125
127
|
- spec/client/itemset_spec.rb
|
126
128
|
- spec/client/notifier_spec.rb
|
127
129
|
homepage: https://github.com/infochimps-labs/vayacondios
|
@@ -138,7 +140,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
138
140
|
version: '0'
|
139
141
|
segments:
|
140
142
|
- 0
|
141
|
-
hash: -
|
143
|
+
hash: -1918215973120450598
|
142
144
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
143
145
|
none: false
|
144
146
|
requirements:
|
@@ -147,10 +149,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
147
149
|
version: '0'
|
148
150
|
segments:
|
149
151
|
- 0
|
150
|
-
hash: -
|
152
|
+
hash: -1918215973120450598
|
151
153
|
requirements: []
|
152
154
|
rubyforge_project:
|
153
|
-
rubygems_version: 1.8.
|
155
|
+
rubygems_version: 1.8.25
|
154
156
|
signing_key:
|
155
157
|
specification_version: 3
|
156
158
|
summary: Data goes in. The right thing happens
|