vayacondios-server 0.1.2 → 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. data/Gemfile +1 -0
  2. data/app/http_shim.rb +1 -5
  3. data/lib/vayacondios-client.rb +2 -0
  4. data/lib/vayacondios-server.rb +1 -0
  5. data/lib/vayacondios/client/cube_client.rb +39 -0
  6. data/lib/vayacondios/client/itemset.rb +43 -28
  7. data/lib/vayacondios/client/notifier.rb +24 -1
  8. data/lib/vayacondios/client/zabbix_client.rb +148 -0
  9. data/lib/vayacondios/server/handlers/itemset_handler.rb +0 -1
  10. data/lib/vayacondios/server/model/itemset_document.rb +8 -4
  11. data/lib/vayacondios/server/rack/assume_json.rb +13 -0
  12. data/lib/vayacondios/server/rack/extract_methods.rb +11 -1
  13. data/lib/vayacondios/version.rb +1 -1
  14. data/pom.xml +97 -0
  15. data/scripts/hadoop_monitor/configurable.rb +1 -1
  16. data/scripts/hadoop_monitor/hadoop_attempt_scraper.rb +6 -3
  17. data/scripts/hadoop_monitor/hadoop_client.rb +20 -19
  18. data/scripts/hadoop_monitor/hadoop_monitor.rb +3 -3
  19. data/scripts/hadoop_monitor/hadoopable.rb +3 -3
  20. data/scripts/hadoop_monitor/machine_monitor.rb +2 -2
  21. data/spec/client/itemset_spec.rb +8 -8
  22. data/spec/server/itemset_spec.rb +4 -4
  23. data/src/main/java/com/infochimps/util/CurrentClass.java +26 -0
  24. data/src/main/java/com/infochimps/util/DebugUtil.java +38 -0
  25. data/src/main/java/com/infochimps/util/HttpHelper.java +112 -0
  26. data/src/main/java/com/infochimps/vayacondios/ItemSets.java +456 -0
  27. data/src/main/java/com/infochimps/vayacondios/Organization.java +49 -0
  28. data/src/main/java/com/infochimps/vayacondios/PathBuilder.java +13 -0
  29. data/src/main/java/com/infochimps/vayacondios/VCDIntegrationTest.java +68 -0
  30. data/src/main/java/com/infochimps/vayacondios/VayacondiosClient.java +88 -0
  31. data/vayacondios-client.gemspec +2 -2
  32. data/vayacondios-server.gemspec +4 -2
  33. metadata +37 -9
data/Gemfile CHANGED
@@ -9,4 +9,5 @@ group :hadoop_monitor do
9
9
  gem 'json'
10
10
  gem 'nokogiri', '~> 1.5'
11
11
  gem 'nibbler', '~> 1.3'
12
+ gem "swineherd-fs", "~> 0.0.3"
12
13
  end
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
@@ -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'
@@ -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 = host
9
- @port = port
10
-
11
- @path = "/v1/#{organization}/itemset/#{topic}/#{id}"
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
- # Exposed for testing.
15
- def _req type, ary = nil
16
- case type
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 fetch
30
- execute_request(_req(:fetch))
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 update ary
34
- execute_request(_req(:update, ary))
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 create ary
38
- execute_request(_req(:create, ary))
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
+
@@ -41,7 +41,6 @@ class Vayacondios
41
41
  validate_options options
42
42
 
43
43
  existing_document = ItemsetDocument.find(@mongo, options)
44
- puts "destroy existing"
45
44
  existing_document.destroy(document)
46
45
  end
47
46
 
@@ -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, :body, :id
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'] # should be items
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
@@ -0,0 +1,13 @@
1
+ class Vayacondios
2
+ module Rack
3
+ class AssumeJSON
4
+ include Goliath::Rack::AsyncMiddleware
5
+
6
+ def call(env)
7
+ env['CONTENT_TYPE'] =
8
+ 'application/json' unless env.has_key? 'CONTENT_TYPE'
9
+ super env
10
+ end
11
+ end
12
+ end
13
+ 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
- (env['HTTP_X_METHOD'] && env['HTTP_X_METHOD'].upcase == 'PATCH') ? :patch : :update
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