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
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
#require 'net/sftp'
|
|
2
|
+
#require 'net/ssh'
|
|
3
|
+
require 'log4r'
|
|
4
|
+
require 'log'
|
|
5
|
+
require 'params'
|
|
6
|
+
require 'content_server'
|
|
7
|
+
require 'content_server/content_server' # specific content server impl
|
|
8
|
+
require 'content_server/backup_server' # specific backup server impl
|
|
9
|
+
require 'content_data'
|
|
10
|
+
require 'email'
|
|
11
|
+
require 'file_utils/file_generator/file_generator'
|
|
12
|
+
require 'validations'
|
|
13
|
+
|
|
14
|
+
# Testing server. Assumes that content and backup servers are running.
|
|
15
|
+
# The server runs 24-7, generates/deletes files and validates content at backup periodically.
|
|
16
|
+
module TestingServer
|
|
17
|
+
# TODO get latest ContentData object directly from Content/BackupServer
|
|
18
|
+
# Then need changes in Content/BackupServers API
|
|
19
|
+
# TODO split to Backup/ContentTestingServers separate classes ?
|
|
20
|
+
# TODO split long methods
|
|
21
|
+
# TODO Tests
|
|
22
|
+
# TODO No default config taken by Params
|
|
23
|
+
# TODO Additional testing scenarios (remove/rename/change)
|
|
24
|
+
|
|
25
|
+
# Messages types
|
|
26
|
+
:GET_INDEX # get index from remote master
|
|
27
|
+
:PUT_INDEX # message contains requested index from master
|
|
28
|
+
# TODO in thus implementation validation called automatically on master side
|
|
29
|
+
# with receiving GET_INDEX message
|
|
30
|
+
# More flexible will be separating it to two separate operations:
|
|
31
|
+
# GET_INDEX and VALIDATE
|
|
32
|
+
#:VALIDATE # validate index on master
|
|
33
|
+
:PUT_VALIDATION # message contains validation result from master
|
|
34
|
+
|
|
35
|
+
Params.path('testing_log_path', nil, 'Testing server log path.')
|
|
36
|
+
|
|
37
|
+
# init process vars
|
|
38
|
+
$objects_counters = {}
|
|
39
|
+
$objects_counters["Time"] = Time.now.to_i
|
|
40
|
+
|
|
41
|
+
def init_log4r
|
|
42
|
+
#init log4r
|
|
43
|
+
log_path = Params['testing_log_path']
|
|
44
|
+
unless log_path
|
|
45
|
+
raise("pls specify testing log path through param:'testing_log_path'")
|
|
46
|
+
end
|
|
47
|
+
log_dir = File.dirname(log_path)
|
|
48
|
+
FileUtils.mkdir_p(log_dir) unless File.exists?(log_dir)
|
|
49
|
+
|
|
50
|
+
$log4r = Log4r::Logger.new 'BBFS testing server log'
|
|
51
|
+
$log4r.trace = true
|
|
52
|
+
formatter = Log4r::PatternFormatter.new(:pattern => "[%d] [%m]")
|
|
53
|
+
#file setup
|
|
54
|
+
file_config = {
|
|
55
|
+
"filename" => Params['testing_log_path'],
|
|
56
|
+
"maxsize" => Params['log_rotation_size'],
|
|
57
|
+
"trunc" => true
|
|
58
|
+
}
|
|
59
|
+
file_outputter = Log4r::RollingFileOutputter.new("testing_log", file_config)
|
|
60
|
+
file_outputter.level = Log4r::INFO
|
|
61
|
+
file_outputter.formatter = formatter
|
|
62
|
+
$log4r.outputters << file_outputter
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def run_content_testing_server
|
|
66
|
+
Log.info('Testing server started')
|
|
67
|
+
init_log4r
|
|
68
|
+
$log4r.info 'Testing server started'
|
|
69
|
+
all_threads = [];
|
|
70
|
+
messages = Queue.new
|
|
71
|
+
|
|
72
|
+
all_threads << Thread.new do
|
|
73
|
+
ContentServer.run_content_server
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
all_threads << Thread.new do
|
|
77
|
+
fg = FileGenerator::FileGenerator.new
|
|
78
|
+
fg.run
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
receive_msg_proc = lambda do |addr_info, message|
|
|
82
|
+
$log4r.info("message received: #{message}")
|
|
83
|
+
messages.push(message)
|
|
84
|
+
end
|
|
85
|
+
tcp = Networking::TCPServer.new(Params['testing_server_port'], receive_msg_proc)
|
|
86
|
+
|
|
87
|
+
all_threads << tcp.tcp_thread
|
|
88
|
+
|
|
89
|
+
while(true) do
|
|
90
|
+
msg_type, validation_timestamp = messages.pop
|
|
91
|
+
if msg_type == :GET_INDEX
|
|
92
|
+
cur_index = ContentData::ContentData.new
|
|
93
|
+
cur_index.from_file(Params['local_content_data_path'])
|
|
94
|
+
tcp.send_obj([:PUT_INDEX, validation_timestamp, cur_index])
|
|
95
|
+
$log4r.info "PUT_INDEX sent with timestamp #{validation_timestamp}"
|
|
96
|
+
is_index_ok = cur_index.validate
|
|
97
|
+
tcp.send_obj([:PUT_VALIDATION, validation_timestamp, is_index_ok])
|
|
98
|
+
$log4r.info "PUT_VALIDATION sent with timestamp #{validation_timestamp}"
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
module_function :run_content_testing_server
|
|
103
|
+
|
|
104
|
+
def run_backup_testing_server
|
|
105
|
+
Log.info('Testing server started')
|
|
106
|
+
init_log4r
|
|
107
|
+
$log4r.info 'Testing server started'
|
|
108
|
+
all_threads = [];
|
|
109
|
+
messages = Queue.new
|
|
110
|
+
# used for synchronization between servers
|
|
111
|
+
validation_timestamp = Time.now.to_i
|
|
112
|
+
# holds boolean whether all contents according to this timestamp that should be backed are backed and OK
|
|
113
|
+
is_cur_synch_ok = nil
|
|
114
|
+
# holds boolean whether all already backuped files are OK
|
|
115
|
+
is_backup_ok = nil
|
|
116
|
+
# holds boolean whether master is valid. receive from master
|
|
117
|
+
is_master_ok = nil
|
|
118
|
+
# number of contents must be backuped this synch
|
|
119
|
+
# with current scenarios this number expected to grow
|
|
120
|
+
# used to check that file_generator is actually running
|
|
121
|
+
numb_backuped_contents = 0
|
|
122
|
+
|
|
123
|
+
all_threads << Thread.new do
|
|
124
|
+
ContentServer.run_backup_server
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
receive_msg_proc = lambda do |message|
|
|
128
|
+
$log4r.info("message received: #{message}")
|
|
129
|
+
messages.push(message)
|
|
130
|
+
end
|
|
131
|
+
tcp = Networking::TCPClient.new(Params['content_server_hostname'],
|
|
132
|
+
Params['testing_server_port'], receive_msg_proc)
|
|
133
|
+
all_threads << tcp.tcp_thread
|
|
134
|
+
|
|
135
|
+
# used to request index from master per validation interval of time
|
|
136
|
+
all_threads << Thread.new do
|
|
137
|
+
while(true) do
|
|
138
|
+
sleep Params['validation_interval']
|
|
139
|
+
validation_timestamp = Time.now.to_i
|
|
140
|
+
tcp.send_obj([:GET_INDEX, validation_timestamp])
|
|
141
|
+
$log4r.info "GET_INDEX sent with timestamp #{validation_timestamp}"
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# gets messages from master via queue
|
|
146
|
+
# handles them according to type
|
|
147
|
+
while(true) do
|
|
148
|
+
msg_type, response_validation_timestamp, msg_body = messages.pop
|
|
149
|
+
$log4r.info "#{msg_type} : #{validation_timestamp} : #{msg_body.class}"
|
|
150
|
+
unless (validation_timestamp == response_validation_timestamp)
|
|
151
|
+
# TODO problem of servers synch. What should be done here?
|
|
152
|
+
$log4r.warning "Timestamps differs: requested #{validation_timestamp} : received #{response_validation_timestamp}"
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
case msg_type
|
|
156
|
+
when :PUT_INDEX
|
|
157
|
+
# index of already backuped contents
|
|
158
|
+
backuped_index = ContentData::ContentData.new
|
|
159
|
+
backuped_index.from_file Params['local_content_data_path']
|
|
160
|
+
# index contains only master current contents
|
|
161
|
+
# that must be backuped according to backup_time_requirement parameter
|
|
162
|
+
index_must_be_backuped = ContentData::ContentData.new(msg_body)
|
|
163
|
+
# we backup contents, so content mtime used to determine contents should be validated
|
|
164
|
+
index_must_be_backuped.each_content do |checksum, size, mtime|
|
|
165
|
+
if (validation_timestamp - mtime < Params['backup_time_requirement'])
|
|
166
|
+
index_must_be_backuped.remove_content checksum
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
is_cur_synch_ok =
|
|
170
|
+
Validations::IndexValidations.validate_remote_index index_must_be_backuped, backuped_index
|
|
171
|
+
is_backup_ok = backuped_index.validate
|
|
172
|
+
numb_backuped_contents = index_must_be_backuped.contents_size
|
|
173
|
+
when :PUT_VALIDATION
|
|
174
|
+
is_master_ok = msg_body
|
|
175
|
+
else
|
|
176
|
+
$log4r.error "Incorrect message received: #{msg_type}"
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
unless (is_master_ok.nil? || is_cur_synch_ok.nil? || is_backup_ok.nil?)
|
|
180
|
+
send_email is_master_ok, is_cur_synch_ok, is_backup_ok, numb_backuped_contents
|
|
181
|
+
is_master_ok = nil
|
|
182
|
+
is_cur_synch_ok = nil
|
|
183
|
+
is_backup_ok = nil
|
|
184
|
+
numb_backuped_contents = 0
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def generate_mem_report
|
|
191
|
+
# Generate memory report
|
|
192
|
+
current_objects_counters = {}
|
|
193
|
+
time = Time.now
|
|
194
|
+
current_objects_counters['Time'] = time.to_i
|
|
195
|
+
count = ObjectSpace.each_object(String).count
|
|
196
|
+
current_objects_counters['String'] = count
|
|
197
|
+
count = ObjectSpace.each_object(ContentData::ContentData).count
|
|
198
|
+
current_objects_counters['ContentData'] = count
|
|
199
|
+
dir_count = ObjectSpace.each_object(FileMonitoring::DirStat).count
|
|
200
|
+
current_objects_counters['DirStat'] = dir_count
|
|
201
|
+
file_count = ObjectSpace.each_object(FileMonitoring::FileStat).count
|
|
202
|
+
current_objects_counters['FileStat'] = file_count-dir_count
|
|
203
|
+
|
|
204
|
+
# Generate report and update global counters
|
|
205
|
+
report = ""
|
|
206
|
+
current_objects_counters.each_key { |type|
|
|
207
|
+
$objects_counters[type] = 0 unless $objects_counters[type]
|
|
208
|
+
diff = current_objects_counters[type] - $objects_counters[type]
|
|
209
|
+
report += "Type:#{type} raised in:#{diff} \n"
|
|
210
|
+
$objects_counters[type] = current_objects_counters[type]
|
|
211
|
+
}
|
|
212
|
+
final_report = "Memory report at Time:#{time}:\n#{report}\n"
|
|
213
|
+
$log4r.info(final_report)
|
|
214
|
+
final_report
|
|
215
|
+
end
|
|
216
|
+
def send_email(is_master_ok, is_cur_synch_ok, is_backup_ok, numb_backuped_contents)
|
|
217
|
+
|
|
218
|
+
msg =<<EOF
|
|
219
|
+
Master index ok: #{is_master_ok}
|
|
220
|
+
Backup index ok: #{is_backup_ok}
|
|
221
|
+
Backup includes all master files upto -#{Params['backup_time_requirement']} sec: #{is_cur_synch_ok}
|
|
222
|
+
Number of contents must be back-up: #{numb_backuped_contents}
|
|
223
|
+
#{generate_mem_report}
|
|
224
|
+
EOF
|
|
225
|
+
Email.send_email(Params['from_email'],
|
|
226
|
+
Params['from_email_password'],
|
|
227
|
+
Params['to_email'],
|
|
228
|
+
'Testing server update',
|
|
229
|
+
msg)
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
module_function :send_email, :run_backup_testing_server, :init_log4r, :generate_mem_report
|
|
233
|
+
|
|
234
|
+
end # module TestingServer
|
|
235
|
+
|
|
236
|
+
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
require 'testing_server/testing_server'
|
|
2
|
+
|
|
3
|
+
module TestingServer
|
|
4
|
+
Params.integer('testing_server_port', 4445, 'Remote port to synchronization between testing servers')
|
|
5
|
+
#Params.integer('email_delay_in_seconds', 60*60*6, 'Number of seconds before sending email again.')
|
|
6
|
+
Params.integer('validation_interval', 60*60*6, 'Number of seconds between validations')
|
|
7
|
+
Params.integer('backup_time_requirement', 60*60,
|
|
8
|
+
'Max diff in seconds between timestamps of file indexation on master ' +
|
|
9
|
+
'and its content indexation on backup.' +
|
|
10
|
+
' NOTE Machines must have time synchronization.' +
|
|
11
|
+
' NOTE This requirement must be set sufficient, i.e. enough to finish copy file process')
|
|
12
|
+
end # module TestingServer
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
require 'params'
|
|
2
|
+
require 'log'
|
|
3
|
+
require 'content_data'
|
|
4
|
+
require 'file_indexing/index_agent'
|
|
5
|
+
|
|
6
|
+
# TODO additional params?
|
|
7
|
+
# TODO are params names good enough?
|
|
8
|
+
module Validations
|
|
9
|
+
class IndexValidations
|
|
10
|
+
|
|
11
|
+
# TODO should it be renamed cause it can validate any indexes, not specially remote, one against another.
|
|
12
|
+
|
|
13
|
+
# Validates remote index against local file system.
|
|
14
|
+
# Remote index defined valid when every remote content has at least one valid local instance.
|
|
15
|
+
#
|
|
16
|
+
# There are two levels of validation, controlled by instance_check_level system parameter:
|
|
17
|
+
# * shallow - quick, tests instance for file existence and attributes.
|
|
18
|
+
# * deep - can take more time, in addition to shallow recalculates hash sum.
|
|
19
|
+
# For more infoemation see <tt>ContentData</tt> documentation.
|
|
20
|
+
# @param [ContentData] remote_index it's contents should be validate against local index
|
|
21
|
+
# @param [ContentData] local_index contains local contents and instances used for validation
|
|
22
|
+
# @param [Hash] params hash of parameters of validation, can be used to return additional data
|
|
23
|
+
#
|
|
24
|
+
# Supported key/value combinations:
|
|
25
|
+
# * key is <tt>:failed</tt> value is <tt>ContentData</tt> used to return failed instances
|
|
26
|
+
# @return [Boolean] true when every remote content has at least one valid local instance, false otherwise
|
|
27
|
+
# @raise [ArgumentError] when instance_check_level is incorrect
|
|
28
|
+
def IndexValidations.validate_remote_index(remote_index, local_index, params = nil)
|
|
29
|
+
raise ArgumentError 'remote_index must be defined' if remote_index.nil?
|
|
30
|
+
raise ArgumentError 'local index must be defined' if local_index.nil?
|
|
31
|
+
|
|
32
|
+
# used to answer whether specific param was set
|
|
33
|
+
param_exists = Proc.new do |param|
|
|
34
|
+
!(params.nil? || params[param].nil?)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# used to process method parameters centrally
|
|
38
|
+
# TODO consider more convenient form of parameters
|
|
39
|
+
# TODO code duplication with ContentData::validate
|
|
40
|
+
process_params = Proc.new do |checksum, content_mtime, size, instance_mtime, server, path|
|
|
41
|
+
if param_exists.call(:failed)
|
|
42
|
+
params[:failed].add_instance(checksum, size, server, path, instance_mtime)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# result variable
|
|
47
|
+
is_valid = true
|
|
48
|
+
# contains contents absent or without valid instances on local system
|
|
49
|
+
# and corresponding local instances
|
|
50
|
+
failed = ContentData::ContentData.new
|
|
51
|
+
# contains checksums of contains absent in local
|
|
52
|
+
absent_checksums = Array.new
|
|
53
|
+
|
|
54
|
+
# contains local instances corresponding to given remote index contents
|
|
55
|
+
local_index_to_check = ContentData::ContentData.new
|
|
56
|
+
|
|
57
|
+
# check whether remote contents exist locally
|
|
58
|
+
remote_index.each_content do |checksum|
|
|
59
|
+
unless local_index.content_exists checksum
|
|
60
|
+
is_valid = false
|
|
61
|
+
absent_checksums << checksum
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# add instances of contents that should be check, i.e. exist in local
|
|
66
|
+
local_index.each_instance do |checksum, size, content_mtime, instance_mtime, server, path|
|
|
67
|
+
if remote_index.content_exists checksum
|
|
68
|
+
local_index_to_check.add_instance checksum, size, server, path, instance_mtime
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
# validate against local file system
|
|
74
|
+
is_valid = local_index_to_check.validate(:failed => failed) && is_valid
|
|
75
|
+
|
|
76
|
+
# contains contents that have at least one valid local instance,
|
|
77
|
+
# then also corresponding remote content defined valid
|
|
78
|
+
contents_with_succeeded_instances = ContentData.remove_instances failed, local_index
|
|
79
|
+
|
|
80
|
+
# write summary to log
|
|
81
|
+
# TODO should be controlled from params?
|
|
82
|
+
absent_checksums.each do |checksum|
|
|
83
|
+
Log.warning "Content #{checksum} is absent in the local file system"
|
|
84
|
+
end
|
|
85
|
+
failed.each_content do |checksum|
|
|
86
|
+
if !contents_with_succeeded_instances.content_exists checksum
|
|
87
|
+
# if checked content have no valid local instances
|
|
88
|
+
# then content defined invalid
|
|
89
|
+
Log.warning "Content #{checksum} is invalid in the local file system"
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# if needed process output params
|
|
94
|
+
unless params.nil? || params.empty?
|
|
95
|
+
remote_index.each_instance do |checksum, size, content_mtime, instance_mtime, server, path|
|
|
96
|
+
unless contents_with_succeeded_instances.content_exists checksum
|
|
97
|
+
process_params.call checksum, content_mtime, size, instance_mtime, server, path
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
is_valid
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
end # class IndexValidations
|
|
106
|
+
end # module Validations
|
data/lib/validations.rb
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
require 'rspec'
|
|
2
|
+
require 'file_indexing/index_agent'
|
|
3
|
+
require 'params'
|
|
4
|
+
require_relative '../../lib/content_data.rb'
|
|
5
|
+
|
|
6
|
+
module ContentData
|
|
7
|
+
module Spec
|
|
8
|
+
describe 'Index validation' do
|
|
9
|
+
before :all do
|
|
10
|
+
@size = 100
|
|
11
|
+
@path = '/dir1/dir2/file'
|
|
12
|
+
@path2 = '/dir3/dir4/file'
|
|
13
|
+
@mod_time = 123456
|
|
14
|
+
@checksum = 'abcde987654321'
|
|
15
|
+
@checksum2 = '987654321abcde'
|
|
16
|
+
@server = 'server_1'
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
before :each do
|
|
20
|
+
@index = ContentData.new
|
|
21
|
+
@index.add_instance @checksum, @size, @server, @path, @mod_time
|
|
22
|
+
@index.add_instance @checksum2, @size, @server, @path2, @mod_time
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
context 'with shallow check' do
|
|
26
|
+
before :all do
|
|
27
|
+
Params['instance_check_level'] = 'shallow'
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
before :each do
|
|
31
|
+
File.stub(:exists?).and_return(true)
|
|
32
|
+
File.stub(:size).and_return(@size)
|
|
33
|
+
File.stub(:mtime).and_return(@mod_time)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it 'succeeds when files exist and their attributes the same as in the index' do
|
|
37
|
+
@index.validate().should be_true
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it 'fails when there is a file that doesn\'t exist' do
|
|
41
|
+
File.stub(:exists?).with(@path).and_return(false)
|
|
42
|
+
@index.validate().should be_false
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it 'fails when there is a file with size different from indexed' do
|
|
46
|
+
File.stub(:size).with(@path).and_return(@size + 10)
|
|
47
|
+
@index.validate().should be_false
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it 'fails when there is a file with modification time different from indexed' do
|
|
51
|
+
modified_mtime = @mod_time + 10
|
|
52
|
+
File.stub(:mtime).with(@path).and_return(modified_mtime)
|
|
53
|
+
@index.validate().should be_false
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
context 'with deep check (shallow check assumed succeeded)' do
|
|
58
|
+
before :all do
|
|
59
|
+
Params['instance_check_level'] = 'deep'
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
before :each do
|
|
63
|
+
FileIndexing::IndexAgent.stub(:get_checksum).with(@path).and_return(@checksum)
|
|
64
|
+
FileIndexing::IndexAgent.stub(:get_checksum).with(@path2).and_return(@checksum2)
|
|
65
|
+
@index.stub(:shallow_check).and_return(true)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it 'succeeds when for all files checksums are the same as indexed' do
|
|
69
|
+
@index.validate().should be_true
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it 'fails when there is a file with checksum different from indexed' do
|
|
73
|
+
FileIndexing::IndexAgent.stub(:get_checksum).with(@path2).and_return(@checksum2 + 'a')
|
|
74
|
+
@index.validate().should be_false
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
context 'validation params' do
|
|
79
|
+
before :all do
|
|
80
|
+
Params['instance_check_level'] = 'shallow'
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
before :each do
|
|
84
|
+
File.stub(:exists?).and_return(true)
|
|
85
|
+
File.stub(:size).and_return(@size)
|
|
86
|
+
File.stub(:mtime).and_return(@mod_time)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
it ':failed param returns index of absent or invalid instances' do
|
|
90
|
+
# one instance that absent,
|
|
91
|
+
absent_checksum = '123'
|
|
92
|
+
absent_path = @path2 + '1'
|
|
93
|
+
@index.add_instance absent_checksum, @size, @server, absent_path, @mod_time
|
|
94
|
+
File.stub(:exists?).with(absent_path).and_return(false)
|
|
95
|
+
|
|
96
|
+
# another instance with different content was changed
|
|
97
|
+
File.stub(:mtime).with(@path2).and_return(@mod_time + 10)
|
|
98
|
+
|
|
99
|
+
failed = ContentData.new
|
|
100
|
+
@index.validate(:failed => failed).should be_false
|
|
101
|
+
# should be two failed contents, with one instance per content
|
|
102
|
+
failed.contents_size.should eq(2)
|
|
103
|
+
failed.content_exists(absent_checksum).should be_true
|
|
104
|
+
failed.content_exists(@checksum2).should be_true
|
|
105
|
+
failed.checksum_instances_size(absent_checksum).should eq(1)
|
|
106
|
+
failed.checksum_instances_size(@checksum2).should eq(1)
|
|
107
|
+
failed.instance_exists(absent_path, @server, absent_checksum).should be_true
|
|
108
|
+
failed.instance_exists(@path2, @server, @checksum2).should be_true
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
require 'rspec'
|
|
2
|
+
|
|
3
|
+
require_relative '../../lib/file_copy/copy.rb'
|
|
4
|
+
|
|
5
|
+
module FileCopy
|
|
6
|
+
module Spec
|
|
7
|
+
|
|
8
|
+
describe 'FileCopy::ssh_connect' do
|
|
9
|
+
it 'should raise error when username not specified' do
|
|
10
|
+
ENV.stub(:[]).with(any_args()).and_return(nil)
|
|
11
|
+
expect { FileCopy::ssh_connect(nil, nil, 'a server') }.to raise_error "Undefined username"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it 'should raise error when server not specified' do
|
|
15
|
+
expect { FileCopy::ssh_connect('kuku', nil, nil) }.to raise_error "Undefined server"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it 'should try to connect if username is set explicitly' do
|
|
19
|
+
Net::SSH.should_receive(:start).with(any_args())
|
|
20
|
+
FileCopy::ssh_connect('kuku', nil, 'a server')
|
|
21
|
+
end
|
|
22
|
+
it 'should try to connect if username is set via ENV variable' do
|
|
23
|
+
Net::SSH.should_receive(:start).with(any_args())
|
|
24
|
+
ENV.stub(:[]).with("USER").and_return('kuku')
|
|
25
|
+
FileCopy::ssh_connect(nil, nil, 'a server')
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# TODO(kolman): Bad test, should rewrite and understand how to write test correctly.
|
|
30
|
+
describe 'FileCopy::sftp_copy' do
|
|
31
|
+
it 'call upload with correct files' do
|
|
32
|
+
ssh_connection = double('Net::SSH::Connection')
|
|
33
|
+
sftp_session = double('Net::SFTP::Session')
|
|
34
|
+
uploader = double('Operations::Upload')
|
|
35
|
+
sftp_attributes = double('Attributes')
|
|
36
|
+
|
|
37
|
+
# Stubbing sftp.
|
|
38
|
+
FileCopy.stub(:ssh_connect).and_return(ssh_connection)
|
|
39
|
+
ssh_connection.stub(:sftp).and_return(sftp_session)
|
|
40
|
+
sftp_session.stub(:connect).and_yield(sftp_session)
|
|
41
|
+
sftp_session.stub(:stat!).and_return(sftp_attributes)
|
|
42
|
+
sftp_attributes.stub(:directory?).and_return(true)
|
|
43
|
+
uploader.stub(:wait).and_return(true, true)
|
|
44
|
+
|
|
45
|
+
# Test file uploaded
|
|
46
|
+
sftp_session.should_receive(:upload).with('a', 'b').and_return(uploader)
|
|
47
|
+
sftp_session.should_receive(:upload).with('c', 'd').and_return(uploader)
|
|
48
|
+
FileCopy::sftp_copy(nil, nil, nil, { 'a' => 'b', 'c' => 'd' })
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
require 'rspec'
|
|
2
|
+
require 'tempfile'
|
|
3
|
+
|
|
4
|
+
require_relative '../../lib/file_indexing/index_agent.rb'
|
|
5
|
+
|
|
6
|
+
module FileCopy
|
|
7
|
+
module Spec
|
|
8
|
+
|
|
9
|
+
describe 'checksum' do
|
|
10
|
+
it 'should generate correct checksum' do
|
|
11
|
+
# The test does not checks the problem the problem is when reading from File
|
|
12
|
+
# class which handles read(num) different from read()
|
|
13
|
+
content = ''
|
|
14
|
+
100000.times { content << 'abagadavazahatikalamansapazkareshet' }
|
|
15
|
+
content_checksum = FileIndexing::IndexAgent.get_content_checksum(content)
|
|
16
|
+
|
|
17
|
+
stream = StringIO.new(content)
|
|
18
|
+
File.stub(:open).and_yield(stream)
|
|
19
|
+
file_checksum = FileIndexing::IndexAgent.get_checksum('kuku')
|
|
20
|
+
|
|
21
|
+
content_checksum.should == file_checksum
|
|
22
|
+
content_checksum.should == '381e99eb0e2dfcaf45c9a367a04a4197ef3039a6'
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it 'should generate correct checksum for temp file' do
|
|
26
|
+
# A hack to get tmp file name
|
|
27
|
+
tmp_file = Tempfile.new('foo')
|
|
28
|
+
path = tmp_file .path
|
|
29
|
+
tmp_file .close()
|
|
30
|
+
|
|
31
|
+
# Open file in binary mode.
|
|
32
|
+
file = File.open(path, 'wb')
|
|
33
|
+
100000.times { file.write('abagadavazahatikalamansapazkareshet') }
|
|
34
|
+
file.close()
|
|
35
|
+
|
|
36
|
+
file_checksum = FileIndexing::IndexAgent.get_checksum(path)
|
|
37
|
+
file_checksum.should == '381e99eb0e2dfcaf45c9a367a04a4197ef3039a6'
|
|
38
|
+
|
|
39
|
+
File.open(path, 'rb') { |f|
|
|
40
|
+
content = f.read()
|
|
41
|
+
content_checksum = FileIndexing::IndexAgent.get_content_checksum(content)
|
|
42
|
+
content_checksum.should == '381e99eb0e2dfcaf45c9a367a04a4197ef3039a6'
|
|
43
|
+
file_checksum.should == content_checksum
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
# Delete tmp file.
|
|
47
|
+
tmp_file.unlink
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
require 'log'
|
|
2
|
+
require 'rspec'
|
|
3
|
+
require 'socket'
|
|
4
|
+
require 'stringio'
|
|
5
|
+
|
|
6
|
+
require_relative '../../lib/networking/tcp'
|
|
7
|
+
|
|
8
|
+
# Uncomment to debug spec.
|
|
9
|
+
Params['log_write_to_console'] = true
|
|
10
|
+
Params['log_debug_level'] = 0
|
|
11
|
+
Log.init
|
|
12
|
+
|
|
13
|
+
module Networking
|
|
14
|
+
module Spec
|
|
15
|
+
|
|
16
|
+
describe 'TCPClient' do
|
|
17
|
+
it 'should warn when destination host+port are bad' do
|
|
18
|
+
data = 'kuku!!!'
|
|
19
|
+
stream = StringIO.new
|
|
20
|
+
stream.close()
|
|
21
|
+
::TCPSocket.stub(:new).and_return(stream)
|
|
22
|
+
Log.should_receive(:warning).with('Socket not opened for writing, skipping send.')
|
|
23
|
+
|
|
24
|
+
# Send data first.
|
|
25
|
+
tcp_client = TCPClient.new('kuku', 5555)
|
|
26
|
+
# Send has to fail.
|
|
27
|
+
tcp_client.send_obj(data).should be(false)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it 'should send data and server should receive callback function' do
|
|
31
|
+
info = 'info'
|
|
32
|
+
data = 'kuku!!!'
|
|
33
|
+
stream = StringIO.new
|
|
34
|
+
::Socket.stub(:tcp_server_loop).and_yield(stream, info)
|
|
35
|
+
::TCPSocket.stub(:new).and_return(stream)
|
|
36
|
+
|
|
37
|
+
# Send data first.
|
|
38
|
+
tcp_client = TCPClient.new('kuku', 5555)
|
|
39
|
+
# Send has to be successful.
|
|
40
|
+
tcp_client.send_obj(data).should be(21)
|
|
41
|
+
|
|
42
|
+
# Note this is very important so that reading the stream from beginning.
|
|
43
|
+
stream.rewind
|
|
44
|
+
|
|
45
|
+
func = lambda { |info, data| Log.info("info, data: #{info}, #{data}") }
|
|
46
|
+
# Check data is received.
|
|
47
|
+
func.should_receive(:call).with(info, data)
|
|
48
|
+
|
|
49
|
+
tcp_server = TCPServer.new(5555, func)
|
|
50
|
+
# Wait on server thread.
|
|
51
|
+
tcp_server.tcp_thread.join
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# TODO(kolman): Don't work, missing client send_obj/open_socket execution in
|
|
55
|
+
# the correct place.
|
|
56
|
+
it 'should connect and receive callback from server' do
|
|
57
|
+
info = 'info'
|
|
58
|
+
data = 'kuku!!!'
|
|
59
|
+
stream = StringIO.new
|
|
60
|
+
::Socket.stub(:tcp_server_loop).once.and_yield(stream, info)
|
|
61
|
+
::TCPSocket.stub(:new).and_return(stream)
|
|
62
|
+
|
|
63
|
+
tcp_server = nil
|
|
64
|
+
new_clb = lambda { |i|
|
|
65
|
+
Log.info("#2 - After after @sockets is filled. Send has to be successful.")
|
|
66
|
+
tcp_server.send_obj(data).should eq({info => 21})
|
|
67
|
+
|
|
68
|
+
stream.rewind
|
|
69
|
+
|
|
70
|
+
func = lambda { |d|
|
|
71
|
+
Log.info("#6, After client initialized, it should send object.")
|
|
72
|
+
Log.info("data: #{d}")
|
|
73
|
+
# Validate received data.
|
|
74
|
+
d.should eq(data)
|
|
75
|
+
# Exit tcp client reading loop thread.
|
|
76
|
+
Thread.exit
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
Log.info("#4 - Create client and wait for read data.")
|
|
80
|
+
tcp_client = TCPClient.new('kuku', 5555, func)
|
|
81
|
+
tcp_client.tcp_thread.abort_on_exception = true
|
|
82
|
+
tcp_client.tcp_thread.join()
|
|
83
|
+
# We can finish now. Exit tcp server listening loop.
|
|
84
|
+
Thread.exit
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
Log.info("#1 - Create server, send data and wait.")
|
|
88
|
+
tcp_server = TCPServer.new(5555, nil, new_clb)
|
|
89
|
+
tcp_server.tcp_thread.abort_on_exception = true
|
|
90
|
+
tcp_server.tcp_thread.join()
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|