Vered Volansky has uploaded a new change for review.
Change subject: utils: Moved ContextManager from misc to utils ......................................................................
utils: Moved ContextManager from misc to utils
The ContextManager was located in storage/misc, and is used by several non-storage tests. It was therefore moved to utils, where it belongs. Accordingly, ContextManagerTests class was moved to utilstests from mistests.
Change-Id: I985103650a5706d35d9cd519618d09c692feb0be Signed-off-by: Vered Volansky vvolansk@redhat.com --- M lib/vdsm/utils.py M tests/functional/networkTests.py M tests/functional/storageTests.py M tests/functional/virtTests.py M tests/miscTests.py M tests/utilsTests.py M vdsm/storage/misc.py M vdsm/storage/resourceManager.py 8 files changed, 118 insertions(+), 121 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/61/22861/1
diff --git a/lib/vdsm/utils.py b/lib/vdsm/utils.py index b072bd4..d6c65b2 100644 --- a/lib/vdsm/utils.py +++ b/lib/vdsm/utils.py @@ -1077,3 +1077,44 @@ :return: """ self.callbacks.append(Callback(func, args, kwargs)) + + +class RollbackContext(object): + ''' + A context manager for recording and playing rollback. + The first exception will be remembered and re-raised after rollback + + Sample usage: + with RollbackContext() as rollback: + step1() + rollback.prependDefer(lambda: undo step1) + def undoStep2(arg): pass + step2() + rollback.prependDefer(undoStep2, arg) + + More examples see tests/miscTests.py + ''' + def __init__(self, *args): + self._finally = [] + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, traceback): + for undo, args, kwargs in self._finally: + try: + undo(*args, **kwargs) + except Exception: + # keep the earliest exception info + if exc_type is None: + exc_type, exc_value, traceback = sys.exc_info() + + # re-raise the earliest exception + if exc_type is not None: + raise exc_type, exc_value, traceback + + def defer(self, func, *args, **kwargs): + self._finally.append((func, args, kwargs)) + + def prependDefer(self, func, *args, **kwargs): + self._finally.insert(0, (func, args, kwargs)) diff --git a/tests/functional/networkTests.py b/tests/functional/networkTests.py index 21d00ff..9f8e50f 100644 --- a/tests/functional/networkTests.py +++ b/tests/functional/networkTests.py @@ -21,8 +21,6 @@ import os.path
import neterrors -from storage.misc import RollbackContext - from hookValidation import ValidatesHook from testrunner import (VdsmTestCase as TestCaseBase, expandPermutations, permutations) @@ -40,6 +38,7 @@ ruleExists, Route, Rule, addrFlush, LinkType, getLinks)
+from vdsm.utils import RollbackContext from vdsm.netinfo import operstate, prefix2netmask, getRouteDeviceTo from vdsm import ipwrapper
diff --git a/tests/functional/storageTests.py b/tests/functional/storageTests.py index 542f72a..de70864 100644 --- a/tests/functional/storageTests.py +++ b/tests/functional/storageTests.py @@ -40,12 +40,11 @@ import storage.storage_exception as se import storage.volume from storage.misc import execCmd -from storage.misc import RollbackContext from storage.mount import Mount
from vdsm.config import config from vdsm.constants import VDSM_USER, VDSM_GROUP -from vdsm.utils import CommandPath +from vdsm.utils import CommandPath, RollbackContext from vdsm import vdscli
_VARTMP = '/var/tmp' diff --git a/tests/functional/virtTests.py b/tests/functional/virtTests.py index bd54621..f41de07 100644 --- a/tests/functional/virtTests.py +++ b/tests/functional/virtTests.py @@ -30,10 +30,9 @@ from testrunner import VdsmTestCase as TestCaseBase from testrunner import permutations, expandPermutations
-from vdsm.utils import CommandPath +from vdsm.utils import CommandPath, RollbackContext import storageTests as storage from storage.misc import execCmd -from storage.misc import RollbackContext
from utils import VdsProxy, SUCCESS
diff --git a/tests/miscTests.py b/tests/miscTests.py index c726908..824e783 100644 --- a/tests/miscTests.py +++ b/tests/miscTests.py @@ -1086,77 +1086,6 @@ proc.wait()
-class RollbackContextTests(TestCaseBase): - def setUp(self): - self._called = 0 - - def _callDef(self): - self._called += 1 - self.log.info("Incremented call count (%d)", self._called) - - def _raiseDef(self, ex=Exception()): - self.log.info("Raised exception (%s)", ex.__class__.__name__) - raise ex - - def test(self): - with misc.RollbackContext() as rollback: - rollback.prependDefer(self._callDef) - - self.assertEquals(self._called, 1) - - def testRaise(self): - """ - Test that raising an exception in a deferred action does - not block all subsequent actions from running - """ - try: - with misc.RollbackContext() as rollback: - rollback.prependDefer(self._callDef) - rollback.prependDefer(self._raiseDef) - rollback.prependDefer(self._callDef) - except Exception: - self.assertEquals(self._called, 2) - return - - self.fail("Exception was not raised") - - def testFirstException(self): - """ - Test that if multiple actions raise an exception only the first one is - raised. When performing a batch rollback operations, probably the first - exception is the root cause. - """ - try: - with misc.RollbackContext() as rollback: - rollback.prependDefer(self._callDef) - rollback.prependDefer(self._raiseDef) - rollback.prependDefer(self._callDef) - rollback.prependDefer(self._raiseDef, RuntimeError()) - rollback.prependDefer(self._callDef) - except RuntimeError: - self.assertEquals(self._called, 3) - return - except Exception: - self.fail("Wrong exception was raised") - - self.fail("Exception was not raised") - - def testKeyErrorException(self): - """ - KeyError is raised as a tuple and not expection. Re-rasing it - should be aware of this fact and handled carfully. - """ - try: - with misc.RollbackContext(): - {}['aKey'] - except KeyError: - return - except Exception: - self.fail("Wrong exception was raised") - - self.fail("Exception was not raised") - - class FindCallerTests(TestCaseBase): def _assertFindCaller(self, callback): frame = inspect.currentframe() diff --git a/tests/utilsTests.py b/tests/utilsTests.py index c5b250d..7cde5a3 100644 --- a/tests/utilsTests.py +++ b/tests/utilsTests.py @@ -302,3 +302,74 @@ def handle(self, record): assert self.record is None self.record = record + + +class RollbackContextTests(TestCaseBase): + def setUp(self): + self._called = 0 + + def _callDef(self): + self._called += 1 + self.log.info("Incremented call count (%d)", self._called) + + def _raiseDef(self, ex=Exception()): + self.log.info("Raised exception (%s)", ex.__class__.__name__) + raise ex + + def test(self): + with utils.RollbackContext() as rollback: + rollback.prependDefer(self._callDef) + + self.assertEquals(self._called, 1) + + def testRaise(self): + """ + Test that raising an exception in a deferred action does + not block all subsequent actions from running + """ + try: + with utils.RollbackContext() as rollback: + rollback.prependDefer(self._callDef) + rollback.prependDefer(self._raiseDef) + rollback.prependDefer(self._callDef) + except Exception: + self.assertEquals(self._called, 2) + return + + self.fail("Exception was not raised") + + def testFirstException(self): + """ + Test that if multiple actions raise an exception only the first one is + raised. When performing a batch rollback operations, probably the first + exception is the root cause. + """ + try: + with utils.RollbackContext() as rollback: + rollback.prependDefer(self._callDef) + rollback.prependDefer(self._raiseDef) + rollback.prependDefer(self._callDef) + rollback.prependDefer(self._raiseDef, RuntimeError()) + rollback.prependDefer(self._callDef) + except RuntimeError: + self.assertEquals(self._called, 3) + return + except Exception: + self.fail("Wrong exception was raised") + + self.fail("Exception was not raised") + + def testKeyErrorException(self): + """ + KeyError is raised as a tuple and not expection. Re-rasing it + should be aware of this fact and handled carfully. + """ + try: + with utils.RollbackContext(): + {}['aKey'] + except KeyError: + return + except Exception: + self.fail("Wrong exception was raised") + + self.fail("Exception was not raised") diff --git a/vdsm/storage/misc.py b/vdsm/storage/misc.py index f1d8ab0..2c06af3 100644 --- a/vdsm/storage/misc.py +++ b/vdsm/storage/misc.py @@ -43,7 +43,6 @@ import select import string import struct -import sys import threading import types import weakref @@ -675,47 +674,6 @@ nextRequest, self._currentState = self._queue.get_nowait()
nextRequest.set() - - -class RollbackContext(object): - ''' - A context manager for recording and playing rollback. - The first exception will be remembered and re-raised after rollback - - Sample usage: - with RollbackContext() as rollback: - step1() - rollback.prependDefer(lambda: undo step1) - def undoStep2(arg): pass - step2() - rollback.prependDefer(undoStep2, arg) - - More examples see tests/miscTests.py - ''' - def __init__(self, *args): - self._finally = [] - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_value, traceback): - for undo, args, kwargs in self._finally: - try: - undo(*args, **kwargs) - except Exception: - # keep the earliest exception info - if exc_type is None: - exc_type, exc_value, traceback = sys.exc_info() - - # re-raise the earliest exception - if exc_type is not None: - raise exc_type, exc_value, traceback - - def defer(self, func, *args, **kwargs): - self._finally.append((func, args, kwargs)) - - def prependDefer(self, func, *args, **kwargs): - self._finally.insert(0, (func, args, kwargs))
class DynamicBarrier(object): diff --git a/vdsm/storage/resourceManager.py b/vdsm/storage/resourceManager.py index 14049dc..9d30eba 100644 --- a/vdsm/storage/resourceManager.py +++ b/vdsm/storage/resourceManager.py @@ -29,6 +29,7 @@ import storage_exception as se import misc from logUtils import SimpleLogAdapter +from vdsm.utils import RollbackContext
# Errors @@ -539,7 +540,7 @@ request = Request(namespace, name, lockType, callback) self._log.debug("Trying to register resource '%s' for lock type '%s'", fullName, lockType) - with nested(misc.RollbackContext(), + with nested(RollbackContext(), self._syncRoot.shared) as (contextCleanup, lock): try: namespaceObj = self._namespaces[namespace] @@ -613,7 +614,7 @@ fullName = "%s.%s" % (namespace, name)
self._log.debug("Trying to release resource '%s'", fullName) - with nested(misc.RollbackContext(), + with nested(RollbackContext(), self._syncRoot.shared) as (contextCleanup, lock): try: namespaceObj = self._namespaces[namespace]