nfagent 0.9.0 → 0.9.5

Sign up to get free protection for your applications and to get access to all the features.
data/Manifest.txt CHANGED
@@ -7,8 +7,13 @@ lib/nfagent/encoder.rb
7
7
  lib/nfagent/server.rb
8
8
  lib/nfagent/chunk_handler.rb
9
9
  lib/nfagent/log.rb
10
+ lib/nfagent/info.rb
10
11
  lib/nfagent/event.rb
12
+ lib/nfagent/payload.rb
13
+ lib/nfagent/poller.rb
11
14
  lib/nfagent/chunk.rb
15
+ lib/nfagent/client.rb
16
+ lib/nfagent/client_response.rb
12
17
  lib/nfagent/config.rb
13
18
  lib/nfagent/tail.rb
14
19
  lib/nfagent/submitter.rb
data/Rakefile CHANGED
@@ -11,7 +11,7 @@ $hoe = Hoe.spec('nfagent') do |p|
11
11
  p.post_install_message = 'PostInstall.txt'
12
12
  p.rubyforge_name = p.name
13
13
  p.extra_deps = [
14
- ['svutil','>= 0.0.3'], ['eventmachine', '>= 0.12.8']
14
+ ['svutil','>= 0.0.6'], ['eventmachine', '>= 0.12.8']
15
15
  ]
16
16
  p.extra_dev_deps = [
17
17
  ['newgem', ">= #{::Newgem::VERSION}"]
data/lib/nfagent.rb CHANGED
@@ -4,25 +4,31 @@ $:.unshift(File.dirname(__FILE__)) unless
4
4
  require 'rubygems'
5
5
  require 'svutil'
6
6
 
7
- require 'thread'
8
7
  require 'fileutils'
9
8
  require 'logger'
10
9
  require 'pp'
11
10
  require 'uri'
12
11
  require 'net/http'
13
12
  require 'eventmachine'
13
+ require 'em/timers'
14
+ require 'rbconfig'
14
15
 
15
16
  require 'nfagent/chunk'
17
+ require 'nfagent/client'
18
+ require 'nfagent/client_response'
16
19
  require 'nfagent/chunk_handler'
17
20
  require 'nfagent/submitter'
18
21
  require 'nfagent/encoder'
19
22
  require 'nfagent/config'
20
23
  require 'nfagent/log'
24
+ require 'nfagent/info'
25
+ require 'nfagent/payload'
26
+ require 'nfagent/poller'
21
27
  require 'nfagent/tail'
22
28
  require 'nfagent/event'
23
29
  require 'nfagent/server'
24
30
  require 'nfagent/cli'
25
31
 
26
32
  module NFAgent
27
- VERSION = '0.9.0'
33
+ VERSION = '0.9.5'
28
34
  end
data/lib/nfagent/chunk.rb CHANGED
@@ -26,19 +26,16 @@ module NFAgent
26
26
  (Time.now - @created_at > ::DEFAULT_TIME_OUT) && !@array.empty?
27
27
  end
28
28
 
29
+ # TODO: Is this the right place for compression, encoding and check summing? Perhaps it should go into the submitter to that it can be deferred
29
30
  def dump
30
- payload = Encoder.encode64url(Zlib::Deflate.deflate(@array.join("\n"), Zlib::BEST_COMPRESSION))
31
- checksum = Digest::SHA1.hexdigest(payload)
32
- [ payload, checksum ]
31
+ Payload.new do |payload|
32
+ payload.data = Encoder.encode64url(Zlib::Deflate.deflate(@array.join("\n"), Zlib::BEST_COMPRESSION))
33
+ payload.checksum = Digest::SHA1.hexdigest(payload.data)
34
+ end
33
35
  end
34
36
 
35
37
  def clear
36
38
  @array.clear
37
39
  end
38
-
39
- def submit_to_server
40
- payload, checksum = dump
41
- @submitter.submit(payload, checksum)
42
- end
43
40
  end
44
41
  end
@@ -1,57 +1,36 @@
1
1
  module NFAgent
2
2
  class ChunkHandler
3
3
 
4
+ # TODO: Rename this to Controller later
4
5
  def initialize(chunk_size = 500)
5
- @mutex = Mutex.new
6
- @chunk_size = chunk_size
7
- make_new_chunk
6
+ @chunk = Chunk.new(chunk_size)
8
7
  end
9
8
 
10
- def submit(line)
9
+ def append(line)
11
10
  # if current day is > day of last entry on current_chunk
12
11
  # then submit and reset the chunk before adding the line
13
12
  current_day = Time.now.day
14
- if current_day != current_chunk.created_at.day
13
+ if current_day != @chunk.created_at.day
15
14
  Log.info("Expiring chunk due to date rollover")
16
15
  reset_chunk
17
16
  end
18
- current_chunk << line
17
+ @chunk << line
19
18
  end
20
19
 
21
- def periodically_check_expired
22
- Thread.new do
23
- loop do
24
- check_full_or_expired
25
- sleep 5
26
- end
20
+ def check_full_or_expired
21
+ if @chunk.full? || @chunk.expired?
22
+ reset_chunk
27
23
  end
28
24
  end
29
25
 
30
26
  private
31
- def check_full_or_expired
32
- if current_chunk.full? || current_chunk.expired?
33
- reset_chunk
34
- end
35
- end
36
-
37
27
  def reset_chunk
38
- outgoing_chunk = current_chunk
39
- make_new_chunk
40
- Thread.new do
41
- outgoing_chunk.submit_to_server
42
- end
43
- end
44
-
45
- def make_new_chunk
46
- @mutex.synchronize do
47
- @current_chunk = Chunk.new(@chunk_size)
48
- end
49
- end
50
-
51
- def current_chunk
52
- @mutex.synchronize do
53
- return @current_chunk
54
- end
28
+ submitter = Submitter.new(@chunk.dump)
29
+ submitter.errback { |payload|
30
+ payload.write_to_disk(Config.dump_dir)
31
+ }
32
+ @chunk.clear
33
+ submitter.perform
55
34
  end
56
35
  end
57
36
  end
@@ -0,0 +1,21 @@
1
+ module NFAgent
2
+ class Client
3
+ SERVICE_HOST = "collector.service.netfox.com"
4
+
5
+ def self.post(end_point, data_hash)
6
+ proxy_class = Net::HTTP::Proxy(Config.proxy.host, Config.proxy.port, Config.proxy.user, Config.proxy.password)
7
+ # TODO: Enable SSL
8
+ proxy_class.start(SERVICE_HOST, 80) do |http|
9
+ req = Net::HTTP::Post.new("/#{end_point}")
10
+ req.set_form_data(data_hash.merge("key" => Config.client_key))
11
+ ClientResponse.new do |resp|
12
+ resp.response, resp.message = http.request(req)
13
+ end
14
+ end
15
+ rescue
16
+ ClientResponse.new do |resp|
17
+ resp.message = $!
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,13 @@
1
+ module NFAgent
2
+ class ClientResponse
3
+ attr_accessor :response, :message
4
+
5
+ def initialize
6
+ yield self if block_given?
7
+ end
8
+
9
+ def ok?
10
+ Net::HTTPOK === response
11
+ end
12
+ end
13
+ end
@@ -3,6 +3,8 @@ module NFAgent
3
3
  attr_accessor :host, :port, :user, :password
4
4
  end
5
5
 
6
+ RBConfig = Config
7
+
6
8
  class Config < SVUtil::Config
7
9
  @@proxy = Proxy.new
8
10
 
data/lib/nfagent/event.rb CHANGED
@@ -1,7 +1,8 @@
1
1
  module NFAgent
2
2
  class Event < EventMachine::Connection
3
- def initialize(chunk_handler)
3
+ def initialize(chunk_handler, poller)
4
4
  @handler = chunk_handler
5
+ @poller = poller
5
6
  end
6
7
 
7
8
  def post_init
@@ -10,7 +11,7 @@ module NFAgent
10
11
 
11
12
  def receive_data(data)
12
13
  if data && data.length > 2
13
- @handler.submit(data)
14
+ @handler.append(data)
14
15
  end
15
16
  send_data('OK')
16
17
  end
@@ -0,0 +1,26 @@
1
+ module NFAgent
2
+ class Info
3
+ attr_accessor :last_proxy_connection
4
+
5
+ def hostname
6
+ Socket.gethostname
7
+ end
8
+
9
+ def version
10
+ NFAgent::VERSION
11
+ end
12
+
13
+ def host_string
14
+ RBConfig::CONFIG['host']
15
+ end
16
+
17
+ def to_hash
18
+ {
19
+ :hostname => hostname,
20
+ :version => version,
21
+ :host_string => host_string,
22
+ :last_proxy_connection => last_proxy_connection
23
+ }
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,70 @@
1
+
2
+ module NFAgent
3
+ class Payload < Struct.new(:data, :checksum, :filename)
4
+ def initialize
5
+ yield self
6
+ end
7
+
8
+ def attempt
9
+ @attempt || 1
10
+ end
11
+
12
+ def attempt=(value)
13
+ @attempt = value.to_i
14
+ end
15
+
16
+ def increment_attempt!
17
+ @attempt ||= 1
18
+ @attempt += 1
19
+ end
20
+
21
+ def size
22
+ (self.data || "").size + 1
23
+ end
24
+
25
+ def write_to_disk(directory)
26
+ File.open(File.join(directory, "#{self.checksum}-#{self.attempt}"), "w") do |file|
27
+ file << self.data
28
+ end
29
+ end
30
+
31
+ def lock
32
+ return if locked?
33
+ FileUtils.touch(lockfile) if filename
34
+ yield
35
+ FileUtils.rm_f(lockfile) if filename
36
+ end
37
+
38
+ def locked?
39
+ filename && File.exists?(lockfile)
40
+ end
41
+
42
+ def self.read_from_file(filename)
43
+ # Ensure the file is only relative
44
+ filename = File.basename(filename)
45
+ self.new do |payload|
46
+ payload.filename = filename
47
+ payload.checksum, payload.attempt = filename.split("-")
48
+ payload.data = ""
49
+ ref = File.join(Config.dump_dir, filename)
50
+ File.open(ref, "r") do |file|
51
+ payload.data << file.read
52
+ end
53
+ end
54
+ end
55
+
56
+ def destroy!
57
+ FileUtils.rm_f(File.join(Config.dump_dir, self.filename)) if self.filename
58
+ end
59
+
60
+ def try_again_later
61
+ # TODO: Move the file to a new name with a later timetamp
62
+ FileUtils.mv(File.join(Config.dump_dir, self.filename), File.join(Config.dump_dir, "#{self.checksum}-#{self.attempt}"))
63
+ end
64
+
65
+ private
66
+ def lockfile
67
+ File.join(Config.dump_dir, "#{filename}.lock") if filename
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,18 @@
1
+ module NFAgent
2
+ class Poller
3
+ include EM::Deferrable
4
+
5
+ def initialize
6
+ @info = Info.new
7
+ end
8
+
9
+ def send_heartbeat
10
+ payload = @info.to_hash
11
+ Log.info("Polling: #{payload.inspect}")
12
+ response = Client.post(:poller, payload)
13
+ if !response.ok?
14
+ Log.error("Poll Failed: #{response.message}")
15
+ end
16
+ end
17
+ end
18
+ end
@@ -1,14 +1,26 @@
1
1
  module NFAgent
2
2
  class Server
3
- def initialize
3
+ def run
4
4
  Log.info("Starting up")
5
- Submitter.start_resubmission_thread
6
5
  chunk_handler = ChunkHandler.new
7
- chunk_handler.periodically_check_expired
6
+ poller = Poller.new
8
7
 
9
8
  EM.run {
10
- EM.start_server "0.0.0.0", 10000, Event, chunk_handler
9
+ EM::PeriodicTimer.new(5) do
10
+ chunk_handler.check_full_or_expired
11
+ end
12
+ EM::PeriodicTimer.new(60) do
13
+ Submitter.resubmit_failed_dumps
14
+ end
15
+ EM::PeriodicTimer.new(120) do
16
+ poller.send_heartbeat
17
+ end
18
+ EM.start_server "0.0.0.0", 10000, Event, chunk_handler, poller
11
19
  }
12
20
  end
21
+
22
+ def shutdown
23
+ EM::stop_event_loop
24
+ end
13
25
  end
14
26
  end
@@ -1,59 +1,47 @@
1
1
  module NFAgent
2
2
  class Submitter
3
+ include EM::Deferrable
3
4
  attr_accessor :host
4
5
 
5
- def initialize(key)
6
- @service_host = "collector.service.netfox.com"
7
- @key = key
6
+ def initialize(payload)
7
+ @payload = payload
8
8
  end
9
9
 
10
- def submit(payload, checksum, options = {})
11
- attempt = options[:attempt] || 1
12
- raise "Delaying attempt" unless [ 1, 2, 4, 8, 16 ].include?(attempt)
13
- puts "Submitting Payload: #{checksum}, Attempt #{attempt}, (#{payload.size + 1} bytes)"
14
- proxy_class = Net::HTTP::Proxy(Config.proxy.host, Config.proxy.port, Config.proxy.user, Config.proxy.password)
15
- proxy_class.start(@service_host, 80) do |http|
16
- req = Net::HTTP::Post.new('/collector')
17
- req.set_form_data({ "payload" => payload, "checksum" => checksum, "key" => @key })
18
- response, body = http.request(req)
19
- raise body unless Net::HTTPOK === response
20
- end
21
- rescue
22
- Log.error "Submission Failed: #{$!}"
23
- write_failed_dump(payload, checksum, attempt)
24
- end
25
-
26
- def write_failed_dump(payload, checksum, attempt)
27
- File.open(File.join(Config.dump_dir, "#{checksum}-#{attempt}"), "w") do |file|
28
- file << payload
29
- end
30
- end
31
-
32
- def self.start_resubmission_thread
33
- Thread.new do
34
- loop do
35
- self.resubmit_failed_dumps
36
- sleep 60
10
+ def perform
11
+ @payload.increment_attempt!
12
+ fail(@payload) unless [ 1, 2, 4, 8, 16 ].include?(@payload.attempt)
13
+ Log.info "Submitting Payload: #{@payload.checksum}, Attempt #{@payload.attempt}, (#{@payload.size} bytes)"
14
+ @payload.lock do
15
+ response = Client.post(:collector, "payload" => @payload.data, "checksum" => @payload.checksum)
16
+ if response.ok?
17
+ succeed(@payload)
18
+ else
19
+ Log.error "Submission Failed: #{response.message}"
20
+ fail(@payload)
37
21
  end
38
22
  end
39
23
  end
40
24
 
25
+ # TODO: Change attempt logic
26
+ # Add the next timestamp for when submission should be attenpted again to the end of the filename
41
27
  def self.resubmit_failed_dumps
42
28
  submitter = Submitter.new(Config.client_key)
43
29
  dump_dir = Dir.new(Config.dump_dir)
44
30
  dump_dir.entries.select { |e| not e =~ /^\./ }.each do |entry|
45
- dump_file, attempt = entry.split("-")
46
31
  Log.info "Resubmitting #{entry}"
47
- payload = ""
48
- ref = File.join(dump_dir.path, entry)
49
- File.open(ref, "r") do |file|
50
- payload << file.read
51
- end
52
- FileUtils.rm_f(ref)
53
- attempt = attempt.to_i + 1
54
- if attempt <= 16
55
- submitter.submit(payload, dump_file, :attempt => attempt)
56
- end
32
+ payload = Payload.read_from_file(entry)
33
+ submitter = self.new(payload)
34
+ submitter.callback { |payload|
35
+ payload.destroy!
36
+ }
37
+ submitter.errback { |payload|
38
+ if payload.attempt > 16
39
+ payload.destroy!
40
+ else
41
+ payload.try_again_later
42
+ end
43
+ }
44
+ submitter.perform
57
45
  end
58
46
  end
59
47
  end
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 9
8
- - 0
9
- version: 0.9.0
8
+ - 5
9
+ version: 0.9.5
10
10
  platform: ruby
11
11
  authors:
12
12
  - Daniel Draper
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-07-07 00:00:00 +09:30
17
+ date: 2010-07-12 00:00:00 +09:30
18
18
  default_executable: nfagent
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -27,8 +27,8 @@ dependencies:
27
27
  segments:
28
28
  - 0
29
29
  - 0
30
- - 3
31
- version: 0.0.3
30
+ - 6
31
+ version: 0.0.6
32
32
  type: :runtime
33
33
  version_requirements: *id001
34
34
  - !ruby/object:Gem::Dependency
@@ -96,8 +96,13 @@ files:
96
96
  - lib/nfagent/server.rb
97
97
  - lib/nfagent/chunk_handler.rb
98
98
  - lib/nfagent/log.rb
99
+ - lib/nfagent/info.rb
99
100
  - lib/nfagent/event.rb
101
+ - lib/nfagent/payload.rb
102
+ - lib/nfagent/poller.rb
100
103
  - lib/nfagent/chunk.rb
104
+ - lib/nfagent/client.rb
105
+ - lib/nfagent/client_response.rb
101
106
  - lib/nfagent/config.rb
102
107
  - lib/nfagent/tail.rb
103
108
  - lib/nfagent/submitter.rb