nfagent 0.9.31 → 0.9.50

Sign up to get free protection for your applications and to get access to all the features.
@@ -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