I'd suggest to specifically check for a timeout exception, and re-raise any others so that if something bad happens, the situation gets properly logged (instead of acting as if a timeout occurred).
Also I'd add a comment documenting that the socket is closed just after the first connection (and the communication with the connected client is magically kept open). On Tue, Feb 18, 2014 at 3:39 PM, Hrvoje Ribicic <[email protected]> wrote: > The gnt-debug delay command could be useful as a means of acquiring > locks for testing purposes. In practice, to be useful it should be > interruptible, otherwise we risk race conditions or long delays. > > This patch follows the examples of the move-instance command and the > gnt-debug test-jobqueue commands, and introduces a mechanism for > communicating with the delay command, allowing it to be interrupted > early. > > Signed-off-by: Hrvoje Ribicic <[email protected]> > --- > lib/cmdlib/test.py | 68 > ++++++++++++++++++++++++++++++++++++++++++++++++++---- > 1 file changed, 64 insertions(+), 4 deletions(-) > > diff --git a/lib/cmdlib/test.py b/lib/cmdlib/test.py > index d623986..f73a40d 100644 > --- a/lib/cmdlib/test.py > +++ b/lib/cmdlib/test.py > @@ -25,6 +25,7 @@ import logging > import shutil > import socket > import tempfile > +import time > > from ganeti import compat > from ganeti import constants > @@ -127,8 +128,52 @@ class LUTestDelay(NoHooksLU): > self.needed_locks = {} > self.needed_locks[locking.LEVEL_NODE] = self.op.on_node_uuids > > - def _TestDelay(self): > - """Do the actual sleep. > + def _InterruptibleDelay(self): > + """Delays but provides the mechanisms necessary to interrupt the > delay as > + needed. > + > + """ > + socket_wrapper = TestSocketWrapper() > + sock, path = socket_wrapper.Create() > + > + self.Log(constants.ELOG_DELAY_TEST, (path,)) > + > + try: > + sock.settimeout(self.op.duration) > + start = time.time() > + (conn, _) = sock.accept() > + except socket.error, _: > + # If we timed out, all is well > + return False > + finally: > + socket_wrapper.Destroy() > + > + try: > + # Change to remaining time > + time_to_go = self.op.duration - (time.time() - start) > + self.Log(constants.ELOG_MESSAGE, > + "Received connection, time to go is %d" % time_to_go) > + if time_to_go < 0: > + time_to_go = 0 > + # pylint: disable=E1101 > + # Instance of '_socketobject' has no ... member > + conn.settimeout(time_to_go) > + conn.recv(1) > + # pylint: enable=E1101 > + except socket.error, _: > + # A second timeout can occur if no data is sent > + return False > + finally: > + conn.close() > + > + self.Log(constants.ELOG_MESSAGE, > + "Interrupted, time spent waiting: %d" % (time.time() - > start)) > + > + # Reaching this point means we were interrupted > + return True > + > + def _UninterruptibleDelay(self): > + """Delays without allowing interruptions. > > """ > if self.op.on_node_uuids: > @@ -140,17 +185,32 @@ class LUTestDelay(NoHooksLU): > if not utils.TestDelay(self.op.duration)[0]: > raise errors.OpExecError("Error during master delay test") > > + def _TestDelay(self): > + """Do the actual sleep. > + > + @rtype: bool > + @return: Whether the delay was interrupted > + > + """ > + if self.op.interruptible: > + return self._InterruptibleDelay() > + else: > + self._UninterruptibleDelay() > + return False > + > def Exec(self, feedback_fn): > """Execute the test delay opcode, with the wanted repetitions. > > """ > if self.op.repeat == 0: > - self._TestDelay() > + i = self._TestDelay() > else: > top_value = self.op.repeat - 1 > for i in range(self.op.repeat): > self.LogInfo("Test delay iteration %d/%d", i, top_value) > - self._TestDelay() > + # Break in case of interruption > + if self._TestDelay(): > + break > > > class LUTestJqueue(NoHooksLU): > -- > 1.9.0.rc1.175.g0b1dcb5 > >
