Merge lp:~david-goetz/swift/zero_byte_obj_audit into lp:~hudson-openstack/swift/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
Reviewer Review Type Date Requested Status
Swift Core security contacts Pending
Review via email: mp+48862@code.launchpad.net

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 :

The TRUE_VALUES -> TRUE_VALS thing seems out of place in this branch.

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()