content_server 1.1.0 → 1.2.0
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.
- data/bin/backup_server +8 -20
- data/bin/content_server +8 -20
- data/bin/testing_memory +60 -0
- data/bin/testing_server +57 -0
- data/ext/run_in_background/mkrf_conf.rb +34 -0
- data/lib/content_data/content_data.rb +613 -0
- data/lib/content_data/version.rb +3 -0
- data/lib/content_data.rb +6 -0
- data/lib/content_server/backup_server.rb +65 -86
- data/lib/content_server/content_server.rb +47 -77
- data/lib/content_server/file_streamer.rb +27 -33
- data/lib/content_server/queue_copy.rb +154 -49
- data/lib/content_server/queue_indexer.rb +19 -11
- data/lib/content_server/remote_content.rb +41 -23
- data/lib/content_server/server.rb +91 -0
- data/lib/content_server/version.rb +1 -1
- data/lib/content_server.rb +0 -15
- data/lib/email/email.rb +87 -0
- data/lib/email/version.rb +3 -0
- data/lib/email.rb +4 -0
- data/lib/file_copy/copy.rb +68 -0
- data/lib/file_copy/version.rb +4 -0
- data/lib/file_copy.rb +4 -0
- data/lib/file_indexing/index_agent.rb +170 -0
- data/lib/file_indexing/indexer_patterns.rb +72 -0
- data/lib/file_indexing/version.rb +3 -0
- data/lib/file_indexing.rb +9 -0
- data/lib/file_monitoring/file_monitoring.rb +105 -0
- data/lib/file_monitoring/monitor_path.rb +304 -0
- data/lib/file_monitoring/version.rb +3 -0
- data/lib/file_monitoring.rb +29 -0
- data/lib/file_utils/file_generator/README +97 -0
- data/lib/file_utils/file_generator/file_generator.rb +156 -0
- data/lib/file_utils/file_utils.rb +260 -0
- data/lib/file_utils/version.rb +3 -0
- data/lib/file_utils.rb +4 -0
- data/lib/log/version.rb +3 -0
- data/lib/log.rb +188 -0
- data/lib/networking/tcp.rb +213 -0
- data/lib/networking/version.rb +3 -0
- data/lib/networking.rb +4 -0
- data/lib/params/version.rb +3 -0
- data/lib/params.rb +419 -0
- data/lib/process_monitoring/monitoring.rb +85 -0
- data/lib/process_monitoring/monitoring_info.rb +79 -0
- data/lib/process_monitoring/send_email.rb +40 -0
- data/lib/process_monitoring/thread_safe_hash.rb +77 -0
- data/lib/process_monitoring/version.rb +3 -0
- data/lib/process_monitoring.rb +6 -0
- data/lib/run_in_background/version.rb +3 -0
- data/lib/run_in_background.rb +432 -0
- data/lib/testing_memory/testing_memory.rb +187 -0
- data/lib/testing_server/testing_server.rb +236 -0
- data/lib/testing_server/version.rb +3 -0
- data/lib/testing_server.rb +12 -0
- data/lib/validations/index_validations.rb +106 -0
- data/lib/validations/version.rb +3 -0
- data/lib/validations.rb +4 -0
- data/spec/content_data/validations_spec.rb +113 -0
- data/spec/file_copy/copy_spec.rb +54 -0
- data/spec/file_indexing/index_agent_spec.rb +53 -0
- data/spec/networking/tcp_spec.rb +95 -0
- data/spec/validations/index_validations_spec.rb +77 -0
- data/test/content_data/content_data_test.rb +290 -0
- data/test/file_generator/file_generator_spec.rb +84 -0
- data/test/file_indexing/index_agent_test/New.txt +0 -0
- data/test/file_indexing/index_agent_test/libxslt-1.1.26.win32/bin/libexslt.dll +0 -0
- data/test/file_indexing/index_agent_test/libxslt-1.1.26.win32/bin/libxslt.dll +0 -0
- data/test/file_indexing/index_agent_test/libxslt-1.1.26.win32/bin/xsltproc.exe +0 -0
- data/test/file_indexing/index_agent_test/libxslt-1.1.26.win32/include/libexslt/exslt.h +102 -0
- data/test/file_indexing/index_agent_test/libxslt-1.1.26.win32/include/libexslt/exsltconfig.h +73 -0
- data/test/file_indexing/index_agent_test/libxslt-1.1.26.win32/include/libexslt/exsltexports.h +140 -0
- data/test/file_indexing/index_agent_test/libxslt-1.1.26.win32/include/libexslt/libexslt.h +29 -0
- data/test/file_indexing/index_agent_test/libxslt-1.1.26.win32/include/libxslt/attributes.h +38 -0
- data/test/file_indexing/index_agent_test/libxslt-1.1.26.win32/include/libxslt/documents.h +93 -0
- data/test/file_indexing/index_agent_test/libxslt-1.1.26.win32/include/libxslt/extensions.h +262 -0
- data/test/file_indexing/index_agent_test/libxslt-1.1.26.win32/include/libxslt/extra.h +80 -0
- data/test/file_indexing/index_agent_test/libxslt-1.1.26.win32/include/libxslt/functions.h +78 -0
- data/test/file_indexing/index_agent_test/libxslt-1.1.26.win32/include/libxslt/imports.h +75 -0
- data/test/file_indexing/index_agent_test/libxslt-1.1.26.win32/include/libxslt/keys.h +53 -0
- data/test/file_indexing/index_agent_test/libxslt-1.1.26.win32/include/libxslt/libxslt.h +30 -0
- data/test/file_indexing/index_agent_test/libxslt-1.1.26.win32/include/libxslt/namespaces.h +68 -0
- data/test/file_indexing/index_agent_test/libxslt-1.1.26.win32/include/libxslt/numbersInternals.h +69 -0
- data/test/file_indexing/index_agent_test/libxslt-1.1.26.win32/include/libxslt/pattern.h +81 -0
- data/test/file_indexing/index_agent_test/libxslt-1.1.26.win32/include/libxslt/preproc.h +43 -0
- data/test/file_indexing/index_agent_test/libxslt-1.1.26.win32/include/libxslt/security.h +104 -0
- data/test/file_indexing/index_agent_test/libxslt-1.1.26.win32/include/libxslt/templates.h +77 -0
- data/test/file_indexing/index_agent_test/libxslt-1.1.26.win32/include/libxslt/transform.h +207 -0
- data/test/file_indexing/index_agent_test/libxslt-1.1.26.win32/include/libxslt/trio.h +216 -0
- data/test/file_indexing/index_agent_test/libxslt-1.1.26.win32/include/libxslt/triodef.h +220 -0
- data/test/file_indexing/index_agent_test/libxslt-1.1.26.win32/include/libxslt/variables.h +91 -0
- data/test/file_indexing/index_agent_test/libxslt-1.1.26.win32/include/libxslt/win32config.h +101 -0
- data/test/file_indexing/index_agent_test/libxslt-1.1.26.win32/include/libxslt/xslt.h +103 -0
- data/test/file_indexing/index_agent_test/libxslt-1.1.26.win32/include/libxslt/xsltInternals.h +1967 -0
- data/test/file_indexing/index_agent_test/libxslt-1.1.26.win32/include/libxslt/xsltconfig.h +172 -0
- data/test/file_indexing/index_agent_test/libxslt-1.1.26.win32/include/libxslt/xsltexports.h +142 -0
- data/test/file_indexing/index_agent_test/libxslt-1.1.26.win32/include/libxslt/xsltlocale.h +57 -0
- data/test/file_indexing/index_agent_test/libxslt-1.1.26.win32/include/libxslt/xsltutils.h +309 -0
- data/test/file_indexing/index_agent_test/libxslt-1.1.26.win32/include/libxslt/xsltwin32config.h +105 -0
- data/test/file_indexing/index_agent_test/libxslt-1.1.26.win32/lib/libexslt.lib +0 -0
- data/test/file_indexing/index_agent_test/libxslt-1.1.26.win32/lib/libexslt_a.lib +0 -0
- data/test/file_indexing/index_agent_test/libxslt-1.1.26.win32/lib/libxslt.lib +0 -0
- data/test/file_indexing/index_agent_test/libxslt-1.1.26.win32/lib/libxslt_a.lib +0 -0
- data/test/file_indexing/index_agent_test/libxslt-1.1.26.win32/readme.txt +22 -0
- data/test/file_indexing/index_agent_test/patterns.input +3 -0
- data/test/file_indexing/index_agent_test.rb +51 -0
- data/test/file_monitoring/file_monitoring_test/conf.yml +4 -0
- data/test/file_monitoring/file_monitoring_test/conf_win32.yml +5 -0
- data/test/file_monitoring/file_monitoring_test/log +56 -0
- data/test/file_monitoring/file_monitoring_test.rb +0 -0
- data/test/file_monitoring/monitor_path_test/dir1000/test_file.1000 +1000 -0
- data/test/file_monitoring/monitor_path_test/dir1000/test_file.1000.0 +1000 -0
- data/test/file_monitoring/monitor_path_test/dir1000/test_file.1000.1 +1000 -0
- data/test/file_monitoring/monitor_path_test/dir1500/test_file.1500 +1500 -0
- data/test/file_monitoring/monitor_path_test/dir1500/test_file.1500.0 +1500 -0
- data/test/file_monitoring/monitor_path_test/dir1500/test_file.1500.1 +1500 -0
- data/test/file_monitoring/monitor_path_test/test_file.500 +500 -0
- data/test/file_monitoring/monitor_path_test/test_file.500.0 +500 -0
- data/test/file_monitoring/monitor_path_test/test_file.500.1 +500 -0
- data/test/file_monitoring/monitor_path_test.rb +153 -0
- data/test/file_utils/fileutil_mksymlink_test/dir1000/dir1500/test_file.1500 +1500 -0
- data/test/file_utils/fileutil_mksymlink_test/dir1000/dir1500/test_file.1500.0 +1500 -0
- data/test/file_utils/fileutil_mksymlink_test/dir1000/dir1500/test_file.1500.1 +1500 -0
- data/test/file_utils/fileutil_mksymlink_test/dir1000/test_file.1000 +1000 -0
- data/test/file_utils/fileutil_mksymlink_test/dir1000/test_file.1000.0 +1000 -0
- data/test/file_utils/fileutil_mksymlink_test/dir1000/test_file.1000.1 +1000 -0
- data/test/file_utils/fileutil_mksymlink_test/test_file.500 +500 -0
- data/test/file_utils/fileutil_mksymlink_test/test_file.500.0 +500 -0
- data/test/file_utils/fileutil_mksymlink_test/test_file.500.1 +500 -0
- data/test/file_utils/fileutil_mksymlink_test.rb +125 -0
- data/test/file_utils/time_modification_test.rb +132 -0
- data/test/params/params_spec.rb +280 -0
- data/test/params/params_test.rb +43 -0
- data/test/run_in_background/run_in_background_test.rb +122 -0
- data/test/run_in_background/test_app +57 -0
- metadata +272 -132
- data/lib/content_server/globals.rb +0 -10
data/lib/email/email.rb
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
require 'net/smtp'
|
|
2
|
+
|
|
3
|
+
module Email
|
|
4
|
+
|
|
5
|
+
def Email.send_raw_email(from, password, to, mail_text)
|
|
6
|
+
smtp = Net::SMTP.new('smtp.gmail.com', 587)
|
|
7
|
+
smtp.enable_starttls
|
|
8
|
+
smtp.start(from.split('@')[-1], from, password, :login) do
|
|
9
|
+
smtp.send_message(mail_text, from, to)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def Email.send_email(from, password, to, subject, body)
|
|
14
|
+
marker = (0...50).map{ ('a'..'z').to_a[rand(26)] }.join
|
|
15
|
+
parts = []
|
|
16
|
+
parts << <<EOF
|
|
17
|
+
From: #{from}
|
|
18
|
+
To: #{to}
|
|
19
|
+
Subject: #{subject}
|
|
20
|
+
MIME-Version: 1.0
|
|
21
|
+
Content-Type: multipart/mixed; boundary=#{marker}
|
|
22
|
+
--#{marker}
|
|
23
|
+
EOF
|
|
24
|
+
|
|
25
|
+
# Define the message action
|
|
26
|
+
parts << <<EOF
|
|
27
|
+
Content-Type: text/plain
|
|
28
|
+
Content-Transfer-Encoding:8bit
|
|
29
|
+
|
|
30
|
+
#{body}
|
|
31
|
+
--#{marker}
|
|
32
|
+
EOF
|
|
33
|
+
|
|
34
|
+
mailtext = parts.join('')
|
|
35
|
+
send_raw_email(from, password, to, mailtext)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def Email.send_attachments_email(from, password, to, subject, body, attachments)
|
|
39
|
+
# Read a file and encode it into base64 format
|
|
40
|
+
begin
|
|
41
|
+
encoded_attachments = []
|
|
42
|
+
attachments.each do |attachment|
|
|
43
|
+
file_content = File.read(attachment)
|
|
44
|
+
encoded_content = [file_content].pack("m") # base64
|
|
45
|
+
encoded_attachments << [attachment, encoded_content]
|
|
46
|
+
end
|
|
47
|
+
rescue
|
|
48
|
+
raise "Could not read file #{attachment}"
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
marker = (0...50).map{ ('a'..'z').to_a[rand(26)] }.join
|
|
52
|
+
parts = []
|
|
53
|
+
parts << <<EOF
|
|
54
|
+
From: #{from}
|
|
55
|
+
To: #{to}
|
|
56
|
+
Subject: #{subject}
|
|
57
|
+
MIME-Version: 1.0
|
|
58
|
+
Content-Type: multipart/mixed; boundary=#{marker}
|
|
59
|
+
--#{marker}
|
|
60
|
+
EOF
|
|
61
|
+
|
|
62
|
+
# Define the message action
|
|
63
|
+
parts << <<EOF
|
|
64
|
+
Content-Type: text/plain
|
|
65
|
+
Content-Transfer-Encoding:8bit
|
|
66
|
+
|
|
67
|
+
#{body}
|
|
68
|
+
--#{marker}
|
|
69
|
+
EOF
|
|
70
|
+
|
|
71
|
+
encoded_attachments.each do |attachment, encoded_content|
|
|
72
|
+
# Define the attachment section
|
|
73
|
+
parts << <<EOF
|
|
74
|
+
Content-Type: multipart/mixed; name=\"#{File.basename(attachment)}\"
|
|
75
|
+
Content-Transfer-Encoding:base64
|
|
76
|
+
Content-Disposition: attachment; filename="#{File.basename(attachment)}"
|
|
77
|
+
|
|
78
|
+
#{encoded_content}
|
|
79
|
+
--#{marker}--
|
|
80
|
+
EOF
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
mailtext = parts.join('')
|
|
84
|
+
send_raw_email(from, password, to, mailtext)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
end
|
data/lib/email.rb
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
require 'net/ssh'
|
|
2
|
+
require 'net/sftp'
|
|
3
|
+
|
|
4
|
+
require 'log'
|
|
5
|
+
require 'params'
|
|
6
|
+
|
|
7
|
+
module FileCopy
|
|
8
|
+
|
|
9
|
+
# Creates ssh connection, assumes username and password
|
|
10
|
+
# or without password but with ssh keys.
|
|
11
|
+
def ssh_connect(username, password, server)
|
|
12
|
+
username = (username and username.length > 0) ? username : ENV['USER']
|
|
13
|
+
password = (password and password.length > 0) ? password : nil
|
|
14
|
+
port = 22 # 22 is a standart SSH port
|
|
15
|
+
raise "Undefined server" unless server
|
|
16
|
+
Log.debug1 "Trying to connect(ssh): #{username}, #{password}, #{server}, #{port}."
|
|
17
|
+
if (username and password)
|
|
18
|
+
Net::SSH.start(server, username,
|
|
19
|
+
:password => password,
|
|
20
|
+
:port => port)
|
|
21
|
+
elsif (username)
|
|
22
|
+
Net::SSH.start(server, username,
|
|
23
|
+
:port => port)
|
|
24
|
+
else
|
|
25
|
+
raise "Undefined username"
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
module_function :ssh_connect
|
|
29
|
+
|
|
30
|
+
# Simply copy map files using sftp server on dest_server.
|
|
31
|
+
# Note: stfp.upload support parallel uploads - default is 2.
|
|
32
|
+
# TODO(kolman): packing files, all, not all, determine by part of file.
|
|
33
|
+
def sftp_copy(username, password, server, files_map)
|
|
34
|
+
ssh = FileCopy.ssh_connect(username, password, server)
|
|
35
|
+
ssh.sftp.connect do |sftp|
|
|
36
|
+
uploads = files_map.map { |from,to|
|
|
37
|
+
remote_dir = File.dirname(to)
|
|
38
|
+
sftp_mkdir_recursive(sftp, File.dirname(to))
|
|
39
|
+
Log.debug1 "Copying #{from} to #{to}"
|
|
40
|
+
sftp.upload(from, to)
|
|
41
|
+
}
|
|
42
|
+
uploads.each { |u| u.wait }
|
|
43
|
+
Log.debug1 "Done."
|
|
44
|
+
end # ssh.sftp.connect
|
|
45
|
+
end # def initialize
|
|
46
|
+
module_function :sftp_copy
|
|
47
|
+
|
|
48
|
+
def sftp_mkdir_recursive(sftp, path)
|
|
49
|
+
dir_stat = nil
|
|
50
|
+
begin
|
|
51
|
+
Log.debug1 "Stat remote dir: #{path}."
|
|
52
|
+
dir_stat = sftp.stat!(path).directory?
|
|
53
|
+
Log.debug1 "Stat result #{dir_stat}."
|
|
54
|
+
rescue Net::SFTP::StatusException
|
|
55
|
+
end
|
|
56
|
+
if !dir_stat
|
|
57
|
+
Log.debug1 "Directory does not exists: #{path}."
|
|
58
|
+
sftp_mkdir_recursive sftp, File.dirname(path)
|
|
59
|
+
Log.debug1 "Making dir #{path}."
|
|
60
|
+
response = sftp.mkdir!(path)
|
|
61
|
+
Log.debug1 "Making dir ok:#{response.ok?}."
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
module_function :sftp_mkdir_recursive
|
|
65
|
+
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
|
data/lib/file_copy.rb
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
require 'digest/sha1'
|
|
2
|
+
require 'pp'
|
|
3
|
+
require 'set'
|
|
4
|
+
require 'time'
|
|
5
|
+
|
|
6
|
+
require 'content_data'
|
|
7
|
+
require 'file_indexing/indexer_patterns'
|
|
8
|
+
require 'log'
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
module FileIndexing
|
|
12
|
+
####################
|
|
13
|
+
# Index Agent
|
|
14
|
+
####################
|
|
15
|
+
|
|
16
|
+
class IndexAgent
|
|
17
|
+
attr_reader :indexed_content, :failed_files
|
|
18
|
+
|
|
19
|
+
# Why are those lines needed?
|
|
20
|
+
LOCALTZ = Time.now.zone
|
|
21
|
+
ENV['TZ'] = 'UTC'
|
|
22
|
+
|
|
23
|
+
def initialize
|
|
24
|
+
@indexed_content = ContentData::ContentData.new
|
|
25
|
+
@failed_files = Set.new
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Calculate file checksum (SHA1)
|
|
29
|
+
def self.get_checksum(filename)
|
|
30
|
+
digest = Digest::SHA1.new
|
|
31
|
+
begin
|
|
32
|
+
File.open(filename, 'rb') { |f|
|
|
33
|
+
while buffer = f.read(65536) do
|
|
34
|
+
digest << buffer
|
|
35
|
+
end
|
|
36
|
+
}
|
|
37
|
+
Log.debug1("#{filename} sha1 #{digest.hexdigest.downcase}")
|
|
38
|
+
digest.hexdigest.downcase
|
|
39
|
+
rescue Errno::EACCES, Errno::ETXTBSY => exp
|
|
40
|
+
Log.warning("#{exp.message}")
|
|
41
|
+
false
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def IndexAgent.get_content_checksum(content)
|
|
46
|
+
# Calculate checksum.
|
|
47
|
+
digest = Digest::SHA1.new
|
|
48
|
+
digest << content
|
|
49
|
+
digest.hexdigest.downcase
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# get all files
|
|
53
|
+
# satisfying the pattern
|
|
54
|
+
def collect(pattern)
|
|
55
|
+
Dir.glob(pattern.to_s)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# TODO(kolman): Replace this with File.lstat(file).mtime when new version of Ruby comes out.
|
|
59
|
+
# http://bugs.ruby-lang.org/issues/6385
|
|
60
|
+
def IndexAgent.get_correct_mtime(file)
|
|
61
|
+
begin
|
|
62
|
+
File.open(file, 'r') { |f| f.mtime }
|
|
63
|
+
rescue Errno::EACCES => e
|
|
64
|
+
Log.warning("Could not open file #{file} to get mtime. #{e}")
|
|
65
|
+
return 0
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# index device according to the pattern
|
|
70
|
+
# store the result
|
|
71
|
+
# does not adds automatically otherDB to stored result
|
|
72
|
+
# TODO device support
|
|
73
|
+
def index(patterns, otherDB = nil)
|
|
74
|
+
abort "#{self.class}: DB not empty. Current implementation permits only one running of index" \
|
|
75
|
+
unless @indexed_content.empty?
|
|
76
|
+
local_server_name = `hostname`.strip
|
|
77
|
+
permit_patterns = []
|
|
78
|
+
forbid_patterns = []
|
|
79
|
+
otherDB_updated = ContentData::ContentData.new
|
|
80
|
+
#otherDB_table = Hash.new # contains instances from given DB while full path name is a key and instance is a value
|
|
81
|
+
#otherDB_contents = Hash.new # given DB contents
|
|
82
|
+
|
|
83
|
+
# if there is a given DB then populate table with files
|
|
84
|
+
# that was already indexed on this server/device
|
|
85
|
+
if !otherDB.nil?
|
|
86
|
+
otherDB.each_instance { |checksum, size, content_mod_time, instance_mod_time, server, path|
|
|
87
|
+
if (server == local_server_name)
|
|
88
|
+
# add instance
|
|
89
|
+
otherDB_updated.add_instance(checksum, size, server, path, instance_mod_time)
|
|
90
|
+
end
|
|
91
|
+
}
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
permit_patterns = patterns.positive_patterns
|
|
95
|
+
forbid_patterns = patterns.negative_patterns
|
|
96
|
+
|
|
97
|
+
# add files found by positive patterns
|
|
98
|
+
files = Array.new
|
|
99
|
+
permit_patterns.each_index do |i|
|
|
100
|
+
files = files | (collect(permit_patterns[i]));
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
Log.debug1 "Files: #{files}."
|
|
104
|
+
|
|
105
|
+
# expand to absolute pathes
|
|
106
|
+
files.map! {|f| File.expand_path(f)}
|
|
107
|
+
|
|
108
|
+
# remove files found by negative patterns
|
|
109
|
+
forbid_patterns.each_index do |i|
|
|
110
|
+
forbid_files = Array.new(collect(forbid_patterns[i]));
|
|
111
|
+
forbid_files.each do |f|
|
|
112
|
+
files.delete(File.expand_path(f))
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# create and add contents and instances
|
|
117
|
+
files.each do |file|
|
|
118
|
+
file_stats = File.lstat(file)
|
|
119
|
+
file_mtime = IndexAgent.get_correct_mtime(file)
|
|
120
|
+
device = file_stats.dev.to_s
|
|
121
|
+
|
|
122
|
+
# index only files
|
|
123
|
+
next if file_stats.directory?
|
|
124
|
+
|
|
125
|
+
# add files present in the given DB to the DB and remove these files
|
|
126
|
+
# from further processing (save checksum calculation)
|
|
127
|
+
file_match = false
|
|
128
|
+
otherDB_updated.each_instance { |checksum, size, content_mod_time, instance_mod_time, server, path|
|
|
129
|
+
if otherDB_updated.instance_exists(file, local_server_name, checksum)
|
|
130
|
+
if size == file_stats.size and instance_mod_time == file_mtime.to_i
|
|
131
|
+
@indexed_content.add_instance(checksum, size, server, file, instance_mod_time)
|
|
132
|
+
file_match = true
|
|
133
|
+
break
|
|
134
|
+
else
|
|
135
|
+
Log.warning("File (#{file}) size or modification file is different. size=#{size} actual size=#{file_stats.size}" + \
|
|
136
|
+
" instance_mod_time=#{Time.at(instance_mod_time)} actual=#{file_mtime}")
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
}
|
|
140
|
+
next if file_match
|
|
141
|
+
# calculate a checksum
|
|
142
|
+
unless (checksum = self.class.get_checksum(file))
|
|
143
|
+
Log.warning("Cheksum failure: " + file)
|
|
144
|
+
@failed_files.add(file)
|
|
145
|
+
next
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
@indexed_content.add_instance(checksum, file_stats.size, local_server_name,
|
|
149
|
+
File.expand_path(file), file_mtime.to_i)
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def IndexAgent.create_shallow_instance(filename)
|
|
154
|
+
return nil unless File.exists?(filename)
|
|
155
|
+
file_stats = File.lstat(filename)
|
|
156
|
+
file_mtime = IndexAgent.get_correct_mtime(filename)
|
|
157
|
+
# return instance shallow representation (no server)
|
|
158
|
+
[file_stats.size,
|
|
159
|
+
"%s,%s,%s" % [`hostname`.strip , file_stats.dev.to_s , File.expand_path(filename)],
|
|
160
|
+
file_mtime.to_i]
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def IndexAgent.global_path(filename)
|
|
164
|
+
server_name = `hostname`.strip
|
|
165
|
+
file_stats = File.lstat(filename)
|
|
166
|
+
return "%s,%s,%s" % [server_name, file_stats.dev.to_s,filename]
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
require 'log'
|
|
2
|
+
require 'params'
|
|
3
|
+
|
|
4
|
+
module FileIndexing
|
|
5
|
+
|
|
6
|
+
class IndexerPatterns
|
|
7
|
+
attr_reader :positive_patterns, :negative_patterns
|
|
8
|
+
|
|
9
|
+
# @param indexer_patterns_str [String]
|
|
10
|
+
def initialize (indexer_patterns = nil)
|
|
11
|
+
Log.debug1 "Initialize index patterns #{indexer_patterns}."
|
|
12
|
+
@positive_patterns = Array.new
|
|
13
|
+
@negative_patterns = Array.new
|
|
14
|
+
# TODO add a test (including empty collections)
|
|
15
|
+
if indexer_patterns
|
|
16
|
+
indexer_patterns.positive_patterns.each do |pattern|
|
|
17
|
+
add_pattern(pattern)
|
|
18
|
+
end
|
|
19
|
+
indexer_patterns.negative_patterns.each do |pattern|
|
|
20
|
+
add_pattern(pattern, false)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def serialize
|
|
26
|
+
# TODO add a test (including empty collections)
|
|
27
|
+
indexer_patterns = IndexerPatternsMessage.new
|
|
28
|
+
positive_patterns.each do |pattern|
|
|
29
|
+
indexer_patterns.positive_patterns << pattern
|
|
30
|
+
end
|
|
31
|
+
negative_patterns.each do |pattern|
|
|
32
|
+
indexer_patterns.negative_patterns << pattern
|
|
33
|
+
end
|
|
34
|
+
indexer_patterns
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# @param pattern [String]
|
|
38
|
+
# @param is_positive [true]
|
|
39
|
+
# @param is_positive [false]
|
|
40
|
+
def add_pattern(pattern, is_positive = true)
|
|
41
|
+
pattern.gsub!(/\\/,'/')
|
|
42
|
+
if (is_positive)
|
|
43
|
+
@positive_patterns << pattern
|
|
44
|
+
else
|
|
45
|
+
@negative_patterns << pattern
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def parse_from_file(file)
|
|
50
|
+
input_patterns = IO.readlines(file)
|
|
51
|
+
begin
|
|
52
|
+
Log.debug1 "Error loading patterns=%s" % file
|
|
53
|
+
raise IOError("Error loading patterns=%s" % file)
|
|
54
|
+
end unless not input_patterns.nil?
|
|
55
|
+
|
|
56
|
+
input_patterns.each do |pattern|
|
|
57
|
+
if (m = /^\s*([+-]):(.*)/.match(pattern))
|
|
58
|
+
add_pattern(m[2], m[1].eql?('+') ? true : false)
|
|
59
|
+
elsif (not /^\s*[\/\/|#]/.match(pattern)) # not a comment
|
|
60
|
+
Log.debug1 "pattern in incorrect format: #{pattern}"
|
|
61
|
+
raise RuntimeError("pattern in incorrect format: #{pattern}")
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def size
|
|
67
|
+
return @positive_patterns.size
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
end
|
|
72
|
+
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
require 'algorithms'
|
|
2
|
+
require 'fileutils'
|
|
3
|
+
require 'log4r'
|
|
4
|
+
require 'params'
|
|
5
|
+
require 'content_server/server'
|
|
6
|
+
require 'file_monitoring/monitor_path'
|
|
7
|
+
|
|
8
|
+
module FileMonitoring
|
|
9
|
+
# Manages file monitoring of number of file system locations
|
|
10
|
+
class FileMonitoring
|
|
11
|
+
|
|
12
|
+
def initialize ()
|
|
13
|
+
@content_data_cache = Set.new
|
|
14
|
+
$local_content_data_lock.synchronize {
|
|
15
|
+
$local_content_data.each_instance(){
|
|
16
|
+
|_, _, _, _, _, file_path|
|
|
17
|
+
# save files to cache
|
|
18
|
+
Log.info("File in cache: #{file_path.clone}")
|
|
19
|
+
@content_data_cache.add(file_path.clone)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# Set event queue used for communication between different proceses.
|
|
26
|
+
# @param queue [Queue]
|
|
27
|
+
def set_event_queue(queue)
|
|
28
|
+
@event_queue = queue
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# The main method. Loops on all paths, each time span and monitors them.
|
|
32
|
+
#
|
|
33
|
+
# =Algorithm:
|
|
34
|
+
# There is a loop that performs at every iteration:
|
|
35
|
+
# 1.Pull entry with a minimal time of check from queue
|
|
36
|
+
# 2.Recursively check path taken from entry for changes
|
|
37
|
+
# a.Notify subscribed processes on changes
|
|
38
|
+
# 3.Push entry to the queue with new time of next check
|
|
39
|
+
#
|
|
40
|
+
# This methods controlled by <tt>monitoring_paths</tt> configuration parameter,
|
|
41
|
+
# that provides path and file monitoring configuration data
|
|
42
|
+
def monitor_files
|
|
43
|
+
conf_array = Params['monitoring_paths']
|
|
44
|
+
# Directories states stored in the priority queue,
|
|
45
|
+
# where the key (priority) is a time when it should be checked next time.
|
|
46
|
+
# Priority queue means that all entries arranged by key (time to check) in increasing order.
|
|
47
|
+
pq = Containers::PriorityQueue.new
|
|
48
|
+
conf_array.each { |elem|
|
|
49
|
+
priority = (Time.now + elem['scan_period']).to_i
|
|
50
|
+
dir_stat = DirStat.new(File.expand_path(elem['path']), elem['stable_state'], @content_data_cache, FileStatEnum::NON_EXISTING)
|
|
51
|
+
dir_stat.set_event_queue(@event_queue) if @event_queue
|
|
52
|
+
Log.debug1("File monitoring started for: #{elem}")
|
|
53
|
+
pq.push([priority, elem, dir_stat], -priority)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
#init log4r
|
|
57
|
+
monitoring_log_path = Params['default_monitoring_log_path']
|
|
58
|
+
Log.debug1 'File monitoring log: ' + Params['default_monitoring_log_path']
|
|
59
|
+
monitoring_log_dir = File.dirname(monitoring_log_path)
|
|
60
|
+
FileUtils.mkdir_p(monitoring_log_dir) unless File.exists?(monitoring_log_dir)
|
|
61
|
+
|
|
62
|
+
@log4r = Log4r::Logger.new 'BBFS monitoring log'
|
|
63
|
+
@log4r.trace = true
|
|
64
|
+
formatter = Log4r::PatternFormatter.new(:pattern => "[%d] [%m]")
|
|
65
|
+
#file setup
|
|
66
|
+
file_config = {
|
|
67
|
+
"filename" => Params['default_monitoring_log_path'],
|
|
68
|
+
"maxsize" => Params['log_rotation_size'],
|
|
69
|
+
"trunc" => true
|
|
70
|
+
}
|
|
71
|
+
file_outputter = Log4r::RollingFileOutputter.new("monitor_log", file_config)
|
|
72
|
+
file_outputter.level = Log4r::INFO
|
|
73
|
+
file_outputter.formatter = formatter
|
|
74
|
+
@log4r.outputters << file_outputter
|
|
75
|
+
FileStat.set_log(@log4r)
|
|
76
|
+
|
|
77
|
+
while true do
|
|
78
|
+
# pull entry that should be checked next,
|
|
79
|
+
# according to it's scan_period
|
|
80
|
+
time, conf, dir_stat = pq.pop
|
|
81
|
+
# time remains to wait before directory should be checked
|
|
82
|
+
time_span = time - Time.now.to_i
|
|
83
|
+
if (time_span > 0)
|
|
84
|
+
sleep(time_span)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
unless $testing_memory_active
|
|
88
|
+
dir_stat.monitor
|
|
89
|
+
else
|
|
90
|
+
$testing_memory_log.info("Start monitor at :#{Time.now}")
|
|
91
|
+
puts "Start monitor at :#{Time.now}"
|
|
92
|
+
dir_stat.monitor
|
|
93
|
+
$testing_memory_log.info("End monitor at :#{Time.now}")
|
|
94
|
+
puts "End monitor at :#{Time.now}"
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# push entry with new a next time it should be checked as a priority key
|
|
98
|
+
priority = (Time.now + conf['scan_period']).to_i
|
|
99
|
+
pq.push([priority, conf, dir_stat], -priority)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
end
|
|
105
|
+
|