[cura-tools] master: fix exception handling in connect() (34bd0e8)
by phatina@fedoraproject.org
Repository : http://git.fedorahosted.org/cgit/cura-tools.git
On branch : master
>---------------------------------------------------------------
commit 34bd0e83211967e2a1c2c4866ffc448ee1362e7d
Author: Peter Hatina <phatina(a)redhat.com>
Date: Wed Oct 9 08:25:35 2013 +0200
fix exception handling in connect()
>---------------------------------------------------------------
cli/lmi/shell/LMIConnection.py | 16 ++++++++--------
1 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/cli/lmi/shell/LMIConnection.py b/cli/lmi/shell/LMIConnection.py
index 629310f..81e57c1 100644
--- a/cli/lmi/shell/LMIConnection.py
+++ b/cli/lmi/shell/LMIConnection.py
@@ -270,24 +270,24 @@ class LMIConnection(object):
CIMError exception with the flag CIM_ERR_NOT_FOUND set. Otherwise, the should
receive AuthError. Returns True, if provided credentials are OK.
"""
- use_exceptions = lmi_get_use_exceptions()
try:
+ use_exceptions = lmi_get_use_exceptions()
lmi_set_use_exceptions(True)
- self._client._get_class("SomeNonExistingClass")
+ try:
+ self._client._get_class("SomeNonExistingClass")
+ except:
+ raise
+ finally:
+ lmi_set_use_exceptions(use_exceptions)
except pywbem.cim_operations.CIMError, e:
if e.args[0] == pywbem.cim_constants.CIM_ERR_NOT_FOUND:
return True
lmi_raise_or_dump_exception(e)
- return False
except pywbem.cim_http.AuthError, e:
lmi_raise_or_dump_exception(e)
- return False
except OpenSSL.SSL.Error, e:
lmi_raise_or_dump_exception(e)
- return False
- finally:
- lmi_set_use_exceptions(use_exceptions)
- return True
+ return False
def subscribe_indication(self, **kwargs):
"""
10 years, 7 months
[cura-tools] master: introduce SIGINT and SIGTERM signal handling (27fe641)
by phatina@fedoraproject.org
Repository : http://git.fedorahosted.org/cgit/cura-tools.git
On branch : master
>---------------------------------------------------------------
commit 27fe64115ab037f95c30b6d465f14a62a12b1ae3
Author: Peter Hatina <phatina(a)redhat.com>
Date: Wed Oct 9 07:43:34 2013 +0200
introduce SIGINT and SIGTERM signal handling
When performing a synchronous method call, such call and job can be aborted by sending SIGINT or SIGTERM to the LMIShell.
>---------------------------------------------------------------
cli/lmi/shell/LMIMethod.py | 205 ++++++++++++++++++++++++++++++++++++-------
1 files changed, 171 insertions(+), 34 deletions(-)
diff --git a/cli/lmi/shell/LMIMethod.py b/cli/lmi/shell/LMIMethod.py
index 92faf65..b4f265f 100644
--- a/cli/lmi/shell/LMIMethod.py
+++ b/cli/lmi/shell/LMIMethod.py
@@ -14,11 +14,12 @@
# along with this program; if not, see <http://www.gnu.org/licenses/>.
import sys
-import time
import pywbem
+import signal
import socket
import urlparse
import threading
+import collections
from LMIBaseObject import LMIWrapperBaseObject
from LMIBaseClient import LMIBaseClient
@@ -50,6 +51,90 @@ from LMIExceptions import LMISynchroMethodCallFilterError
from LMIExceptions import LMIUnknownParameterError
from LMIExceptions import LMIHandlerNamePatternError
+class LMISignalHelper(object):
+ """
+ Helper class, which takes care of signal (de)registration and handling.
+ """
+
+ _instance = None
+
+ def __new__(cls):
+ """
+ Creates a new LMISignalHelper instance, if not created, and returns
+ the singleton instance object.
+ """
+ if cls._instance is None:
+ cls._instance = super(LMISignalHelper, cls).__new__(cls)
+ LMISignalHelper.reset(cls._instance)
+ return cls._instance
+
+ def reset(self):
+ """
+ Resets the single instance into default state.
+ """
+ self._handler_sigint = None
+ self._handler_sigterm = None
+ self._signal_handled = False
+ self._instance._callbacks = collections.OrderedDict()
+
+ def signal_attach(self):
+ """
+ Registers SIGINT and SIGTERM signals to local handler in which, the
+ flags for each signal are modified, if such signal is caught.
+ """
+ self._signal_handled = False
+ self._handler_sigint = signal.signal(signal.SIGINT, LMISignalHelper.__signal_handler)
+ self._handler_sigterm = signal.signal(signal.SIGTERM, LMISignalHelper.__signal_handler)
+
+ def signal_detach(self):
+ """
+ Deregisters SIGINT and SIGTERM handler and removes all the attached callbacks.
+ """
+ signal.signal(signal.SIGINT, self._handler_sigint)
+ signal.signal(signal.SIGTERM, self._handler_sigterm)
+
+ def signal_handled(self):
+ """
+ Returns True, if any of SIGINT or SIGTERM has been caught; False otherwise.
+ """
+ return self._signal_handled
+
+ def callback_attach(self, cb_name, cb):
+ """
+ Registers a callback, which will be called when a SIGINT or SIGTERM is caught.
+
+ Arguments:
+ cb_name -- string with name of the callback
+ cb -- callable object, which takes zero arguments
+ """
+ self._callbacks[cb_name] = cb
+
+ def callback_detach(self, cb_name):
+ """
+ Removes a callback from the callback dictionary.
+
+ Arguments:
+ cb_name -- string containing callback name
+ """
+ self._callbacks.pop(cb_name)
+
+ @staticmethod
+ def __signal_handler(signo, frame):
+ """
+ Signal handler, which is called, whem SIGINT and SIGTERM are sent to
+ the LMIShell.
+
+ Arguments:
+ signo -- signal number
+ frame -- stack frame
+
+ NOTE: see help(signal)
+ """
+ if signo in (signal.SIGINT, signal.SIGTERM):
+ LMISignalHelper._instance._signal_handled = True
+ for cb in LMISignalHelper._instance._callbacks.values():
+ cb()
+
class LMIMethod(LMIWrapperBaseObject):
"""
LMI wrapper class representing CIMMethod.
@@ -255,37 +340,40 @@ class LMIMethod(LMIWrapperBaseObject):
if lmi_is_job_finished(job_inst):
job_finished.value = JOB_FINISH_EARLY
+ # Register signal callback for SIGINT, SIGTERM with callback,
+ # which awakes waiting thread for immediate return.
+ LMISignalHelper().callback_attach("indication", lambda: LMIMethod.__wake(cond))
+ LMISignalHelper().signal_attach()
+
# Wait for the job to finish
- interrupt = False
- try:
- wake_cnt = 0
- cond.acquire()
- while True:
- if interrupt or job_finished.value:
+ wake_cnt = 0
+ cond.acquire()
+ while not LMISignalHelper().signal_handled() and \
+ not job_finished.value and \
+ not lmi_is_job_finished(job_inst):
+ cond.wait(LMIMethod._COND_WAIT_TIME)
+ wake_cnt += 1
+ # XXX: threading.Condition.wait() does not inform about timeout or being awaken by
+ # notify call. There is a counting to 4 sleep cycles before we actually check for
+ # job status manually. This number can be increased, so we rely more on indications,
+ # rather then on manual polling.
+ if wake_cnt >= LMIMethod._COND_WAIT_WAKE_CNT and \
+ not job_finished.value:
+ wake_cnt = 0
+ try:
+ (refreshed, _, errorstr) = job_inst.refresh()
+ except pywbem.cim_operations.CIMError, e:
+ job_exception.value = e
+ break
+ if not refreshed:
+ job_exception.value = LMISynchroMethodCallError(errorstr)
break
- cond.wait(LMIMethod._COND_WAIT_TIME)
- wake_cnt += 1
- # XXX: threading.Condition.wait() does not inform about timeout or being awaken by
- # notify call. There is a counting to 4 sleep cycles before we actually check for
- # job status manually. This number can be increased, so we rely more on indications,
- # rather then on manual polling.
- if not job_finished.value and wake_cnt >= LMIMethod._COND_WAIT_WAKE_CNT:
- wake_cnt = 0
- try:
- (refreshed, _, errorstr) = job_inst.refresh()
- except pywbem.cim_operations.CIMError, e:
- job_exception.value = e
- break
- if not refreshed:
- job_exception.value = LMISynchroMethodCallError(errorstr)
- break
- if lmi_is_job_finished(job_inst):
- listener.stop()
- break
- except KeyboardInterrupt:
- interrupt = True
- finally:
- cond.release()
+
+ # Unregister signal handler
+ LMISignalHelper().signal_detach()
+ LMISignalHelper().callback_detach("indication")
+
+ cond.release()
# Cleanup
listener.stop()
@@ -293,6 +381,13 @@ class LMIMethod(LMIWrapperBaseObject):
self._conn._client._delete_instance(cim_handler.path)
if job_exception.value:
raise job_exception.value
+ if LMISignalHelper().signal_handled() and not job_finished.value:
+ # We got SIGINT or SIGTERM, when waiting for the job, cancelling the job
+ sys.stderr.write("Cancelling a job '%s'\n" % job_inst.Name)
+ job_inst.RequestStateChange(
+ RequestedState=job_inst.RequestStateChange.RequestedStateValues.Terminate
+ )
+ return LMIReturnValue(rval=None)
# Return the job return values, refresh the job_inst object, if we got notified
# about the job finish state by indication.
@@ -311,18 +406,48 @@ class LMIMethod(LMIWrapperBaseObject):
Arguments:
job_inst -- LMIInstance of a job
"""
+
+ # Register signal callback for SIGINT, SIGTERM with callback,
+ # which awakes waiting thread for immediate return.
+ LMISignalHelper().callback_attach("polling", lambda: LMIMethod.__wake(cond))
+ LMISignalHelper().signal_attach()
+
+ cond = threading.Condition()
+ cond.acquire()
+
+ job_exception = None
+
try:
sleep_time = 1
- while not lmi_is_job_finished(job_inst):
+ while not LMISignalHelper().signal_handled() and \
+ not lmi_is_job_finished(job_inst):
# Sleep, a bit longer in every iteration
- time.sleep(sleep_time)
+ cond.wait(sleep_time)
if sleep_time < LMIMethod._POLLING_ADAPT_MAX_WAITING_TIME:
sleep_time *= 2
(refreshed, _, errorstr) = job_inst.refresh()
if not refreshed:
- raise LMISynchroMethodCallError(errorstr)
+ job_exception = LMISynchroMethodCallError(errorstr)
+ break
except pywbem.cim_operations.CIMError, e:
- raise LMISynchroMethodCallError(e.message)
+ job_exception = LMISynchroMethodCallError(e.message)
+ finally:
+ cond.release()
+
+ # Unregister signal handler and callback
+ LMISignalHelper().signal_detach()
+ LMISignalHelper().callback_detach("polling")
+
+ if LMISignalHelper().signal_handled() and not lmi_is_job_finished(job_inst):
+ # We got SIGINT or SIGTERM, when waiting for the job, cancelling the job
+ sys.stderr.write("Cancelling a job '%s'\n" % job_inst.Name)
+ job_inst.RequestStateChange(
+ RequestedState=job_inst.RequestStateChange.RequestedStateValues.Terminate
+ )
+ return LMIReturnValue(rval=None)
+
+ if not job_exception is None:
+ raise job_exception
# Return the job return values. No need to refresh the job instance, we already
# have a "fresh" one.
@@ -438,6 +563,18 @@ class LMIMethod(LMIWrapperBaseObject):
return LMIConstantValuesMethodReturnType(self._method)
raise AttributeError(name)
+ @staticmethod
+ def __wake(cond):
+ """
+ Helper function used for manual threading.Condition wakeup.
+
+ Arguments:
+ cond -- threading.Condition object
+ """
+ cond.acquire()
+ cond.notify()
+ cond.release()
+
@property
def return_type(self):
"""
10 years, 7 months