vayacondios-server 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/Gemfile +1 -0
- data/app/http_shim.rb +1 -5
- data/lib/vayacondios-client.rb +2 -0
- data/lib/vayacondios-server.rb +1 -0
- data/lib/vayacondios/client/cube_client.rb +39 -0
- data/lib/vayacondios/client/itemset.rb +43 -28
- data/lib/vayacondios/client/notifier.rb +24 -1
- data/lib/vayacondios/client/zabbix_client.rb +148 -0
- data/lib/vayacondios/server/handlers/itemset_handler.rb +0 -1
- data/lib/vayacondios/server/model/itemset_document.rb +8 -4
- data/lib/vayacondios/server/rack/assume_json.rb +13 -0
- data/lib/vayacondios/server/rack/extract_methods.rb +11 -1
- data/lib/vayacondios/version.rb +1 -1
- data/pom.xml +97 -0
- data/scripts/hadoop_monitor/configurable.rb +1 -1
- data/scripts/hadoop_monitor/hadoop_attempt_scraper.rb +6 -3
- data/scripts/hadoop_monitor/hadoop_client.rb +20 -19
- data/scripts/hadoop_monitor/hadoop_monitor.rb +3 -3
- data/scripts/hadoop_monitor/hadoopable.rb +3 -3
- data/scripts/hadoop_monitor/machine_monitor.rb +2 -2
- data/spec/client/itemset_spec.rb +8 -8
- data/spec/server/itemset_spec.rb +4 -4
- data/src/main/java/com/infochimps/util/CurrentClass.java +26 -0
- data/src/main/java/com/infochimps/util/DebugUtil.java +38 -0
- data/src/main/java/com/infochimps/util/HttpHelper.java +112 -0
- data/src/main/java/com/infochimps/vayacondios/ItemSets.java +456 -0
- data/src/main/java/com/infochimps/vayacondios/Organization.java +49 -0
- data/src/main/java/com/infochimps/vayacondios/PathBuilder.java +13 -0
- data/src/main/java/com/infochimps/vayacondios/VCDIntegrationTest.java +68 -0
- data/src/main/java/com/infochimps/vayacondios/VayacondiosClient.java +88 -0
- data/vayacondios-client.gemspec +2 -2
- data/vayacondios-server.gemspec +4 -2
- metadata +37 -9
data/Gemfile
CHANGED
data/app/http_shim.rb
CHANGED
@@ -3,12 +3,8 @@
|
|
3
3
|
require 'vayacondios-server'
|
4
4
|
|
5
5
|
class HttpShim < Goliath::API
|
6
|
+
use Vayacondios::Rack::AssumeJSON # assume application/json content type
|
6
7
|
use Goliath::Rack::Tracer, 'X-Tracer' # log trace statistics
|
7
|
-
# /v1/infochimps/itemset/foo/1 -d '["foo","bar","baz"]'
|
8
|
-
# use Vayacondios::Rack::Versionator
|
9
|
-
# /v1/infochimps/itemset/foo/1 -d '{"items":["foo","bar","baz"]}' ... env[:vayacondios_version] = 1
|
10
|
-
# post_process()
|
11
|
-
# extract results = results[:results] from body for legacy
|
12
8
|
use Goliath::Rack::Params # parse query string and message body into params hash
|
13
9
|
use Goliath::Rack::Validation::RequestMethod, %w[GET PUT PATCH DELETE] # only allow these methods
|
14
10
|
use Vayacondios::Rack::ExtractMethods # interpolate GET, PUT into :create, :update, etc
|
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'
|
data/lib/vayacondios-server.rb
CHANGED
@@ -22,6 +22,7 @@ require 'vayacondios/server/handlers/config_handler'
|
|
22
22
|
require 'vayacondios/server/handlers/event_handler'
|
23
23
|
require 'vayacondios/server/handlers/itemset_handler'
|
24
24
|
|
25
|
+
require 'vayacondios/server/rack/assume_json'
|
25
26
|
require 'vayacondios/server/rack/extract_methods'
|
26
27
|
require 'vayacondios/server/rack/path'
|
27
28
|
require 'vayacondios/server/rack/path_validation'
|
@@ -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
|
+
|
@@ -10,7 +10,7 @@ require 'vayacondios/server/model/document'
|
|
10
10
|
# work while Goliath is in a streaming context.
|
11
11
|
|
12
12
|
class Vayacondios::ItemsetDocument < Vayacondios::Document
|
13
|
-
attr_reader :organization, :topic, :
|
13
|
+
attr_reader :organization, :topic, :id
|
14
14
|
|
15
15
|
def initialize(mongodb, options = {})
|
16
16
|
super options
|
@@ -33,13 +33,17 @@ class Vayacondios::ItemsetDocument < Vayacondios::Document
|
|
33
33
|
self.new(mongodb, options).find
|
34
34
|
end
|
35
35
|
|
36
|
+
def body
|
37
|
+
{contents: @body}
|
38
|
+
end
|
39
|
+
|
36
40
|
def find
|
37
41
|
result = @collection.find_one({_id: @id})
|
38
42
|
|
39
43
|
if result
|
40
44
|
result.delete("_id")
|
41
45
|
@body = result["d"]
|
42
|
-
self
|
46
|
+
self
|
43
47
|
else
|
44
48
|
nil
|
45
49
|
end
|
@@ -48,7 +52,7 @@ class Vayacondios::ItemsetDocument < Vayacondios::Document
|
|
48
52
|
def update(document)
|
49
53
|
raise Vayacondios::Error::BadRequest.new unless document.is_a?(Hash)
|
50
54
|
|
51
|
-
@body = document['contents']
|
55
|
+
@body = document['contents']
|
52
56
|
|
53
57
|
|
54
58
|
@collection.update({:_id => @id}, {:_id => @id, 'd' => @body }, {upsert: true})
|
@@ -61,7 +65,7 @@ class Vayacondios::ItemsetDocument < Vayacondios::Document
|
|
61
65
|
|
62
66
|
# Merge ourselves
|
63
67
|
if @body
|
64
|
-
@body = body + document['contents']
|
68
|
+
@body = @body + document['contents']
|
65
69
|
else
|
66
70
|
@body = document['contents']
|
67
71
|
end
|
@@ -12,7 +12,17 @@ class Vayacondios
|
|
12
12
|
return unless env['REQUEST_METHOD']
|
13
13
|
case env['REQUEST_METHOD'].upcase
|
14
14
|
when 'PUT' then
|
15
|
-
|
15
|
+
if env.has_key? 'HTTP_X_METHOD'
|
16
|
+
if env['HTTP_X_METHOD'].upcase == 'PATCH'
|
17
|
+
:patch
|
18
|
+
elsif env['HTTP_X_METHOD'].upcase == 'DELETE'
|
19
|
+
:delete
|
20
|
+
else
|
21
|
+
:update
|
22
|
+
end
|
23
|
+
else
|
24
|
+
:update
|
25
|
+
end
|
16
26
|
when 'GET' then :show
|
17
27
|
when 'POST' then :create
|
18
28
|
when 'PATCH' then :patch
|