nfagent 0.9.31 → 0.9.50

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.
@@ -1,4 +1,4 @@
1
- == 0.0.1 2009-09-22
1
+ === 0.0.1 2011-02-03
2
2
 
3
3
  * 1 major enhancement:
4
4
  * Initial release
@@ -1,29 +1,46 @@
1
+ History.txt
2
+ Manifest.txt
3
+ PostInstall.txt
4
+ README.rdoc
1
5
  Rakefile
6
+ script/console
7
+ script/destroy
8
+ script/generate
9
+ test/test_chunk.rb
10
+ test/test_chunk_handler.rb
11
+ test/test_client.rb
12
+ test/test_config.rb
13
+ test/test_helper.rb
14
+ test/test_mapper_proxy.rb
15
+ test/test_nfagent.rb
16
+ test/test_payload.rb
17
+ test/test_plugin.rb
18
+ test/config
19
+ test/plugins/my_mapper.rb
2
20
  lib/nfagent.rb
3
21
  lib/nfagent
4
- lib/nfagent/cli.rb
5
22
  lib/nfagent/base64.rb
6
- lib/nfagent/encoder.rb
7
- lib/nfagent/server.rb
8
- lib/nfagent/chunk_handler.rb
9
- lib/nfagent/log.rb
10
- lib/nfagent/info.rb
11
- lib/nfagent/event.rb
12
- lib/nfagent/payload.rb
13
- lib/nfagent/poller.rb
14
23
  lib/nfagent/chunk.rb
24
+ lib/nfagent/chunk_handler.rb
25
+ lib/nfagent/cli.rb
15
26
  lib/nfagent/client.rb
16
27
  lib/nfagent/client_response.rb
17
28
  lib/nfagent/config.rb
29
+ lib/nfagent/encoder.rb
30
+ lib/nfagent/event.rb
31
+ lib/nfagent/info.rb
32
+ lib/nfagent/log.rb
33
+ lib/nfagent/mapper_proxy.rb
34
+ lib/nfagent/object_extra.rb
35
+ lib/nfagent/payload.rb
36
+ lib/nfagent/plugin.rb
37
+ lib/nfagent/poller.rb
38
+ lib/nfagent/server.rb
39
+ lib/nfagent/submitter.rb
18
40
  lib/nfagent/tail.rb
19
41
  lib/nfagent/tests.rb
20
- lib/nfagent/submitter.rb
21
42
  bin/nfagent
22
43
  bin/squid_log_writer
23
- PostInstall.txt
24
- History.txt
25
- Manifest.txt
26
- README.txt
27
44
  nfagent.conf
28
45
  nfagent.init.ubuntu.txt
29
46
  nfagent.init.redhat.txt
@@ -1 +1,7 @@
1
+
1
2
  For more information on nfagent, see http://nfagent.rubyforge.org
3
+
4
+ NOTE: Change this information in PostInstall.txt
5
+ You can also delete it if you don't want it.
6
+
7
+
@@ -1,6 +1,6 @@
1
- = NetFox Agent
1
+ = nfagent
2
2
 
3
- * http://netfox.com
3
+ * http://github.com/#{github_username}/#{project_name}
4
4
 
5
5
  == DESCRIPTION:
6
6
 
@@ -8,33 +8,25 @@ Logging Agent for NetFox Online
8
8
 
9
9
  == FEATURES/PROBLEMS:
10
10
 
11
+ * FIX (list of features or problems)
11
12
 
12
13
  == SYNOPSIS:
13
14
 
15
+ FIX (code sample of usage)
14
16
 
15
17
  == REQUIREMENTS:
16
18
 
17
- * Event Machine
18
- * SV Util
19
+ * FIX (list of requirements)
19
20
 
20
21
  == INSTALL:
21
22
 
22
- sudo gem install nfagent
23
-
24
- == DEVELOPERS:
25
-
26
- After checking out the source, run:
27
-
28
- $ rake newb
29
-
30
- This task will install any missing dependencies, run the tests/specs,
31
- and generate the RDoc.
23
+ * FIX (sudo gem install, anything else)
32
24
 
33
25
  == LICENSE:
34
26
 
35
27
  (The MIT License)
36
28
 
37
- Copyright (c) 2010 FIX
29
+ Copyright (c) 2011 FIXME full name
38
30
 
39
31
  Permission is hereby granted, free of charge, to any person obtaining
40
32
  a copy of this software and associated documentation files (the
data/Rakefile CHANGED
@@ -1,31 +1,28 @@
1
- %w[rubygems rake rake/clean hoe fileutils newgem rubigen].each { |f| require f }
2
- require File.dirname(__FILE__) + '/lib/nfagent'
1
+ require 'rubygems'
2
+ gem 'hoe', '>= 2.1.0'
3
+ require 'hoe'
4
+ require 'fileutils'
5
+ require './lib/nfagent'
6
+
7
+ Hoe.plugin :newgem
8
+ # Hoe.plugin :website
9
+ # Hoe.plugin :cucumberfeatures
3
10
 
4
11
  # Generate all the Rake tasks
5
12
  # Run 'rake -T' to see list of generated tasks (from gem root directory)
6
- $hoe = Hoe.spec('nfagent') do |p|
7
- p.version = NFAgent::VERSION
8
- p.summary = "Logging Agent for NetFox Online"
9
- p.developer('Daniel Draper', 'daniel@netfox.com')
10
- p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
11
- p.post_install_message = 'PostInstall.txt'
12
- p.rubyforge_name = p.name
13
- p.extra_deps = [
14
- ['svutil','>= 0.0.12'], ['eventmachine', '>= 0.12.8']
15
- ]
16
- p.extra_dev_deps = [
17
- ['newgem', ">= #{::Newgem::VERSION}"]
13
+ $hoe = Hoe.spec 'nfagent' do
14
+ self.developer('Daniel Draper', 'daniel@netfox.com')
15
+ self.post_install_message = 'PostInstall.txt' # TODO remove if post-install message not required
16
+ self.rubyforge_name = self.name # TODO this is default value
17
+ self.extra_deps = [
18
+ ['svutil','>= 0.1.3'], ['eventmachine', '>= 0.12.8'], ['squiggle', '>= 0.0.8']
18
19
  ]
19
- p.clean_globs |= %w[**/.DS_Store tmp *.log]
20
- path = (p.rubyforge_name == p.name) ? p.rubyforge_name : "\#{p.rubyforge_name}/\#{p.name}"
21
- p.remote_rdoc_dir = File.join(path.gsub(/^#{p.rubyforge_name}\/?/,''), 'rdoc')
22
- p.rsync_args = '-av --delete --ignore-errors'
23
- p.readme_file = "README.txt"
24
- p.spec_extras[:default_executable] = 'nfagent'
20
+
25
21
  end
26
22
 
27
- require 'newgem/tasks' # load /tasks/*.rake
23
+ require 'newgem/tasks'
28
24
  Dir['tasks/**/*.rake'].each { |t| load t }
29
25
 
30
26
  # TODO - want other tests/tasks run by default? Add them to the list
27
+ # remove_task :default
31
28
  # task :default => [:spec, :features]
@@ -6,12 +6,14 @@ class Client
6
6
  def initialize(host, port)
7
7
  @host = host
8
8
  @port = port
9
+ @count = 0
9
10
  end
10
11
 
11
12
  def write_safely(data)
12
13
  connect_socket unless @connected
13
14
  begin
14
15
  @client.puts(data)
16
+ sleep 0.01
15
17
  rescue
16
18
  @connected = false
17
19
  end
@@ -2,7 +2,9 @@ $:.unshift(File.dirname(__FILE__)) unless
2
2
  $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
3
 
4
4
  require 'rubygems'
5
+ require 'active_support'
5
6
  require 'svutil'
7
+ require 'squiggle'
6
8
 
7
9
  require 'fileutils'
8
10
  require 'logger'
@@ -13,10 +15,13 @@ require 'eventmachine'
13
15
  require 'em/timers'
14
16
  require 'rbconfig'
15
17
 
18
+ require 'nfagent/object_extra'
16
19
  require 'nfagent/chunk'
17
20
  require 'nfagent/client'
18
21
  require 'nfagent/client_response'
19
22
  require 'nfagent/chunk_handler'
23
+ require 'nfagent/mapper_proxy'
24
+ require 'nfagent/plugin'
20
25
  require 'nfagent/submitter'
21
26
  require 'nfagent/encoder'
22
27
  require 'nfagent/config'
@@ -31,5 +36,5 @@ require 'nfagent/cli'
31
36
  require 'nfagent/tests'
32
37
 
33
38
  module NFAgent
34
- VERSION = '0.9.31'
39
+ VERSION = '0.9.50'
35
40
  end
@@ -2,44 +2,58 @@ require 'zlib'
2
2
  require 'digest'
3
3
 
4
4
  module NFAgent
5
- class Chunk
5
+ class ChunkExpired < StandardError; end
6
+ class ChunkFull < StandardError; end
7
+ class DayBoundary < StandardError; end
8
+
9
+ class Chunk < Array
6
10
  attr_reader :created_at
11
+ attr_reader :max_size
7
12
 
8
- ::DEFAULT_TIME_OUT = 60
13
+ DEFAULT_MAX_SIZE = 500
9
14
 
10
- def initialize(max_size = 500)
15
+ def initialize(max_size = DEFAULT_MAX_SIZE)
11
16
  @max_size = max_size
12
17
  @created_at = Time.now
13
- @array = []
14
- @submitter = Submitter.new(Config.client_key)
15
18
  end
16
19
 
17
20
  def <<(line)
18
- @array << line
21
+ raise ChunkExpired if expired?
22
+ raise ChunkFull if full?
23
+ raise DayBoundary if Time.now.day != self.created_at.day
24
+ super(line)
19
25
  end
20
26
 
21
27
  def full?
22
- @array.size >= @max_size
28
+ self.size >= @max_size
23
29
  end
24
30
 
25
31
  def expired?
26
- (Time.now - @created_at > ::DEFAULT_TIME_OUT) && !@array.empty?
32
+ (Time.now - @created_at > Config.chunk_timeout) && !self.empty?
27
33
  end
28
34
 
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
30
- def dump
35
+ def dump(key = nil)
31
36
  Payload.new do |payload|
32
- Log.info("Dumping payload from chunk (#{@array.size} lines)")
33
- payload.line_count = @array.size
37
+ Log.info("Dumping payload from chunk (#{self.size || 0} lines #{'due to expiry' if expired?}")
38
+ payload.line_count = self.size
34
39
  payload.chunk_expired = expired?
35
- payload.data = Encoder.encode64url(Zlib::Deflate.deflate(@array.join("\n"), Zlib::BEST_COMPRESSION))
40
+ payload.key = key
41
+ payload.data = Encoder.encode64url(Zlib::Deflate.deflate(self.join("\n"), Zlib::BEST_COMPRESSION))
36
42
  payload.checksum = Digest::SHA1.hexdigest(payload.data)
37
43
  end
38
44
  end
39
45
 
40
- def clear
41
- @array.clear
42
- @created_at = Time.now
46
+ def submit(key = nil)
47
+ Log.info("Submitting...")
48
+ # TODO God knows why EM Deferrable isn't working - defer here is OK
49
+ EM.defer {
50
+ submitter = Submitter.new(self.dump(key))
51
+ submitter.errback { |payload|
52
+ payload.write_to_disk(Config.dump_dir)
53
+ }
54
+ submitter.perform
55
+ }
56
+ # Callback and remove from chunk group
43
57
  end
44
58
  end
45
59
  end
@@ -1,40 +1,72 @@
1
1
  module NFAgent
2
+ class LookUpError < StandardError; end
3
+ class IgnoreLine < StandardError; end
4
+
2
5
  class ChunkHandler
3
6
 
4
- # TODO: Rename this to Controller later
5
- def initialize(chunk_size = 500)
6
- @chunk = Chunk.new(chunk_size)
7
+ attr_accessor :chunk_group
8
+
9
+ def initialize(options = {})
10
+ @chunk_size = options[:chunk_size] || 500
11
+ @parser = options[:parser] || Squiggle::SquidStandardParser.new(Config.time_zone)
12
+ @chunk_group = {}
13
+ class << @chunk_group
14
+ def fetch!(key, new_chunk)
15
+ if self.has_key?(key)
16
+ self.fetch(key)
17
+ else
18
+ self[key] = new_chunk
19
+ new_chunk
20
+ end
21
+ end
22
+ end
7
23
  end
8
24
 
9
25
  def append(line)
10
- # if current day is > day of last entry on current_chunk
11
- # then submit and reset the chunk before adding the line
12
- current_day = Time.now.day
13
- if current_day != @chunk.created_at.day
14
- Log.info("Expiring chunk due to date rollover")
15
- reset_chunk
26
+ if Config.parse == 'locally'
27
+ parsed = @parser.parse(line)
28
+ return if parsed.invalid?
29
+ if Config.mode == 'multi'
30
+ begin
31
+ key = MapperProxy.find_account_id(parsed.username, parsed.client_ip)
32
+ # TODO: Still appending line as string until Server API has been updated
33
+ return append2(line, key)
34
+ rescue LookUpError, IgnoreLine
35
+ return # Do nothing
36
+ end
37
+ end
38
+ end
39
+ # TODO: rename append2
40
+ append2(line)
41
+ end
42
+
43
+ def append2(line, key = nil)
44
+ key ||= 'all'
45
+ begin
46
+ chunk = @chunk_group.fetch!(key, Chunk.new(@chunk_size))
47
+ chunk << line
48
+ rescue ChunkExpired, ChunkFull
49
+ Log.info("Chunk full or expired, cannot add lines")
50
+ reset_chunk(key)
16
51
  end
17
- @chunk << line
18
52
  end
19
53
 
20
54
  def check_full_or_expired
21
- if @chunk.full?
22
- Log.info("Chunk Full: Resetting...")
23
- reset_chunk
24
- elsif @chunk.expired?
25
- Log.info("Chunk Expired: Resetting...")
26
- reset_chunk
55
+ @chunk_group.each_pair do |key, chunk|
56
+ if chunk.full?
57
+ Log.info("Chunk Full: Resetting...")
58
+ reset_chunk(key)
59
+ elsif chunk.expired?
60
+ Log.info("Chunk Expired: Resetting...")
61
+ reset_chunk(key)
62
+ end
27
63
  end
28
64
  end
29
65
 
30
66
  private
31
- def reset_chunk
32
- submitter = Submitter.new(@chunk.dump)
33
- submitter.errback { |payload|
34
- payload.write_to_disk(Config.dump_dir)
35
- }
36
- @chunk.clear
37
- submitter.perform
67
+ def reset_chunk(key = nil)
68
+ chunk = @chunk_group.delete(key || 'all')
69
+ chunk.submit(key == 'all' ? nil : key)
38
70
  end
39
71
  end
40
72
  end
@@ -4,7 +4,7 @@ module NFAgent
4
4
  include SVUtil
5
5
 
6
6
  def initialize
7
- Config.load_and_parse
7
+ Config.init
8
8
  if Config.test_mode?
9
9
  Tests.run
10
10
  exit 1
@@ -1,6 +1,7 @@
1
1
  module NFAgent
2
2
  class Client
3
- SERVICE_HOST = "collector.service.netfox.com"
3
+ # TODO: Make this a config option
4
+ SERVICE_HOST = "sandbox.netfox.com"
4
5
 
5
6
  def self.post(end_point, data_hash)
6
7
  proxy_class = Net::HTTP::Proxy(Config.http_proxy_host, Config.http_proxy_port, Config.http_proxy_user, Config.http_proxy_password)
@@ -8,14 +9,19 @@ module NFAgent
8
9
  proxy_class.start(SERVICE_HOST, 80) do |http|
9
10
  http.read_timeout = 120 # 2 minutes TODO: Make this a config option with 120 as default
10
11
  req = Net::HTTP::Post.new("/#{end_point}")
11
- req.set_form_data(data_hash.merge("key" => Config.client_key))
12
+ p({"key" => Config.client_key}.merge(data_hash).delete('data'))
13
+ req.set_form_data({"key" => Config.client_key}.merge(data_hash))
12
14
  ClientResponse.new do |resp|
13
15
  resp.response, resp.message = http.request(req)
16
+ if !resp.ok?
17
+ Log.info("Client Returned with code (#{resp.response.code}, #{resp.response.msg}) and message '#{resp.message}'")
18
+ end
14
19
  end
15
20
  end
16
21
  rescue Exception => e
17
22
  # Trap Exception class here to ensure we catch Timeout
18
23
  ClientResponse.new do |resp|
24
+ Log.info("Client Error: #{$!}")
19
25
  resp.message = $!
20
26
  end
21
27
  end
@@ -8,11 +8,41 @@ module NFAgent
8
8
  @@test_mode
9
9
  end
10
10
 
11
+ # Config Options
12
+ # client_key: String, the access key for the client (for the account in normal mode or for a partner in multi mode)
13
+ # dump_dir: String, the directory path of a local spool location
14
+ # pid_file: String, path of process ID file
15
+ # mode: (optional, default: 'normal') String, either 'normal' or 'multi' - can be left blank
16
+ # mapping: Class, this is a plugin class which must be stored in a file in the directory /etc/nfagent/plugins/
17
+ # parse: (optional, default: 'remotely'): String, either 'remotely' or 'locally'
18
+ #
19
+ defaults do |c|
20
+ c.mode = 'normal'
21
+ c.parse = 'remotely'
22
+ c.chunk_timeout = 60
23
+ c.time_zone = 'UTC'
24
+ c.plugin_directory = '/etc/nfagent/plugins/'
25
+ end
26
+
11
27
  class << self
12
28
  def validate
13
29
  unless dump_dir and File.exists?(dump_dir) and File.directory?(dump_dir)
14
30
  raise "Dump dir (#{dump_dir}) must exist and be a directory"
15
31
  end
32
+ # Mode
33
+ unless %w(normal multi).include?(mode)
34
+ raise "Invalid mode: must be one of 'normal' or 'multi'"
35
+ end
36
+ if mode == 'multi' && mapper.blank?
37
+ raise "Multi mode requires a mapper to be set"
38
+ end
39
+ if mode == 'multi' && parse != 'locally'
40
+ raise "Multi mode requires that parsing be done locally (set parse = 'locally')"
41
+ end
42
+ # Parse
43
+ unless %w(remotely locally).include?(parse)
44
+ raise "Invalid parse option: Must be one of 'remotely' or 'locally'"
45
+ end
16
46
  super
17
47
  end
18
48
 
@@ -30,6 +60,9 @@ module NFAgent
30
60
  opts.on("-T", "--test", "Run connection tests") do
31
61
  @@test_mode = true
32
62
  end
63
+ opts.on("-P", "--parse", "Parse locally before submitting") do
64
+ Config.parse_locally = true
65
+ end
33
66
  end
34
67
  end
35
68
  end