Merge lp:~david-goetz/swift/zero_byte_obj_audit into lp:~hudson-openstack/swift/trunk
- zero_byte_obj_audit
- Merge into trunk
Proposed by
David Goetz
Status: | Rejected |
---|---|
Rejected by: | David Goetz |
Proposed branch: | lp:~david-goetz/swift/zero_byte_obj_audit |
Merge into: | lp:~hudson-openstack/swift/trunk |
Diff against target: |
819 lines (+204/-108) 22 files modified
bin/swift-log-uploader (+1/-1) bin/swift-object-auditor (+7/-1) doc/source/admin_guide.rst (+14/-0) swift/account/auditor.py (+2/-3) swift/account/reaper.py (+2/-3) swift/account/server.py (+2/-3) swift/common/bench.py (+3/-3) swift/common/daemon.py (+6/-9) swift/common/db_replicator.py (+4/-5) swift/common/middleware/auth.py (+2/-2) swift/common/utils.py (+7/-5) swift/container/auditor.py (+2/-3) swift/container/server.py (+2/-3) swift/container/updater.py (+2/-3) swift/obj/auditor.py (+66/-23) swift/obj/replicator.py (+3/-5) swift/obj/server.py (+2/-3) swift/obj/updater.py (+2/-3) swift/stats/account_stats.py (+2/-2) swift/stats/log_uploader.py (+1/-1) test/unit/common/test_utils.py (+6/-16) test/unit/obj/test_auditor.py (+66/-11) |
To merge this branch: | bzr merge lp:~david-goetz/swift/zero_byte_obj_audit |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Swift Core security contacts | Pending | ||
Review via email: mp+48862@code.launchpad.net |
Commit message
Description of the change
Add a way to speed up object-auditor to check for zero byte files.
To post a comment you must log in.
Revision history for this message
gholt (gholt) wrote : | # |
Unmerged revisions
- 206. By David Goetz
-
merge up to trunk
- 205. By David Goetz
-
fixes and true vals refactor
- 204. By David Goetz
-
object auditor to quickly look for zero byte files
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'bin/swift-log-uploader' |
2 | --- bin/swift-log-uploader 2011-01-04 23:34:43 +0000 |
3 | +++ bin/swift-log-uploader 2011-02-08 00:11:22 +0000 |
4 | @@ -24,7 +24,7 @@ |
5 | conf_file, options = parse_options(usage="Usage: %prog CONFIG_FILE PLUGIN") |
6 | try: |
7 | plugin = options['extra_args'][0] |
8 | - except IndexError: |
9 | + except (KeyError, IndexError): |
10 | print "Error: missing plugin name" |
11 | sys.exit(1) |
12 | |
13 | |
14 | === modified file 'bin/swift-object-auditor' |
15 | --- bin/swift-object-auditor 2011-01-04 23:34:43 +0000 |
16 | +++ bin/swift-object-auditor 2011-02-08 00:11:22 +0000 |
17 | @@ -17,7 +17,13 @@ |
18 | from swift.obj.auditor import ObjectAuditor |
19 | from swift.common.utils import parse_options |
20 | from swift.common.daemon import run_daemon |
21 | +from optparse import OptionParser |
22 | |
23 | if __name__ == '__main__': |
24 | - conf_file, options = parse_options(once=True) |
25 | + parser = OptionParser("%prog CONFIG [options]") |
26 | + parser.add_option('-z', '--zero_byte_only', default=False, |
27 | + action='store_true', help='Audit only zero byte files') |
28 | + parser.add_option('-f', '--zero_byte_fps', |
29 | + help='Override zero byte files per second in config.') |
30 | + conf_file, options = parse_options(parser=parser, once=True) |
31 | run_daemon(ObjectAuditor, conf_file, **options) |
32 | |
33 | === modified file 'doc/source/admin_guide.rst' |
34 | --- doc/source/admin_guide.rst 2010-12-10 01:57:26 +0000 |
35 | +++ doc/source/admin_guide.rst 2011-02-08 00:11:22 +0000 |
36 | @@ -288,3 +288,17 @@ |
37 | completely stopping the old service. There is also a special case of |
38 | `swift-init all <command>`, which will run the command for all swift services. |
39 | |
40 | +-------------- |
41 | +Object Auditor |
42 | +-------------- |
43 | + |
44 | +On system failures, the XFS file system can sometimes truncate files it's |
45 | +trying to write and produce zero byte files. The object-auditor will catch |
46 | +these problems but in the case of a system crash it would be advisable to run |
47 | +an extra, less rate limited sweep to check for these specific files. You can |
48 | +run this command as follows: |
49 | +`swift-object-auditor /path/to/object-server/config/file.conf once -z -f 1000` |
50 | +"-z" will check for only zero-byte files and "-f" overrides the |
51 | +zero_byte_files_per_second to a 1000 from the config file, which by default is |
52 | +only 50. |
53 | + |
54 | |
55 | === modified file 'swift/account/auditor.py' |
56 | --- swift/account/auditor.py 2011-01-04 23:34:43 +0000 |
57 | +++ swift/account/auditor.py 2011-02-08 00:11:22 +0000 |
58 | @@ -19,7 +19,7 @@ |
59 | |
60 | from swift.account import server as account_server |
61 | from swift.common.db import AccountBroker |
62 | -from swift.common.utils import get_logger, audit_location_generator |
63 | +from swift.common.utils import get_logger, audit_location_generator, TRUE_VALS |
64 | from swift.common.daemon import Daemon |
65 | |
66 | |
67 | @@ -30,8 +30,7 @@ |
68 | self.conf = conf |
69 | self.logger = get_logger(conf, 'account-auditor') |
70 | self.devices = conf.get('devices', '/srv/node') |
71 | - self.mount_check = conf.get('mount_check', 'true').lower() in \ |
72 | - ('true', 't', '1', 'on', 'yes', 'y') |
73 | + self.mount_check = conf.get('mount_check', 'true').lower() in TRUE_VALS |
74 | self.interval = int(conf.get('interval', 1800)) |
75 | self.account_passes = 0 |
76 | self.account_failures = 0 |
77 | |
78 | === modified file 'swift/account/reaper.py' |
79 | --- swift/account/reaper.py 2011-01-19 23:21:57 +0000 |
80 | +++ swift/account/reaper.py 2011-02-08 00:11:22 +0000 |
81 | @@ -26,7 +26,7 @@ |
82 | from swift.common.direct_client import ClientException, \ |
83 | direct_delete_container, direct_delete_object, direct_get_container |
84 | from swift.common.ring import Ring |
85 | -from swift.common.utils import get_logger, whataremyips |
86 | +from swift.common.utils import get_logger, whataremyips, TRUE_VALS |
87 | from swift.common.daemon import Daemon |
88 | |
89 | |
90 | @@ -55,8 +55,7 @@ |
91 | self.conf = conf |
92 | self.logger = get_logger(conf) |
93 | self.devices = conf.get('devices', '/srv/node') |
94 | - self.mount_check = conf.get('mount_check', 'true').lower() in \ |
95 | - ('true', 't', '1', 'on', 'yes', 'y') |
96 | + self.mount_check = conf.get('mount_check', 'true').lower() in TRUE_VALS |
97 | self.interval = int(conf.get('interval', 3600)) |
98 | swift_dir = conf.get('swift_dir', '/etc/swift') |
99 | self.account_ring_path = os.path.join(swift_dir, 'account.ring.gz') |
100 | |
101 | === modified file 'swift/account/server.py' |
102 | --- swift/account/server.py 2011-01-26 22:31:33 +0000 |
103 | +++ swift/account/server.py 2011-02-08 00:11:22 +0000 |
104 | @@ -29,7 +29,7 @@ |
105 | |
106 | from swift.common.db import AccountBroker |
107 | from swift.common.utils import get_logger, get_param, hash_path, \ |
108 | - normalize_timestamp, split_path, storage_directory |
109 | + normalize_timestamp, split_path, storage_directory, TRUE_VALS |
110 | from swift.common.constraints import ACCOUNT_LISTING_LIMIT, \ |
111 | check_mount, check_float, check_utf8 |
112 | from swift.common.db_replicator import ReplicatorRpc |
113 | @@ -44,8 +44,7 @@ |
114 | def __init__(self, conf): |
115 | self.logger = get_logger(conf) |
116 | self.root = conf.get('devices', '/srv/node') |
117 | - self.mount_check = conf.get('mount_check', 'true').lower() in \ |
118 | - ('true', 't', '1', 'on', 'yes', 'y') |
119 | + self.mount_check = conf.get('mount_check', 'true').lower() in TRUE_VALS |
120 | self.replicator_rpc = \ |
121 | ReplicatorRpc(self.root, DATADIR, AccountBroker, self.mount_check) |
122 | |
123 | |
124 | === modified file 'swift/common/bench.py' |
125 | --- swift/common/bench.py 2011-01-26 22:31:33 +0000 |
126 | +++ swift/common/bench.py 2011-02-08 00:11:22 +0000 |
127 | @@ -22,7 +22,7 @@ |
128 | import eventlet.pools |
129 | from eventlet.green.httplib import CannotSendRequest |
130 | |
131 | -from swift.common.utils import TRUE_VALUES |
132 | +from swift.common.utils import TRUE_VALS |
133 | from swift.common import client |
134 | from swift.common import direct_client |
135 | |
136 | @@ -44,7 +44,7 @@ |
137 | self.user = conf.user |
138 | self.key = conf.key |
139 | self.auth_url = conf.auth |
140 | - self.use_proxy = conf.use_proxy in TRUE_VALUES |
141 | + self.use_proxy = conf.use_proxy in TRUE_VALS |
142 | if self.use_proxy: |
143 | url, token = client.get_auth(self.auth_url, self.user, self.key) |
144 | self.token = token |
145 | @@ -126,7 +126,7 @@ |
146 | self.logger = logger |
147 | self.conf = conf |
148 | self.names = [] |
149 | - self.delete = conf.delete in TRUE_VALUES |
150 | + self.delete = conf.delete in TRUE_VALS |
151 | self.gets = int(conf.num_gets) |
152 | |
153 | def run(self): |
154 | |
155 | === modified file 'swift/common/daemon.py' |
156 | --- swift/common/daemon.py 2011-01-04 23:34:43 +0000 |
157 | +++ swift/common/daemon.py 2011-02-08 00:11:22 +0000 |
158 | @@ -41,22 +41,19 @@ |
159 | utils.validate_configuration() |
160 | utils.capture_stdio(self.logger, **kwargs) |
161 | utils.drop_privileges(self.conf.get('user', 'swift')) |
162 | - |
163 | def kill_children(*args): |
164 | signal.signal(signal.SIGTERM, signal.SIG_IGN) |
165 | os.killpg(0, signal.SIGTERM) |
166 | sys.exit() |
167 | |
168 | signal.signal(signal.SIGTERM, kill_children) |
169 | - |
170 | if once: |
171 | - self.run_once() |
172 | + self.run_once(**kwargs) |
173 | else: |
174 | - self.run_forever() |
175 | - |
176 | - |
177 | -def run_daemon(klass, conf_file, section_name='', |
178 | - once=False, **kwargs): |
179 | + self.run_forever(**kwargs) |
180 | + |
181 | + |
182 | +def run_daemon(klass, conf_file, section_name='', once=False, **kwargs): |
183 | """ |
184 | Loads settings from conf, then instantiates daemon "klass" and runs the |
185 | daemon with the specified once kwarg. The section_name will be derived |
186 | @@ -77,7 +74,7 @@ |
187 | log_name=kwargs.get('log_name')) |
188 | |
189 | # once on command line (i.e. daemonize=false) will over-ride config |
190 | - once = once or conf.get('daemonize', 'true') not in utils.TRUE_VALUES |
191 | + once = once or conf.get('daemonize', 'true').lower() not in utils.TRUE_VALS |
192 | |
193 | # pre-configure logger |
194 | if 'logger' in kwargs: |
195 | |
196 | === modified file 'swift/common/db_replicator.py' |
197 | --- swift/common/db_replicator.py 2011-01-26 22:31:33 +0000 |
198 | +++ swift/common/db_replicator.py 2011-02-08 00:11:22 +0000 |
199 | @@ -29,7 +29,8 @@ |
200 | HTTPInsufficientStorage, HTTPBadRequest |
201 | |
202 | from swift.common.utils import get_logger, whataremyips, storage_directory, \ |
203 | - renamer, mkdirs, lock_parent_directory, unlink_older_than, LoggerFileObject |
204 | + renamer, mkdirs, lock_parent_directory, unlink_older_than, \ |
205 | + LoggerFileObject, TRUE_VALS |
206 | from swift.common import ring |
207 | from swift.common.bufferedhttp import BufferedHTTPConnection |
208 | from swift.common.exceptions import DriveNotMounted, ConnectionTimeout |
209 | @@ -94,8 +95,7 @@ |
210 | self.conf = conf |
211 | self.logger = get_logger(conf) |
212 | self.root = conf.get('devices', '/srv/node') |
213 | - self.mount_check = conf.get('mount_check', 'true').lower() in \ |
214 | - ('true', 't', '1', 'on', 'yes', 'y') |
215 | + self.mount_check = conf.get('mount_check', 'true').lower() in TRUE_VALS |
216 | self.port = int(conf.get('bind_port', self.default_port)) |
217 | concurrency = int(conf.get('concurrency', 8)) |
218 | self.cpool = GreenPool(size=concurrency) |
219 | @@ -103,8 +103,7 @@ |
220 | self.ring = ring.Ring(os.path.join(swift_dir, self.ring_file)) |
221 | self.per_diff = int(conf.get('per_diff', 1000)) |
222 | self.run_pause = int(conf.get('run_pause', 30)) |
223 | - self.vm_test_mode = conf.get( |
224 | - 'vm_test_mode', 'no').lower() in ('yes', 'true', 'on', '1') |
225 | + self.vm_test_mode = conf.get('vm_test_mode', 'no').lower() in TRUE_VALS |
226 | self.node_timeout = int(conf.get('node_timeout', 10)) |
227 | self.conn_timeout = float(conf.get('conn_timeout', 0.5)) |
228 | self.reclaim_age = float(conf.get('reclaim_age', 86400 * 7)) |
229 | |
230 | === modified file 'swift/common/middleware/auth.py' |
231 | --- swift/common/middleware/auth.py 2011-01-25 05:39:00 +0000 |
232 | +++ swift/common/middleware/auth.py 2011-02-08 00:11:22 +0000 |
233 | @@ -20,7 +20,7 @@ |
234 | |
235 | from swift.common.bufferedhttp import http_connect_raw as http_connect |
236 | from swift.common.middleware.acl import clean_acl, parse_acl, referrer_allowed |
237 | -from swift.common.utils import cache_from_env, split_path, TRUE_VALUES |
238 | +from swift.common.utils import cache_from_env, split_path, TRUE_VALS |
239 | |
240 | |
241 | class DevAuth(object): |
242 | @@ -34,7 +34,7 @@ |
243 | self.reseller_prefix += '_' |
244 | self.auth_host = conf.get('ip', '127.0.0.1') |
245 | self.auth_port = int(conf.get('port', 11000)) |
246 | - self.ssl = conf.get('ssl', 'false').lower() in TRUE_VALUES |
247 | + self.ssl = conf.get('ssl', 'false').lower() in TRUE_VALS |
248 | self.auth_prefix = conf.get('prefix', '/') |
249 | self.timeout = int(conf.get('node_timeout', 10)) |
250 | |
251 | |
252 | === modified file 'swift/common/utils.py' |
253 | --- swift/common/utils.py 2011-02-02 18:47:56 +0000 |
254 | +++ swift/common/utils.py 2011-02-08 00:11:22 +0000 |
255 | @@ -66,7 +66,7 @@ |
256 | pass |
257 | |
258 | # Used when reading config values |
259 | -TRUE_VALUES = set(('true', '1', 'yes', 'True', 'Yes', 'on', 'On')) |
260 | +TRUE_VALS = set(('true', 't', '1', 'yes', 'y', 'True', 'Yes', 'on', 'On')) |
261 | |
262 | |
263 | def validate_configuration(): |
264 | @@ -490,11 +490,11 @@ |
265 | sys.stderr = LoggerFileObject(logger) |
266 | |
267 | |
268 | -def parse_options(usage="%prog CONFIG [options]", once=False, test_args=None): |
269 | +def parse_options(parser=None, once=False, test_args=None): |
270 | """ |
271 | Parse standard swift server/daemon options with optparse.OptionParser. |
272 | |
273 | - :param usage: String describing usage |
274 | + :param parser: OptionParser to use. If not sent one will be created. |
275 | :param once: Boolean indicating the "once" option is available |
276 | :param test_args: Override sys.argv; used in testing |
277 | |
278 | @@ -503,7 +503,8 @@ |
279 | |
280 | :raises SystemExit: First arg (CONFIG) is required, file must exist |
281 | """ |
282 | - parser = OptionParser(usage) |
283 | + if not parser: |
284 | + parser = OptionParser(usage="%prog CONFIG [options]") |
285 | parser.add_option("-v", "--verbose", default=False, action="store_true", |
286 | help="log to console") |
287 | if once: |
288 | @@ -532,7 +533,8 @@ |
289 | extra_args.append(arg) |
290 | |
291 | options = vars(options) |
292 | - options['extra_args'] = extra_args |
293 | + if extra_args: |
294 | + options['extra_args'] = extra_args |
295 | return config, options |
296 | |
297 | |
298 | |
299 | === modified file 'swift/container/auditor.py' |
300 | --- swift/container/auditor.py 2011-01-04 23:34:43 +0000 |
301 | +++ swift/container/auditor.py 2011-02-08 00:11:22 +0000 |
302 | @@ -19,7 +19,7 @@ |
303 | |
304 | from swift.container import server as container_server |
305 | from swift.common.db import ContainerBroker |
306 | -from swift.common.utils import get_logger, audit_location_generator |
307 | +from swift.common.utils import get_logger, audit_location_generator, TRUE_VALS |
308 | from swift.common.daemon import Daemon |
309 | |
310 | |
311 | @@ -30,8 +30,7 @@ |
312 | self.conf = conf |
313 | self.logger = get_logger(conf, 'container-auditor') |
314 | self.devices = conf.get('devices', '/srv/node') |
315 | - self.mount_check = conf.get('mount_check', 'true').lower() in \ |
316 | - ('true', 't', '1', 'on', 'yes', 'y') |
317 | + self.mount_check = conf.get('mount_check', 'true').lower() in TRUE_VALS |
318 | self.interval = int(conf.get('interval', 1800)) |
319 | swift_dir = conf.get('swift_dir', '/etc/swift') |
320 | self.container_passes = 0 |
321 | |
322 | === modified file 'swift/container/server.py' |
323 | --- swift/container/server.py 2011-01-26 22:31:33 +0000 |
324 | +++ swift/container/server.py 2011-02-08 00:11:22 +0000 |
325 | @@ -32,7 +32,7 @@ |
326 | |
327 | from swift.common.db import ContainerBroker |
328 | from swift.common.utils import get_logger, get_param, hash_path, \ |
329 | - normalize_timestamp, storage_directory, split_path |
330 | + normalize_timestamp, storage_directory, split_path, TRUE_VALS |
331 | from swift.common.constraints import CONTAINER_LISTING_LIMIT, \ |
332 | check_mount, check_float, check_utf8 |
333 | from swift.common.bufferedhttp import http_connect |
334 | @@ -51,8 +51,7 @@ |
335 | def __init__(self, conf): |
336 | self.logger = get_logger(conf) |
337 | self.root = conf.get('devices', '/srv/node/') |
338 | - self.mount_check = conf.get('mount_check', 'true').lower() in \ |
339 | - ('true', 't', '1', 'on', 'yes', 'y') |
340 | + self.mount_check = conf.get('mount_check', 'true').lower() in TRUE_VALS |
341 | self.node_timeout = int(conf.get('node_timeout', 3)) |
342 | self.conn_timeout = float(conf.get('conn_timeout', 0.5)) |
343 | self.replicator_rpc = ReplicatorRpc(self.root, DATADIR, |
344 | |
345 | === modified file 'swift/container/updater.py' |
346 | --- swift/container/updater.py 2011-01-26 22:38:13 +0000 |
347 | +++ swift/container/updater.py 2011-02-08 00:11:22 +0000 |
348 | @@ -28,7 +28,7 @@ |
349 | from swift.common.db import ContainerBroker |
350 | from swift.common.exceptions import ConnectionTimeout |
351 | from swift.common.ring import Ring |
352 | -from swift.common.utils import get_logger, whataremyips |
353 | +from swift.common.utils import get_logger, whataremyips, TRUE_VALS |
354 | from swift.common.daemon import Daemon |
355 | |
356 | |
357 | @@ -39,8 +39,7 @@ |
358 | self.conf = conf |
359 | self.logger = get_logger(conf, 'container-updater') |
360 | self.devices = conf.get('devices', '/srv/node') |
361 | - self.mount_check = conf.get('mount_check', 'true').lower() in \ |
362 | - ('true', 't', '1', 'on', 'yes', 'y') |
363 | + self.mount_check = conf.get('mount_check', 'true').lower() in TRUE_VALS |
364 | swift_dir = conf.get('swift_dir', '/etc/swift') |
365 | self.interval = int(conf.get('interval', 300)) |
366 | self.account_ring_path = os.path.join(swift_dir, 'account.ring.gz') |
367 | |
368 | === modified file 'swift/obj/auditor.py' |
369 | --- swift/obj/auditor.py 2011-01-21 01:05:44 +0000 |
370 | +++ swift/obj/auditor.py 2011-02-08 00:11:22 +0000 |
371 | @@ -21,23 +21,33 @@ |
372 | from swift.obj import server as object_server |
373 | from swift.obj.replicator import invalidate_hash |
374 | from swift.common.utils import get_logger, renamer, audit_location_generator, \ |
375 | - ratelimit_sleep |
376 | + ratelimit_sleep, TRUE_VALS |
377 | from swift.common.exceptions import AuditException |
378 | from swift.common.daemon import Daemon |
379 | |
380 | |
381 | -class ObjectAuditor(Daemon): |
382 | - """Audit objects.""" |
383 | - |
384 | - def __init__(self, conf): |
385 | +class AuditorWorker(object): |
386 | + """Walk through file system to audit object""" |
387 | + def __init__(self, conf, zero_byte_file_worker=False, zero_byte_fps=None): |
388 | self.conf = conf |
389 | self.logger = get_logger(conf, 'object-auditor') |
390 | self.devices = conf.get('devices', '/srv/node') |
391 | - self.mount_check = conf.get('mount_check', 'true').lower() in \ |
392 | - ('true', 't', '1', 'on', 'yes', 'y') |
393 | + self.mount_check = conf.get('mount_check', 'true').lower() in TRUE_VALS |
394 | self.max_files_per_second = float(conf.get('files_per_second', 20)) |
395 | self.max_bytes_per_second = float(conf.get('bytes_per_second', |
396 | 10000000)) |
397 | + self.auditor_type = 'ALL' |
398 | + self.fasttrack_zero_byte_files = conf.get( |
399 | + 'fasttrack_zero_byte_files', 'False').lower() in TRUE_VALS |
400 | + self.zero_byte_file_worker = zero_byte_file_worker |
401 | + if self.zero_byte_file_worker: |
402 | + self.fasttrack_zero_byte_files = True |
403 | + if zero_byte_fps: |
404 | + self.max_files_per_second = float(zero_byte_fps) |
405 | + else: |
406 | + self.max_files_per_second = float( |
407 | + conf.get('zero_byte_files_per_second', 50)) |
408 | + self.auditor_type = 'ZBF' |
409 | self.log_time = int(conf.get('log_time', 3600)) |
410 | self.files_running_time = 0 |
411 | self.bytes_running_time = 0 |
412 | @@ -48,18 +58,13 @@ |
413 | self.quarantines = 0 |
414 | self.errors = 0 |
415 | |
416 | - def run_forever(self): |
417 | - """Run the object audit until stopped.""" |
418 | - while True: |
419 | - self.run_once('forever') |
420 | - self.total_bytes_processed = 0 |
421 | - self.total_files_processed = 0 |
422 | - time.sleep(30) |
423 | - |
424 | - def run_once(self, mode='once'): |
425 | - """Run the object audit once.""" |
426 | - self.logger.info(_('Begin object audit "%s" mode' % mode)) |
427 | + def audit_all_objects(self, mode='once'): |
428 | + self.logger.info(_('Begin object audit "%s" mode (%s)' % |
429 | + (mode, self.auditor_type))) |
430 | begin = reported = time.time() |
431 | + self.total_bytes_processed = 0 |
432 | + self.total_files_processed = 0 |
433 | + files_running_time = 0 |
434 | all_locs = audit_location_generator(self.devices, |
435 | object_server.DATADIR, |
436 | mount_check=self.mount_check, |
437 | @@ -71,9 +76,11 @@ |
438 | self.total_files_processed += 1 |
439 | if time.time() - reported >= self.log_time: |
440 | self.logger.info(_( |
441 | - 'Since %(start_time)s: Locally: %(passes)d passed audit, ' |
442 | + 'Object audit (%(type)s). ' |
443 | + 'Since %(start_time)s: Locally: %(passes)d passed, ' |
444 | '%(quars)d quarantined, %(errors)d errors ' |
445 | 'files/sec: %(frate).2f , bytes/sec: %(brate).2f') % { |
446 | + 'type': self.auditor_type, |
447 | 'start_time': time.ctime(reported), |
448 | 'passes': self.passes, |
449 | 'quars': self.quarantines, |
450 | @@ -88,9 +95,11 @@ |
451 | self.bytes_processed = 0 |
452 | elapsed = time.time() - begin |
453 | self.logger.info(_( |
454 | - 'Object audit "%(mode)s" mode completed: %(elapsed).02fs. ' |
455 | + 'Object audit (%(type)s) "%(mode)s" mode ' |
456 | + 'completed: %(elapsed).02fs. ' |
457 | 'Total files/sec: %(frate).2f , ' |
458 | 'Total bytes/sec: %(brate).2f ') % { |
459 | + 'type': self.auditor_type, |
460 | 'mode': mode, |
461 | 'elapsed': elapsed, |
462 | 'frate': self.total_files_processed / elapsed, |
463 | @@ -98,7 +107,7 @@ |
464 | |
465 | def object_audit(self, path, device, partition): |
466 | """ |
467 | - Audits the given object path |
468 | + Audits the given object path. |
469 | |
470 | :param path: a path to an object |
471 | :param device: the device the path is on |
472 | @@ -119,11 +128,14 @@ |
473 | if df.data_file is None: |
474 | # file is deleted, we found the tombstone |
475 | return |
476 | - if os.path.getsize(df.data_file) != \ |
477 | - int(df.metadata['Content-Length']): |
478 | + obj_size = os.path.getsize(df.data_file) |
479 | + if obj_size != int(df.metadata['Content-Length']): |
480 | raise AuditException('Content-Length of %s does not match ' |
481 | 'file size of %s' % (int(df.metadata['Content-Length']), |
482 | os.path.getsize(df.data_file))) |
483 | + if self.fasttrack_zero_byte_files and \ |
484 | + bool(self.zero_byte_file_worker) == bool(obj_size): |
485 | + return |
486 | etag = md5() |
487 | for chunk in df: |
488 | self.bytes_running_time = ratelimit_sleep( |
489 | @@ -150,3 +162,34 @@ |
490 | self.logger.exception(_('ERROR Trying to audit %s'), path) |
491 | return |
492 | self.passes += 1 |
493 | + |
494 | + |
495 | +class ObjectAuditor(Daemon): |
496 | + """Audit objects.""" |
497 | + |
498 | + def __init__(self, conf, **options): |
499 | + self.conf = conf |
500 | + self.logger = get_logger(conf, 'object-auditor') |
501 | + self.fasttrack_zero_byte_files = conf.get( |
502 | + 'fasttrack_zero_byte_files', 'False').lower() in TRUE_VALS |
503 | + |
504 | + def run_forever(self, zero_byte_only=False, zero_byte_fps=None): |
505 | + """Run the object audit until stopped.""" |
506 | + zero_byte_pid = 1 |
507 | + if zero_byte_only or self.fasttrack_zero_byte_files: |
508 | + zero_byte_pid = os.fork() |
509 | + if zero_byte_pid == 0: |
510 | + while True: |
511 | + self.run_once(mode='forever', zero_byte_only=True, |
512 | + zero_byte_fps=zero_byte_fps) |
513 | + time.sleep(30) |
514 | + else: |
515 | + while not zero_byte_only: |
516 | + self.run_once(mode='forever') |
517 | + time.sleep(30) |
518 | + |
519 | + def run_once(self, mode='once', zero_byte_only=False, zero_byte_fps=None): |
520 | + """Run the object audit once.""" |
521 | + worker = AuditorWorker(self.conf, zero_byte_file_worker=zero_byte_only, |
522 | + zero_byte_fps=zero_byte_fps) |
523 | + worker.audit_all_objects(mode=mode) |
524 | |
525 | === modified file 'swift/obj/replicator.py' |
526 | --- swift/obj/replicator.py 2011-01-27 22:42:36 +0000 |
527 | +++ swift/obj/replicator.py 2011-02-08 00:11:22 +0000 |
528 | @@ -30,7 +30,7 @@ |
529 | |
530 | from swift.common.ring import Ring |
531 | from swift.common.utils import whataremyips, unlink_older_than, lock_path, \ |
532 | - renamer, compute_eta, get_logger |
533 | + renamer, compute_eta, get_logger, TRUE_VALS |
534 | from swift.common.bufferedhttp import http_connect |
535 | from swift.common.daemon import Daemon |
536 | |
537 | @@ -209,10 +209,8 @@ |
538 | self.conf = conf |
539 | self.logger = get_logger(conf, 'object-replicator') |
540 | self.devices_dir = conf.get('devices', '/srv/node') |
541 | - self.mount_check = conf.get('mount_check', 'true').lower() in \ |
542 | - ('true', 't', '1', 'on', 'yes', 'y') |
543 | - self.vm_test_mode = conf.get( |
544 | - 'vm_test_mode', 'no').lower() in ('yes', 'true', 'on', '1') |
545 | + self.mount_check = conf.get('mount_check', 'true').lower() in TRUE_VALS |
546 | + self.vm_test_mode = conf.get('vm_test_mode', 'no').lower() in TRUE_VALS |
547 | self.swift_dir = conf.get('swift_dir', '/etc/swift') |
548 | self.port = int(conf.get('bind_port', 6000)) |
549 | self.concurrency = int(conf.get('concurrency', 1)) |
550 | |
551 | === modified file 'swift/obj/server.py' |
552 | --- swift/obj/server.py 2011-01-26 22:38:13 +0000 |
553 | +++ swift/obj/server.py 2011-02-08 00:11:22 +0000 |
554 | @@ -37,7 +37,7 @@ |
555 | |
556 | from swift.common.utils import mkdirs, normalize_timestamp, \ |
557 | storage_directory, hash_path, renamer, fallocate, \ |
558 | - split_path, drop_buffer_cache, get_logger, write_pickle |
559 | + split_path, drop_buffer_cache, get_logger, write_pickle, TRUE_VALS |
560 | from swift.common.bufferedhttp import http_connect |
561 | from swift.common.constraints import check_object_creation, check_mount, \ |
562 | check_float, check_utf8 |
563 | @@ -268,8 +268,7 @@ |
564 | """ |
565 | self.logger = get_logger(conf) |
566 | self.devices = conf.get('devices', '/srv/node/') |
567 | - self.mount_check = conf.get('mount_check', 'true').lower() in \ |
568 | - ('true', 't', '1', 'on', 'yes', 'y') |
569 | + self.mount_check = conf.get('mount_check', 'true').lower() in TRUE_VALS |
570 | self.node_timeout = int(conf.get('node_timeout', 3)) |
571 | self.conn_timeout = float(conf.get('conn_timeout', 0.5)) |
572 | self.disk_chunk_size = int(conf.get('disk_chunk_size', 65536)) |
573 | |
574 | === modified file 'swift/obj/updater.py' |
575 | --- swift/obj/updater.py 2011-01-26 22:31:33 +0000 |
576 | +++ swift/obj/updater.py 2011-02-08 00:11:22 +0000 |
577 | @@ -25,7 +25,7 @@ |
578 | from swift.common.bufferedhttp import http_connect |
579 | from swift.common.exceptions import ConnectionTimeout |
580 | from swift.common.ring import Ring |
581 | -from swift.common.utils import get_logger, renamer, write_pickle |
582 | +from swift.common.utils import get_logger, renamer, write_pickle, TRUE_VALS |
583 | from swift.common.daemon import Daemon |
584 | from swift.obj.server import ASYNCDIR |
585 | |
586 | @@ -37,8 +37,7 @@ |
587 | self.conf = conf |
588 | self.logger = get_logger(conf, 'object-updater') |
589 | self.devices = conf.get('devices', '/srv/node') |
590 | - self.mount_check = conf.get('mount_check', 'true').lower() in \ |
591 | - ('true', 't', '1', 'on', 'yes', 'y') |
592 | + self.mount_check = conf.get('mount_check', 'true').lower() in TRUE_VALS |
593 | swift_dir = conf.get('swift_dir', '/etc/swift') |
594 | self.interval = int(conf.get('interval', 300)) |
595 | self.container_ring_path = os.path.join(swift_dir, 'container.ring.gz') |
596 | |
597 | === modified file 'swift/stats/account_stats.py' |
598 | --- swift/stats/account_stats.py 2011-01-30 14:59:35 +0000 |
599 | +++ swift/stats/account_stats.py 2011-02-08 00:11:22 +0000 |
600 | @@ -21,7 +21,7 @@ |
601 | |
602 | from swift.account.server import DATADIR as account_server_data_dir |
603 | from swift.common.db import AccountBroker |
604 | -from swift.common.utils import renamer, get_logger, readconf, mkdirs |
605 | +from swift.common.utils import renamer, get_logger, readconf, mkdirs, TRUE_VALS |
606 | from swift.common.constraints import check_mount |
607 | from swift.common.daemon import Daemon |
608 | |
609 | @@ -47,7 +47,7 @@ |
610 | mkdirs(self.target_dir) |
611 | self.devices = server_conf.get('devices', '/srv/node') |
612 | self.mount_check = server_conf.get('mount_check', 'true').lower() in \ |
613 | - ('true', 't', '1', 'on', 'yes', 'y') |
614 | + TRUE_VALS |
615 | self.logger = get_logger(stats_conf, 'swift-account-stats-logger') |
616 | |
617 | def run_once(self): |
618 | |
619 | === modified file 'swift/stats/log_uploader.py' |
620 | --- swift/stats/log_uploader.py 2011-01-14 08:45:39 +0000 |
621 | +++ swift/stats/log_uploader.py 2011-02-08 00:11:22 +0000 |
622 | @@ -54,7 +54,7 @@ |
623 | name='proxy-server') |
624 | new_log_cutoff = int(uploader_conf.get('new_log_cutoff', '7200')) |
625 | unlink_log = uploader_conf.get('unlink_log', 'True').lower() in \ |
626 | - ('true', 'on', '1', 'yes') |
627 | + utils.TRUE_VALS |
628 | self.unlink_log = unlink_log |
629 | self.new_log_cutoff = new_log_cutoff |
630 | if not log_dir.endswith('/'): |
631 | |
632 | === modified file 'test/unit/common/test_utils.py' |
633 | --- test/unit/common/test_utils.py 2011-02-02 17:38:17 +0000 |
634 | +++ test/unit/common/test_utils.py 2011-02-08 00:11:22 +0000 |
635 | @@ -260,26 +260,16 @@ |
636 | stde = StringIO() |
637 | utils.sys.stdout = stdo |
638 | utils.sys.stderr = stde |
639 | - err_msg = """Usage: test usage |
640 | - |
641 | -Error: missing config file argument |
642 | -""" |
643 | - test_args = [] |
644 | - self.assertRaises(SystemExit, utils.parse_options, 'test usage', True, |
645 | - test_args) |
646 | - self.assertEquals(stdo.getvalue(), err_msg) |
647 | + self.assertRaises(SystemExit, utils.parse_options, once=True, |
648 | + test_args=[]) |
649 | + self.assert_(stdo.getvalue().find('missing config file') >= 0) |
650 | |
651 | # verify conf file must exist, context manager will delete temp file |
652 | with NamedTemporaryFile() as f: |
653 | conf_file = f.name |
654 | - err_msg += """Usage: test usage |
655 | - |
656 | -Error: unable to locate %s |
657 | -""" % conf_file |
658 | - test_args = [conf_file] |
659 | - self.assertRaises(SystemExit, utils.parse_options, 'test usage', True, |
660 | - test_args) |
661 | - self.assertEquals(stdo.getvalue(), err_msg) |
662 | + self.assertRaises(SystemExit, utils.parse_options, once=True, |
663 | + test_args=[conf_file]) |
664 | + self.assert_(stdo.getvalue().find('unable to locate') >= 0) |
665 | |
666 | # reset stdio |
667 | utils.sys.stdout = orig_stdout |
668 | |
669 | === modified file 'test/unit/obj/test_auditor.py' |
670 | --- test/unit/obj/test_auditor.py 2011-01-25 01:12:38 +0000 |
671 | +++ test/unit/obj/test_auditor.py 2011-02-08 00:11:22 +0000 |
672 | @@ -59,7 +59,7 @@ |
673 | rmtree(os.path.dirname(self.testdir), ignore_errors=1) |
674 | |
675 | def test_object_audit_extra_data(self): |
676 | - self.auditor = auditor.ObjectAuditor(self.conf) |
677 | + self.auditor = auditor.AuditorWorker(self.conf) |
678 | cur_part = '0' |
679 | disk_file = DiskFile(self.devices, 'sda', cur_part, 'a', 'c', 'o') |
680 | data = '0' * 1024 |
681 | @@ -89,7 +89,7 @@ |
682 | self.assertEquals(self.auditor.quarantines, pre_quarantines + 1) |
683 | |
684 | def test_object_audit_diff_data(self): |
685 | - self.auditor = auditor.ObjectAuditor(self.conf) |
686 | + self.auditor = auditor.AuditorWorker(self.conf) |
687 | cur_part = '0' |
688 | disk_file = DiskFile(self.devices, 'sda', cur_part, 'a', 'c', 'o') |
689 | data = '0' * 1024 |
690 | @@ -132,7 +132,7 @@ |
691 | fp.write('0' * 1024) |
692 | fp.close() |
693 | invalidate_hash(os.path.dirname(disk_file.datadir)) |
694 | - self.auditor = auditor.ObjectAuditor(self.conf) |
695 | + self.auditor = auditor.AuditorWorker(self.conf) |
696 | pre_quarantines = self.auditor.quarantines |
697 | self.auditor.object_audit( |
698 | os.path.join(disk_file.datadir, timestamp + '.data'), |
699 | @@ -140,7 +140,7 @@ |
700 | self.assertEquals(self.auditor.quarantines, pre_quarantines + 1) |
701 | |
702 | def test_object_audit_bad_args(self): |
703 | - self.auditor = auditor.ObjectAuditor(self.conf) |
704 | + self.auditor = auditor.AuditorWorker(self.conf) |
705 | pre_errors = self.auditor.errors |
706 | self.auditor.object_audit(5, 'sda', '0') |
707 | self.assertEquals(self.auditor.errors, pre_errors + 1) |
708 | @@ -149,7 +149,7 @@ |
709 | self.assertEquals(self.auditor.errors, pre_errors) # just returns |
710 | |
711 | def test_object_run_once_pass(self): |
712 | - self.auditor = auditor.ObjectAuditor(self.conf) |
713 | + self.auditor = auditor.AuditorWorker(self.conf) |
714 | self.auditor.log_time = 0 |
715 | cur_part = '0' |
716 | timestamp = str(normalize_timestamp(time.time())) |
717 | @@ -168,11 +168,11 @@ |
718 | } |
719 | disk_file.put(fd, tmppath, metadata) |
720 | disk_file.close() |
721 | - self.auditor.run_once() |
722 | + self.auditor.audit_all_objects() |
723 | self.assertEquals(self.auditor.quarantines, pre_quarantines) |
724 | |
725 | def test_object_run_once_no_sda(self): |
726 | - self.auditor = auditor.ObjectAuditor(self.conf) |
727 | + self.auditor = auditor.AuditorWorker(self.conf) |
728 | cur_part = '0' |
729 | timestamp = str(normalize_timestamp(time.time())) |
730 | pre_quarantines = self.auditor.quarantines |
731 | @@ -191,11 +191,11 @@ |
732 | disk_file.put(fd, tmppath, metadata) |
733 | disk_file.close() |
734 | os.write(fd, 'extra_data') |
735 | - self.auditor.run_once() |
736 | + self.auditor.audit_all_objects() |
737 | self.assertEquals(self.auditor.quarantines, pre_quarantines + 1) |
738 | |
739 | def test_object_run_once_multi_devices(self): |
740 | - self.auditor = auditor.ObjectAuditor(self.conf) |
741 | + self.auditor = auditor.AuditorWorker(self.conf) |
742 | cur_part = '0' |
743 | timestamp = str(normalize_timestamp(time.time())) |
744 | pre_quarantines = self.auditor.quarantines |
745 | @@ -213,7 +213,7 @@ |
746 | } |
747 | disk_file.put(fd, tmppath, metadata) |
748 | disk_file.close() |
749 | - self.auditor.run_once() |
750 | + self.auditor.audit_all_objects() |
751 | disk_file = DiskFile(self.devices, 'sdb', cur_part, 'a', 'c', 'ob') |
752 | data = '1' * 10 |
753 | etag = md5() |
754 | @@ -229,9 +229,64 @@ |
755 | disk_file.put(fd, tmppath, metadata) |
756 | disk_file.close() |
757 | os.write(fd, 'extra_data') |
758 | - self.auditor.run_once() |
759 | + self.auditor.audit_all_objects() |
760 | self.assertEquals(self.auditor.quarantines, pre_quarantines + 1) |
761 | |
762 | + def test_object_run_fast_track_non_zero(self): |
763 | + self.conf['fasttrack_zero_byte_files'] = 'yes' |
764 | + self.auditor = auditor.ObjectAuditor(self.conf) |
765 | + self.auditor.log_time = 0 |
766 | + cur_part = '0' |
767 | + disk_file = DiskFile(self.devices, 'sda', cur_part, 'a', 'c', 'o') |
768 | + data = '0' * 1024 |
769 | + etag = md5() |
770 | + with disk_file.mkstemp() as (fd, tmppath): |
771 | + os.write(fd, data) |
772 | + etag.update(data) |
773 | + etag = etag.hexdigest() |
774 | + metadata = { |
775 | + 'ETag': etag, |
776 | + 'X-Timestamp': str(normalize_timestamp(time.time())), |
777 | + 'Content-Length': str(os.fstat(fd).st_size), |
778 | + } |
779 | + disk_file.put(fd, tmppath, metadata) |
780 | + etag = md5() |
781 | + etag.update('1' + '0' * 1023) |
782 | + etag = etag.hexdigest() |
783 | + metadata['ETag'] = etag |
784 | + write_metadata(fd, metadata) |
785 | + |
786 | + quarantine_path = os.path.join(self.devices, |
787 | + 'sda', 'quarantined', 'objects') |
788 | + self.auditor.run_once(zero_byte_only=True) |
789 | + self.assertFalse(os.path.isdir(quarantine_path)) |
790 | + self.auditor.run_once() |
791 | + self.assertTrue(os.path.isdir(quarantine_path)) |
792 | + |
793 | + def test_object_run_fast_track_zero(self): |
794 | + self.conf['fasttrack_zero_byte_files'] = 'yes' |
795 | + self.auditor = auditor.ObjectAuditor(self.conf) |
796 | + self.auditor.log_time = 0 |
797 | + cur_part = '0' |
798 | + disk_file = DiskFile(self.devices, 'sda', cur_part, 'a', 'c', 'o') |
799 | + etag = md5() |
800 | + with disk_file.mkstemp() as (fd, tmppath): |
801 | + etag = etag.hexdigest() |
802 | + metadata = { |
803 | + 'ETag': etag, |
804 | + 'X-Timestamp': str(normalize_timestamp(time.time())), |
805 | + 'Content-Length': 10, |
806 | + } |
807 | + disk_file.put(fd, tmppath, metadata) |
808 | + etag = md5() |
809 | + etag = etag.hexdigest() |
810 | + metadata['ETag'] = etag |
811 | + write_metadata(fd, metadata) |
812 | + quarantine_path = os.path.join(self.devices, |
813 | + 'sda', 'quarantined', 'objects') |
814 | + self.auditor.run_once() |
815 | + self.assertTrue(os.path.isdir(quarantine_path)) |
816 | + |
817 | |
818 | if __name__ == '__main__': |
819 | unittest.main() |
The TRUE_VALUES -> TRUE_VALS thing seems out of place in this branch.