5202b0e586
Refactor on-disk knowledge out of the object server by pushing the
async update pickle creation to the new DiskFileManager class (name is
not the best, so suggestions welcome), along with the REPLICATOR
method logic. We also move the mount checking and thread pool storage
to the new ondisk.Devices object, which then also becomes the new home
of the audit_location_generator method.
For the object server, a new setup() method is now called at the end
of the controller's construction, and the _diskfile() method has been
renamed to get_diskfile(), to allow implementation specific behavior.
We then hide the need for the REST API layer to know how and where
quarantining needs to be performed. There are now two places it is
checked internally, on open() where we verify the content-length,
name, and x-timestamp metadata, and in the reader on close where the
etag metadata is checked if the entire file was read.
We add a reader class to allow implementations to isolate the WSGI
handling code for that specific environment (it is used no-where else
in the REST APIs). This simplifies the caller's code to just use a
"with" statement once open to avoid multiple points where close needs
to be called.
For a full historical comparison, including the usage patterns see:
https://gist.github.com/portante/5488238
(as of master, 2b639f5
, Merge
"Fix 500 from account-quota This Commit
middleware")
--------------------------------+------------------------------------
DiskFileManager(conf)
Methods:
.pickle_async_update()
.get_diskfile()
.get_hashes()
Attributes:
.devices
.logger
.disk_chunk_size
.keep_cache_size
.bytes_per_sync
DiskFile(a,c,o,keep_data_fp=) DiskFile(a,c,o)
Methods: Methods:
*.__iter__()
.close(verify_file=)
.is_deleted()
.is_expired()
.quarantine()
.get_data_file_size()
.open()
.read_metadata()
.create() .create()
.write_metadata()
.delete() .delete()
Attributes: Attributes:
.quarantined_dir
.keep_cache
.metadata
*DiskFileReader()
Methods:
.__iter__()
.close()
Attributes:
+.was_quarantined
DiskWriter() DiskFileWriter()
Methods: Methods:
.write() .write()
.put() .put()
* Note that the DiskFile class * Note that the DiskReader() object
implements all the methods returned by the
necessary for a WSGI app DiskFileOpened.reader() method
iterator implements all the methods
necessary for a WSGI app iterator
+ Note that if the auditor is
refactored to not use the DiskFile
class, see
https://review.openstack.org/44787
then we don't need the
was_quarantined attribute
A reference "in-memory" object server implementation of a backend
DiskFile class in swift/obj/mem_server.py and
swift/obj/mem_diskfile.py.
One can also reference
https://github.com/portante/gluster-swift/commits/diskfile for the
proposed integration with the gluster-swift code based on these
changes.
Change-Id: I44e153fdb405a5743e9c05349008f94136764916
Signed-off-by: Peter Portante <peter.portante@redhat.com>
122 lines
4.0 KiB
Python
122 lines
4.0 KiB
Python
# Copyright (c) 2010-2012 OpenStack Foundation
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
# implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
import unittest
|
|
import mock
|
|
import time
|
|
import os
|
|
import random
|
|
from tempfile import mkdtemp
|
|
from shutil import rmtree
|
|
|
|
from swift.account import auditor
|
|
from test.unit import FakeLogger
|
|
|
|
|
|
class FakeAccountBroker(object):
|
|
def __init__(self, path):
|
|
self.path = path
|
|
self.db_file = path
|
|
self.file = os.path.basename(path)
|
|
|
|
def is_deleted(self):
|
|
return False
|
|
|
|
def get_info(self):
|
|
if self.file.startswith('fail'):
|
|
raise ValueError
|
|
if self.file.startswith('true'):
|
|
return 'ok'
|
|
|
|
|
|
class TestAuditor(unittest.TestCase):
|
|
|
|
def setUp(self):
|
|
self.testdir = os.path.join(mkdtemp(), 'tmp_test_account_auditor')
|
|
self.logger = FakeLogger()
|
|
rmtree(self.testdir, ignore_errors=1)
|
|
os.mkdir(self.testdir)
|
|
fnames = ['true1.db', 'true2.db', 'true3.db',
|
|
'fail1.db', 'fail2.db']
|
|
for fn in fnames:
|
|
with open(os.path.join(self.testdir, fn), 'w+') as f:
|
|
f.write(' ')
|
|
|
|
def tearDown(self):
|
|
rmtree(os.path.dirname(self.testdir), ignore_errors=1)
|
|
|
|
@mock.patch('swift.account.auditor.AccountBroker', FakeAccountBroker)
|
|
def test_run_forever(self):
|
|
sleep_times = random.randint(5, 10)
|
|
call_times = sleep_times - 1
|
|
|
|
class FakeTime(object):
|
|
def __init__(self):
|
|
self.times = 0
|
|
|
|
def sleep(self, sec):
|
|
self.times += 1
|
|
if self.times < sleep_times:
|
|
time.sleep(0.1)
|
|
else:
|
|
# stop forever by an error
|
|
raise ValueError()
|
|
|
|
def time(self):
|
|
return time.time()
|
|
|
|
conf = {}
|
|
test_auditor = auditor.AccountAuditor(conf)
|
|
|
|
with mock.patch('swift.account.auditor.time', FakeTime()):
|
|
def fake_audit_location_generator(*args, **kwargs):
|
|
files = os.listdir(self.testdir)
|
|
return [(os.path.join(self.testdir, f), '', '') for f in files]
|
|
|
|
with mock.patch('swift.account.auditor.audit_location_generator',
|
|
fake_audit_location_generator):
|
|
self.assertRaises(ValueError, test_auditor.run_forever)
|
|
self.assertEqual(test_auditor.account_failures, 2 * call_times)
|
|
self.assertEqual(test_auditor.account_passes, 3 * call_times)
|
|
|
|
@mock.patch('swift.account.auditor.AccountBroker', FakeAccountBroker)
|
|
def test_run_once(self):
|
|
conf = {}
|
|
test_auditor = auditor.AccountAuditor(conf)
|
|
|
|
def fake_audit_location_generator(*args, **kwargs):
|
|
files = os.listdir(self.testdir)
|
|
return [(os.path.join(self.testdir, f), '', '') for f in files]
|
|
|
|
with mock.patch('swift.account.auditor.audit_location_generator',
|
|
fake_audit_location_generator):
|
|
test_auditor.run_once()
|
|
self.assertEqual(test_auditor.account_failures, 2)
|
|
self.assertEqual(test_auditor.account_passes, 3)
|
|
|
|
@mock.patch('swift.account.auditor.AccountBroker', FakeAccountBroker)
|
|
def test_account_auditor(self):
|
|
conf = {}
|
|
test_auditor = auditor.AccountAuditor(conf)
|
|
files = os.listdir(self.testdir)
|
|
for f in files:
|
|
path = os.path.join(self.testdir, f)
|
|
test_auditor.account_audit(path)
|
|
self.assertEqual(test_auditor.account_failures, 2)
|
|
self.assertEqual(test_auditor.account_passes, 3)
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|