[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