Zhou Zheng Sheng has uploaded a new change for review.
Change subject: tests: add a rollback manager for easy undoing
......................................................................
tests: add a rollback manager for easy undoing
Sometimes we need to perform a series of operations:
op[0], op[1], ... op[N]
These operations may allocate files, locks, connections, and op[K] may
depend on op[K-1] 's result
Consider these are contexts, after constructing contexts, we want to
perform some computing using these contexts. Exception may be raised in
the complicated construction stage of the contexts or when we're using
the contexes.
So if op[K] fails, we need to:
undo op[K-1], undo op[K-2], ... undo op[0]
These undo operations release the resources,
or if all the operations succeed, at last we need to:
undo op[N], undo op[N-1], ... undo op[0]
Furthermore, We want to suppress the exceptions occured in "undo op[X]",
and continue the rollback, so that all the resources can be freed.
At last, we want to see the first exception raised so that we can fix
the root cause, so we want to have the oldest exception occured in this
batch of operation reraised.
This patch proposes a concise framework to do this kind of
rollback. It's an upgrade version of contextlib.contextmanager .
Change-Id: Ibc932637dd81c3becf92de34ea647c1cea136111
Signed-off-by: Zhou Zheng Sheng <zhshzhou(a)linux.vnet.ibm.com>
---
M tests/Makefile.am
A tests/rollbackManagerTests.py
M tests/testrunner.py
3 files changed, 161 insertions(+), 0 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/71/8671/1
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 2b61dde..02f48ab 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -45,6 +45,7 @@
persistentDictTests.py \
restTests.py \
restData.py \
+ rollbackManagerTests.py \
tcTests.py \
vdsClientTests.py \
remoteFileHandlerTests.py \
diff --git a/tests/rollbackManagerTests.py b/tests/rollbackManagerTests.py
new file mode 100644
index 0000000..db3a5a0
--- /dev/null
+++ b/tests/rollbackManagerTests.py
@@ -0,0 +1,85 @@
+#
+# Copyright IBM Corp. 2012
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Refer to the README and COPYING files for full details of the license
+#
+import glob
+import os
+import tempfile
+import uuid
+
+from testrunner import VdsmTestCase as TestCaseBase
+from testrunner import rollbackManager
+
+
+class ContextError(Exception):
+ pass
+
+
+class ConsumerError(Exception):
+ pass
+
+
+class TestRollbackManager(TestCaseBase):
+ def setUp(self):
+ self.tmpdirPrefix = 'testrollback' + str(uuid.uuid4())
+
+ @rollbackManager
+ def tempfiles(self, fileCount, excClass, rollback):
+ dirPath = tempfile.mkdtemp(prefix=self.tmpdirPrefix)
+ undo = lambda: os.rmdir(dirPath)
+ rollback.append(undo)
+
+ for i in range(0, fileCount):
+ path = os.path.join(dirPath, str(i))
+ with open(path, "wb") as f:
+ undo = \
+ lambda path=path: os.remove(path)
+ rollback.append(undo)
+ f.write(str(i))
+
+ if excClass is not None:
+ raise excClass("context error")
+
+ return dirPath
+
+ def testExceptionInContext(self):
+ def exceptionInContext():
+ with self.tempfiles(10, ContextError):
+ pass
+
+ self.assertRaises(ContextError, exceptionInContext)
+ # Directory and files should be removed
+ self.assertEquals(glob.glob(self.tmpdirPrefix + "*"), [])
+
+ def testExceptionInConsumer(self):
+ def exceptionInConsumer():
+ with self.tempfiles(10, None):
+ raise ConsumerError("consumer error")
+
+ self.assertRaises(ConsumerError, exceptionInConsumer)
+ # Directory and files should be removed
+ self.assertEquals(glob.glob(self.tmpdirPrefix + "*"), [])
+
+ def testNormalConsumer(self):
+ fileCount = 10
+ with self.tempfiles(fileCount, None) as dirPath:
+ for i in range(0, fileCount):
+ with open(os.path.join(dirPath, str(i)), "rb") as f:
+ self.assertEquals(int(f.read()), i)
+ # Directory and files should be removed
+ self.assertEquals(glob.glob(self.tmpdirPrefix + "*"), [])
diff --git a/tests/testrunner.py b/tests/testrunner.py
index cdbc9d3..f9cc323 100644
--- a/tests/testrunner.py
+++ b/tests/testrunner.py
@@ -22,6 +22,7 @@
import os
import unittest
from functools import wraps
+from contextlib import contextmanager
from nose import config
from nose import core
@@ -239,6 +240,80 @@
return False
+def rollbackManager(transaction):
+ '''
+ A contextmanager-like manager for easy undoing. It's an upgraded
+ contextlib.contextmanager and can manage a variable number of contextes.
+
+ It is used as a decorator to a function. There must exist a parameter
+ named "rollback" of the decorated function. Then the function can treat
+ the "rollback" as a list and append undo operations(lambdas, closures, ...)
+ to the list. The function will be put in the "with" statement, the return
+ value of the function will be assigned to the "as" variable. If an
+ exception is raised, whether it's raised in the function or in the block
+ under the "with" statement, the registered undo operations will be played
+ in reverse order. When peforming rollback, exceptions will be swalloweded
+ to let rollback continue, at last, the earliest exception with original
+ line number and stack trace infomation will be raised.
+
+ Simple example:
+ _________________________________________
+ @rollbackManager
+ def foo(a, b, rollback):
+ x = allocate_X(a)
+ rollback.append(lambda: release_X(x))
+
+ # Need not to catch the exception when allocating Y
+ # and release x in a "try..final" block.
+ # The rollback Manager will do that.
+ y = allocate_Y_using(x)
+ rollback.append(lambda: release_Y(y))
+
+ # Need not to catch the exception when computing z
+ # and release x, y. Let rollback Manager do it for you.
+ z = do_something_with(x, y)
+ return z
+
+ with foo(blah1, blah2) as z:
+ visit(z)
+ _________________________________________
+ When the "with" block is exited, resource y will be released first, then
+ resource x. If exception is raised when constructing y, then only the
+ allocated x will be released, and the original exception will be re-raised.
+ '''
+
+ @contextmanager
+ def wrapper(*args, **kwargs):
+ rollback = []
+ exception = None
+ traceback = None
+ try:
+ yield transaction(rollback=rollback, *args, **kwargs)
+ except Exception as e:
+ # keep the original exception and traceback info
+ exception = e
+ traceback = sys.exc_info()[2]
+ finally:
+ rollback.reverse()
+ _playRollback(rollback, exception, traceback)
+ return wrapper
+
+
+def _playRollback(rollback, exception=None, traceback=None):
+ for undo in rollback:
+ try:
+ undo()
+ except Exception as e:
+ # keep the earliest exception info
+ if not exception:
+ exception = e
+ # keep the original traceback info
+ traceback = sys.exc_info()[2]
+ # re-raise the earliest exception
+ if exception:
+ raise exception, None, traceback
+
+
if __name__ == '__main__':
if "--help" in sys.argv:
print("testrunner options:\n"
--
To view, visit http://gerrit.ovirt.org/8671
To unsubscribe, visit http://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Ibc932637dd81c3becf92de34ea647c1cea136111
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Zhou Zheng Sheng <zhshzhou(a)linux.vnet.ibm.com>
Royce Lv has uploaded a new change for review.
Change subject: join supervdsm subprocess after terminated to avoid defunct status
......................................................................
join supervdsm subprocess after terminated to avoid defunct status
After validateAccess there leaves a defunct process
clear it by join after termination
Change-Id: I0cdcfb3b467e9226f14a4ebc2845ff2db0f19b5f
Signed-off-by: Royce Lv<lvroyce(a)linux.vnet.ibm.com>
---
M vdsm/supervdsmServer.py
1 file changed, 1 insertion(+), 0 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/03/6503/1
--
To view, visit http://gerrit.ovirt.org/6503
To unsubscribe, visit http://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I0cdcfb3b467e9226f14a4ebc2845ff2db0f19b5f
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Royce Lv <lvroyce(a)linux.vnet.ibm.com>
Zhou Zheng Sheng has uploaded a new change for review.
Change subject: xmlrpcTests: narrow the expected exception when using retry
......................................................................
xmlrpcTests: narrow the expected exception when using retry
Change the expected exception from the default Exception to
AssertionError
Change-Id: I0deded14d02a21bcfbca49dbdfadfe8652added3
Signed-off-by: Zhou Zheng Sheng <zhshzhou(a)linux.vnet.ibm.com>
---
M tests/functional/xmlrpcTests.py
1 file changed, 4 insertions(+), 2 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/44/8444/1
diff --git a/tests/functional/xmlrpcTests.py b/tests/functional/xmlrpcTests.py
index 6ccaf66..1d44059 100644
--- a/tests/functional/xmlrpcTests.py
+++ b/tests/functional/xmlrpcTests.py
@@ -122,7 +122,8 @@
'vmName': 'foo'})
self.assertVdsOK(r)
try:
- retry(lambda: self.assertVmUp(VMID), timeout=20)
+ retry(lambda: self.assertVmUp(VMID),
+ expectedException=AssertionError, timeout=20)
finally:
# FIXME: if the server dies now, we end up with a leaked VM.
r = self.s.destroy(VMID)
@@ -154,7 +155,8 @@
try:
self.assertVdsOK(self.s.create(conf))
# wait 65 seconds for VM to come up until timeout
- retry(assertVMAndGuestUp, timeout=65)
+ retry(assertVMAndGuestUp, expectedException=AssertionError,
+ timeout=65)
finally:
destroyResult = self.s.destroy(VMID)
--
To view, visit http://gerrit.ovirt.org/8444
To unsubscribe, visit http://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I0deded14d02a21bcfbca49dbdfadfe8652added3
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Zhou Zheng Sheng <zhshzhou(a)linux.vnet.ibm.com>
Juan Hernandez has uploaded a new change for review.
Change subject: Implement SSL session cache
......................................................................
Implement SSL session cache
We are currently implementing SSL for the XML-RPC communications with
the ssl module. Unfortunately this module uses OpenSSL in such a way
that a new context is created for each TCP connection. This means that
the heavy part of the SSL handshake (asymmetric encryption) is
performed for each connection. In addition as we use HTTP 1.0 this
also means that we use one connection per request, and thus one
handshake per request. This is a potential performance problem in the
VDSM side and a huge performance problem in the engine side: when the
number of hosts managed by the engine the use of CPU grows to a point
where almost all the CPU is used for SSL handshares.
This patch uses the OpenSSL.SSL module (pyOpenSSL) in a way such that
SSL sessions are cached, thus greatly reducing the amount of CPU
needed for handshakes, specially in the engine.
Bug-Id: https://bugzilla.redhat.com/857035
Change-Id: Ic75adee4070b415b8855af1f2ea289825496fbc1
Signed-off-by: Juan Hernandez <juan.hernandez(a)redhat.com>
---
M vdsm/SecureXMLRPCServer.py
1 file changed, 125 insertions(+), 4 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/23/8123/1
diff --git a/vdsm/SecureXMLRPCServer.py b/vdsm/SecureXMLRPCServer.py
index 9396a28..744e594 100644
--- a/vdsm/SecureXMLRPCServer.py
+++ b/vdsm/SecureXMLRPCServer.py
@@ -35,6 +35,129 @@
import socket
import SocketServer
+from OpenSSL import SSL
+
+
+class SSLServerSocket(object):
+ """SSL decorator for server sockets.
+
+ This class wraps a normal socket so that when the accept method
+ is called the client socket as also decorated with SSL functionality.
+ The rest of the methods are just delegated to the raw socket.
+ """
+
+ def __init__(self, raw, certfile=None, keyfile=None, ca_certs=None):
+ # Save the reference to the raw socket so that we can delegate
+ # calls to it later:
+ self.raw = raw
+
+ # Create the OpenSSL context:
+ self.context = SSL.Context(SSL.SSLv3_METHOD)
+ self.context.set_session_id("vdsm")
+
+ # Load the crendentials:
+ if certfile and keyfile:
+ self.context.use_certificate_file(certfile)
+ self.context.use_privatekey_file(keyfile)
+
+ # Load the CA certificates used to verify certifictes presented
+ # by the clients:
+ if ca_certs:
+ self.context.load_verify_locations(ca_certs)
+ self.context.set_verify(
+ SSL.VERIFY_PEER |
+ SSL.VERIFY_FAIL_IF_NO_PEER_CERT |
+ SSL.VERIFY_CLIENT_ONCE,
+ SSLServerSocket.verify)
+
+ @staticmethod
+ def verify(connection, certificate, x, y, z):
+ # No need for additional verifications:
+ return True
+
+ def accept(self):
+ # Get the client socket and address calling the accept method of
+ # the raw socket and replace the client socket with a wrapper:
+ client, address = self.raw.accept()
+ client = SSLClientSocket(client, self.context)
+ return client, address
+
+ def bind(self, address):
+ return self.raw.bind(address)
+
+ def fileno(self):
+ return self.raw.fileno()
+
+ def getsockname(self):
+ return self.raw.getsockname()
+
+ def gettimeout(self):
+ return self.raw.gettimeout()
+
+ def listen(self, backlog=5):
+ return self.raw.listen(backlog)
+
+ def setsockopt(self, level, optname, value):
+ return self.raw.setsockopt(level, optname, value)
+
+
+class SSLClientSocket(object):
+ """SSL decorator for client sockets.
+
+ This class wraps a client socket returned by the accept
+ method of a server socket providing the SSL functionality.
+
+ Note that most methods are delegated to the wrapped raw
+ and many are just not implemented because they are not used
+ in our use case.
+ """
+
+ def __init__(self, raw, context):
+ # Save the reference to the raw client socket so that we can
+ # delegate calls to it later:
+ self.raw = raw
+ self.context = context
+
+ # Create a new SSL connection and a file like object on top of it
+ # in order to be able to implement the makefile method used by
+ # the XML RPC server:
+ self.connection = SSL.Connection(self.context, self.raw)
+
+ # Configure the connection to that it will automatically do the
+ # hand shake before the first read or write operation:
+ self.connection.set_accept_state()
+
+ def close(self):
+ return self.connection.close()
+
+ def do_handshake(self):
+ return self.connection.do_handshake()
+
+ def makefile(self, mode="r", bufsize=-1):
+ # In order to implement this method we borrow the _fileobject
+ # function from the socket module, and that forces us to
+ # implement correcty the recv and send family of methods:
+ return socket._fileobject(self, mode, bufsize)
+
+ def recv(self, bufsize, flags=0):
+ return self.connection.recv(bufsize)
+
+ def send(self, string, flags=0):
+ return self.connection.send(string)
+
+ def sendall(self, string, flags=0):
+ return self.connection.sendall(string)
+
+ def setsockopt(self, level, optname, value):
+ self.connection.setsockopt(level, optname, value)
+
+ def shutdown(self, how):
+ # Not completly sure, but I think we should first shutdown the
+ # SSL connection and then the raw socket:
+ self.connection.shutdown()
+ self.raw.shutdown(how)
+
+
SecureXMLRPCRequestHandler = SimpleXMLRPCServer.SimpleXMLRPCRequestHandler
@@ -53,11 +176,9 @@
requestHandler,
logRequests, allow_none, encoding,
bind_and_activate=False)
- self.socket = ssl.wrap_socket(self.socket,
+ self.socket = SSLServerSocket(self.socket,
keyfile=keyfile, certfile=certfile,
- ca_certs=ca_certs, server_side=True,
- cert_reqs=ssl.CERT_REQUIRED,
- do_handshake_on_connect=False)
+ ca_certs=ca_certs)
if timeout is not None:
self.socket.settimeout = timeout
if bind_and_activate:
--
To view, visit http://gerrit.ovirt.org/8123
To unsubscribe, visit http://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Ic75adee4070b415b8855af1f2ea289825496fbc1
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Juan Hernandez <juan.hernandez(a)redhat.com>
Saggi Mizrahi has uploaded a new change for review.
Change subject: Fix problem where a 0 error code will cause remoteFileHandler to become a zombie
......................................................................
Fix problem where a 0 error code will cause remoteFileHandler to become a zombie
Change-Id: I738d9739673d11c15b882743d412b9b267013208
Signed-off-by: Saggi Mizrahi <smizrahi(a)redhat.com>
---
M vdsm/storage/remoteFileHandler.py
1 file changed, 6 insertions(+), 4 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/07/8907/1
diff --git a/vdsm/storage/remoteFileHandler.py b/vdsm/storage/remoteFileHandler.py
index 43ee951..446087b 100644
--- a/vdsm/storage/remoteFileHandler.py
+++ b/vdsm/storage/remoteFileHandler.py
@@ -240,10 +240,12 @@
except:
pass
- # For some reason Thread might have been released if python is going
- # down. This makes sure that there are no issues when this happens
- if self.process.poll() and Thread:
- Thread(target=self.process.wait).start()
+ if not self.process.wait(0):
+ # For some reason Thread might have been released if python is
+ # going down. This makes sure that there are no issues when this
+ # happens
+ if Thread:
+ Thread(target=self.process.wait).start()
def __del__(self):
self.stop()
--
To view, visit http://gerrit.ovirt.org/8907
To unsubscribe, visit http://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I738d9739673d11c15b882743d412b9b267013208
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Saggi Mizrahi <smizrahi(a)redhat.com>