nfagent 0.9.31 → 0.9.50
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +1 -1
- data/Manifest.txt +31 -14
- data/PostInstall.txt +6 -0
- data/{README.txt → README.rdoc} +7 -15
- data/Rakefile +18 -21
- data/bin/squid_log_writer +2 -0
- data/lib/nfagent.rb +6 -1
- data/lib/nfagent/chunk.rb +30 -16
- data/lib/nfagent/chunk_handler.rb +55 -23
- data/lib/nfagent/cli.rb +1 -1
- data/lib/nfagent/client.rb +8 -2
- data/lib/nfagent/config.rb +33 -0
- data/lib/nfagent/event.rb +1 -1
- data/lib/nfagent/mapper_proxy.rb +18 -0
- data/lib/nfagent/object_extra.rb +5 -0
- data/lib/nfagent/payload.rb +11 -7
- data/lib/nfagent/plugin.rb +14 -0
- data/lib/nfagent/server.rb +5 -0
- data/lib/nfagent/submitter.rb +1 -0
- data/nfagent.conf +4 -4
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/test/config +5 -0
- data/test/plugins/my_mapper.rb +18 -0
- data/test/test_chunk.rb +79 -0
- data/test/test_chunk_handler.rb +131 -0
- data/test/test_client.rb +20 -0
- data/test/test_config.rb +49 -0
- data/test/test_helper.rb +6 -0
- data/test/test_mapper_proxy.rb +20 -0
- data/test/test_nfagent.rb +8 -0
- data/test/test_payload.rb +77 -0
- data/test/test_plugin.rb +13 -0
- metadata +86 -25
data/History.txt
CHANGED
data/Manifest.txt
CHANGED
@@ -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
|
data/PostInstall.txt
CHANGED
data/{README.txt → README.rdoc}
RENAMED
@@ -1,6 +1,6 @@
|
|
1
|
-
=
|
1
|
+
= nfagent
|
2
2
|
|
3
|
-
* http://
|
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
|
-
*
|
18
|
-
* SV Util
|
19
|
+
* FIX (list of requirements)
|
19
20
|
|
20
21
|
== INSTALL:
|
21
22
|
|
22
|
-
sudo gem install
|
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)
|
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
|
-
|
2
|
-
|
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
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
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
|
-
|
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'
|
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]
|
data/bin/squid_log_writer
CHANGED
data/lib/nfagent.rb
CHANGED
@@ -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.
|
39
|
+
VERSION = '0.9.50'
|
35
40
|
end
|
data/lib/nfagent/chunk.rb
CHANGED
@@ -2,44 +2,58 @@ require 'zlib'
|
|
2
2
|
require 'digest'
|
3
3
|
|
4
4
|
module NFAgent
|
5
|
-
class
|
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
|
-
|
13
|
+
DEFAULT_MAX_SIZE = 500
|
9
14
|
|
10
|
-
def initialize(max_size =
|
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
|
-
|
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
|
-
|
28
|
+
self.size >= @max_size
|
23
29
|
end
|
24
30
|
|
25
31
|
def expired?
|
26
|
-
(Time.now - @created_at >
|
32
|
+
(Time.now - @created_at > Config.chunk_timeout) && !self.empty?
|
27
33
|
end
|
28
34
|
|
29
|
-
|
30
|
-
def dump
|
35
|
+
def dump(key = nil)
|
31
36
|
Payload.new do |payload|
|
32
|
-
Log.info("Dumping payload from chunk (#{
|
33
|
-
payload.line_count =
|
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.
|
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
|
41
|
-
|
42
|
-
|
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
|
-
|
5
|
-
|
6
|
-
|
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
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
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
|
-
|
33
|
-
|
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
|
data/lib/nfagent/cli.rb
CHANGED
data/lib/nfagent/client.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
module NFAgent
|
2
2
|
class Client
|
3
|
-
|
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
|
-
|
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
|
data/lib/nfagent/config.rb
CHANGED
@@ -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
|