[Checkins] SVN: gocept.zeoraid/trunk/src/gocept/zeoraid/ Add new option 'fail-mode' to indicate whether the RAID should be closed or
Christian Theune
ct at gocept.com
Wed Oct 6 11:02:53 EDT 2010
Log message for revision 117306:
Add new option 'fail-mode' to indicate whether the RAID should be closed or
turned read-only.
Fixed shared blob support by re-implementing the noop support.
Changed:
U gocept.zeoraid/trunk/src/gocept/zeoraid/component.xml
U gocept.zeoraid/trunk/src/gocept/zeoraid/datatypes.py
U gocept.zeoraid/trunk/src/gocept/zeoraid/storage.py
U gocept.zeoraid/trunk/src/gocept/zeoraid/tests/test_basics.py
-=-
Modified: gocept.zeoraid/trunk/src/gocept/zeoraid/component.xml
===================================================================
--- gocept.zeoraid/trunk/src/gocept/zeoraid/component.xml 2010-10-06 14:28:08 UTC (rev 117305)
+++ gocept.zeoraid/trunk/src/gocept/zeoraid/component.xml 2010-10-06 15:02:53 UTC (rev 117306)
@@ -63,6 +63,17 @@
</description>
</key>
+ <key name="fail-mode" required="no"
+ datatype=".fail_mode" default="close">
+ <description>
+ How the RAID server should behave when too few optimal storages are
+ available. The default option ('close') will close the storage,
+ rendering it unusable for clients and thus allow clients to
+ possibly fail-over to another RAID server. The 'read-only' option
+ will keep the storage open but will only allow reading operations.
+ </description>
+ </key>
+
<multisection
type="ZODB.storage"
name="+"
Modified: gocept.zeoraid/trunk/src/gocept/zeoraid/datatypes.py
===================================================================
--- gocept.zeoraid/trunk/src/gocept/zeoraid/datatypes.py 2010-10-06 14:28:08 UTC (rev 117305)
+++ gocept.zeoraid/trunk/src/gocept/zeoraid/datatypes.py 2010-10-06 15:02:53 UTC (rev 117306)
@@ -27,6 +27,13 @@
return value
+def fail_mode(value):
+ if value not in ('read-only', 'close'):
+ raise ValueError(
+ "Only valid fail modes: 'read-only', 'close', found %r" % value)
+ return value
+
+
class Storage(ZODB.config.BaseConfig):
def open(self):
@@ -42,6 +49,7 @@
self.config.storages,
blob_dir=self.config.blob_dir,
read_only=self.config.read_only,
+ fail_mode=self.config.fail_mode,
cluster_mode=self.config.cluster_mode,
shared_blob_dir=self.config.shared_blob_dir,
zeo=zeo)
Modified: gocept.zeoraid/trunk/src/gocept/zeoraid/storage.py
===================================================================
--- gocept.zeoraid/trunk/src/gocept/zeoraid/storage.py 2010-10-06 14:28:08 UTC (rev 117305)
+++ gocept.zeoraid/trunk/src/gocept/zeoraid/storage.py 2010-10-06 15:02:53 UTC (rev 117306)
@@ -116,9 +116,11 @@
timeout = 6000
def __init__(self, name, openers, read_only=False, cluster_mode='coop',
- blob_dir=None, shared_blob_dir=False, zeo=None):
+ blob_dir=None, shared_blob_dir=False, zeo=None,
+ fail_mode='close'):
self.__name__ = name
self.read_only = read_only
+ self.fail_mode = fail_mode
self.cluster_mode = cluster_mode
self.shared_blob_dir = shared_blob_dir
self.zeo = zeo
@@ -748,8 +750,12 @@
if len(self.storages_optimal) <= len(self.openers) * 0.5:
fail = 'Less than 50% of the configured storages remain optimal.'
if fail:
- self.close()
- raise gocept.zeoraid.interfaces.RAIDClosedError(fail)
+ if self.fail_mode == 'close':
+ self.close()
+ raise gocept.zeoraid.interfaces.RAIDClosedError(fail)
+ elif self.fail_mode == 'read-only':
+ self.read_only = True
+ raise ZODB.POSException.ReadOnlyError(fail)
def _apply_storage(self, storage_name, method_name, args=(), kw={},
expect_connected=True):
@@ -1009,6 +1015,8 @@
if storage not in self.exclude]
if not applicable_storages:
+ if self.ignore_noop:
+ return
raise gocept.zeoraid.interfaces.RAIDError(
'No applicable storages for operation %s available.' %
method_name)
Modified: gocept.zeoraid/trunk/src/gocept/zeoraid/tests/test_basics.py
===================================================================
--- gocept.zeoraid/trunk/src/gocept/zeoraid/tests/test_basics.py 2010-10-06 14:28:08 UTC (rev 117305)
+++ gocept.zeoraid/trunk/src/gocept/zeoraid/tests/test_basics.py 2010-10-06 15:02:53 UTC (rev 117306)
@@ -21,6 +21,7 @@
from ZODB.tests import Synchronization, ConflictResolution, HistoryStorage
from ZODB.tests import TransactionalUndoStorage, PackableStorage
from gocept.zeoraid.tests.loggingstorage import LoggingStorage
+import ZConfig
import ZEO.runzeo
import ZODB.MappingStorage
import ZODB.config
@@ -39,8 +40,8 @@
import unittest
import zc.lockfile
import zope.interface.verify
+import StringIO
-
# import logging
# logging.getLogger().setLevel(0)
# logging.getLogger().addHandler(logging.StreamHandler())
@@ -1680,7 +1681,7 @@
def setUp(self):
self.raid = gocept.zeoraid.storage.RAIDStorage(
- 'test', [Opener('%s' % s) for s in range(5)])
+ 'test', [Opener('%s' % s) for s in range(5)], fail_mode='close')
self.raid._apply_storage = mock.Mock(return_value=(True, None))
@@ -1700,7 +1701,17 @@
self.raid._degrade_storage('1', 'test')
self.assertRaises(ZEO.Exceptions.ClientStorageError,
lambda: self.raid._degrade_storage('2', 'test'))
+ self.assertTrue(self.raid.closed)
+ def test_degrade_turns_readonly(self):
+ self.raid.fail_mode = 'read-only'
+ self.raid._degrade_storage('0', 'test')
+ self.raid._degrade_storage('1', 'test')
+ self.assertRaises(ZODB.POSException.ReadOnlyError,
+ lambda: self.raid._degrade_storage('2', 'test'))
+ self.assert_(self.raid.isReadOnly())
+ self.assertFalse(self.raid.closed)
+
class ClusterModeSingleTests(ClusterModeTests):
def setUp(self):
@@ -1800,6 +1811,32 @@
else:
self.fail('No exception raised')
+
+class ConfigTests(unittest.TestCase):
+
+ @mock.patch('gocept.zeoraid.storage.RAIDStorage')
+ def test_raid_storage_schema(self, raid_class):
+ config, handle = ZConfig.loadConfigFile(
+ ZODB.config.getStorageSchema(),
+ StringIO.StringIO('''\
+%import gocept.zeoraid
+<raidstorage 1>
+ cluster-mode single
+ fail-mode read-only
+ <mappingstorage 1>
+ </mappingstorage>
+</raidstorage>
+'''))
+ self.assertEqual('single', config.storage.config.cluster_mode)
+ self.assertEqual('read-only', config.storage.config.fail_mode)
+
+ raid = config.storage.open()
+ args, kwargs= raid_class.call_args
+ self.assertEqual('1', args[0])
+ self.assertEqual('read-only', kwargs['fail_mode'])
+ self.assertEqual('single', kwargs['cluster_mode'])
+
+
def test_suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(ZEOReplicationStorageTests, "check"))
@@ -1813,4 +1850,5 @@
suite.addTest(unittest.makeSuite(ClusterModeCoopTests))
suite.addTest(unittest.makeSuite(AllStorageConsistencyCheck))
suite.addTest(unittest.makeSuite(OperationExceptionResultTests))
+ suite.addTest(unittest.makeSuite(ConfigTests))
return suite
More information about the checkins
mailing list