- Add more error handling
- Refactor clm code
- Keep raising exceptions for existing python methods
- Add __version__ attribute to utils
---
python/pyosaf/utils/__init__.py | 263 +++++++++++++---
python/pyosaf/utils/clm/__init__.py | 595 ++++++++++++++++++++++++++++++------
2 files changed, 730 insertions(+), 128 deletions(-)
diff --git a/python/pyosaf/utils/__init__.py b/python/pyosaf/utils/__init__.py
index 7a1c21b79..f39c6623b 100644
--- a/python/pyosaf/utils/__init__.py
+++ b/python/pyosaf/utils/__init__.py
@@ -16,17 +16,31 @@
#
############################################################################
""" pyosaf common utils """
+import os
+import syslog
import time
+import warnings
from copy import deepcopy
+from ctypes import POINTER
-from pyosaf.saAis import eSaAisErrorT
+from pyosaf import saImmOm
+from pyosaf.saAis import eSaAisErrorT, SaStringT, unmarshalNullArray
+# Version for pyosaf utils
+__version__ = "1.0.0"
-TRY_AGAIN_COUNT = 60
+# The 'MAX_RETRY_TIME' and 'RETRY_INTERVAL' environment variables shall be set
+# with user-defined values BEFORE importing the pyosaf 'utils' module;
+# Otherwise, the default values for MAX_RETRY_TIME(60s) and RETRY_INTERVAL(1s)
+# will be used throughout.
+MAX_RETRY_TIME = int(os.environ.get("MAX_RETRY_TIME")) \
+ if "MAX_RETRY_TIME" in os.environ else 60
+RETRY_INTERVAL = int(os.environ.get("RETRY_INTERVAL")) \
+ if "RETRY_INTERVAL" in os.environ else 1
class SafException(Exception):
- """ SAF Exception that can be printed """
+ """ SAF Exception for error during executing SAF functions """
def __init__(self, value, msg=None):
Exception.__init__(self)
self.value = value
@@ -44,73 +58,244 @@ def raise_saf_exception(func, error):
raise SafException(error, error_string)
+def check_resource_abort(ccb_handle):
+ """ Get error strings from IMM and check if it is a resource abort case
+
+ Args:
+ ccb_handle (SaImmCcbHandleT): CCB handle
+
+ Returns:
+ bool: 'True' if it is a resource abort case. 'False', otherwise.
+ """
+ c_error_strings = POINTER(SaStringT)()
+ saImmOmCcbGetErrorStrings = decorate(saImmOm.saImmOmCcbGetErrorStrings)
+
+ # Get error strings
+ # As soon as the ccb_handle is finalized, the strings are freed
+ rc = saImmOmCcbGetErrorStrings(ccb_handle, c_error_strings)
+ if rc == eSaAisErrorT.SA_AIS_OK:
+ if c_error_strings:
+ list_err_strings = unmarshalNullArray(c_error_strings)
+ for c_error_string in list_err_strings:
+ if c_error_string.startswith("IMM: Resource abort: "):
+ return True
+
+ return False
+
+
def decorate(func):
""" Decorate the given SAF function so that it retries a fixed number of
- times if needed and raises an exception if it encounters any fault other
- than SA_AIS_ERR_TRY_AGAIN.
+ times for certain returned error codes during execution
+
+ Args:
+ func (function): The decorated SAF function
+
+ Returns:
+ SaAisErrorT: Return code of the decorated SAF function
"""
def inner(*args):
- """ Call "function" in the lexical scope in a retry loop and raise an
- exception if it encounters any fault other than TRY_AGAIN
+ """ Call the decorated function in the lexical scope in a retry loop
+
+ Args:
+ args (tuple): Arguments of the decorated SAF function
"""
- one_sec_sleeps = 0
- error = func(*args)
+ count_sec_sleeps = 0
+
+ rc = func(*args)
- while error == eSaAisErrorT.SA_AIS_ERR_TRY_AGAIN:
- if one_sec_sleeps == TRY_AGAIN_COUNT:
+ while rc != eSaAisErrorT.SA_AIS_OK:
+
+ if count_sec_sleeps >= MAX_RETRY_TIME:
break
- time.sleep(1)
- one_sec_sleeps += 1
- error = func(*args)
+ if rc == eSaAisErrorT.SA_AIS_ERR_TRY_AGAIN:
+ sleep_time_interval = RETRY_INTERVAL
+ elif rc == eSaAisErrorT.SA_AIS_ERR_NO_RESOURCES:
+ sleep_time_interval = RETRY_INTERVAL
+ elif rc == eSaAisErrorT.SA_AIS_ERR_BUSY:
+ sleep_time_interval = 3 * RETRY_INTERVAL
+ elif rc == eSaAisErrorT.SA_AIS_ERR_FAILED_OPERATION:
+ # Retry on getting FAILED_OPERATION only applies to IMM
+ # CCB-related operations in case of a resource abort;
+ ccb_handle = args[0]
+ resource_abort = check_resource_abort(ccb_handle)
+ if not resource_abort:
+ break
+ sleep_time_interval = RETRY_INTERVAL
+ else:
+ break # Break out of the retry loop
- if error != eSaAisErrorT.SA_AIS_OK:
- raise_saf_exception(func, error)
+ # Check sleep_time_interval to sleep and retry the function
+ time.sleep(sleep_time_interval)
+ count_sec_sleeps += sleep_time_interval
+ rc = func(*args)
- return error
+ return rc
return inner
-def initialize_decorate(func):
+def initialize_decorate(init_func):
""" Decorate the given SAF sa<Service>Initialize() function so that it
- retries a fixed number of times if needed with the same arguments and
- raises an exception if it encounters any fault other than
- SA_AIS_ERR_TRY_AGAIN.
+ retries a fixed number of times with the same arguments for certain
+ returned error codes during execution
"""
def inner(*args):
- """ Call "function" in the lexical scope in a retry loop and raise an
- exception if it encounters any fault other than TRY_AGAIN
+ """ Call the decorated Initialize() function in the lexical scope in a
+ retry loop
Args:
- args (tuple): Arguments of SAF Initialize() function with format
- (handle, callbacks, version)
+ args (tuple): Arguments of the SAF Initialize() function with
+ format (handle, callbacks, version)
"""
- # Backup current version
+ count_sec_sleeps = 0
+
+ # Backup the current version
backup_version = deepcopy(args[2])
- one_sec_sleeps = 0
- error = func(*args)
+ rc = init_func(*args)
+ while rc != eSaAisErrorT.SA_AIS_OK:
- while error == eSaAisErrorT.SA_AIS_ERR_TRY_AGAIN:
- if one_sec_sleeps == TRY_AGAIN_COUNT:
+ if count_sec_sleeps >= MAX_RETRY_TIME:
break
- time.sleep(1)
- one_sec_sleeps += 1
+ if rc == eSaAisErrorT.SA_AIS_ERR_TRY_AGAIN:
+ sleep_time_interval = RETRY_INTERVAL
+ elif rc == eSaAisErrorT.SA_AIS_ERR_NO_RESOURCES:
+ sleep_time_interval = RETRY_INTERVAL
+ elif rc == eSaAisErrorT.SA_AIS_ERR_BUSY:
+ sleep_time_interval = 3 * RETRY_INTERVAL
+ else:
+ break # Break out of the retry loop
+
+ # Check sleep_time_interval to sleep and retry the function
+ time.sleep(sleep_time_interval)
+ count_sec_sleeps += sleep_time_interval
# If the SAF Initialize() function returns ERR_TRY_AGAIN, the
- # version (as output argument) will still be updated to the latest
- # supported service API version; thus we need to restore the
- # original backed-up version before next retry of initialization.
+ # version (as output argument) will still get updated to the
+ # latest supported service API version; thus we need to restore
+ # the original backed-up version before the next retry of
+ # initialization.
version = deepcopy(backup_version)
args = args[:2] + (version,)
- error = func(*args)
+ rc = init_func(*args)
- if error != eSaAisErrorT.SA_AIS_OK:
- raise_saf_exception(func, error)
+ return rc
- return error
+ return inner
+
+
+def bad_handle_retry(func):
+ """ Decorate the given function so that it retries a fixed number of times
+ if getting the error code SA_AIS_ERR_BAD_HANDLE during execution
+
+ Args:
+ func (function): The decorated function
+
+ Returns:
+ Return code/output of the decorated function
+ """
+ def inner(*args, **kwargs):
+ """ Call the decorated function in the lexical scope in a retry loop
+ if it gets the returned error code SA_AIS_ERR_BAD_HANDLE
+
+ Args:
+ args (tuple): Arguments of the decorated function
+ """
+ count_sec_sleeps = 0
+ sleep_time_interval = 10 * RETRY_INTERVAL
+
+ result = func(*args, **kwargs)
+ rc = result[0] if type(result) is tuple else result
+ while rc == eSaAisErrorT.SA_AIS_ERR_BAD_HANDLE:
+ if count_sec_sleeps >= MAX_RETRY_TIME:
+ break
+
+ time.sleep(sleep_time_interval)
+ count_sec_sleeps += sleep_time_interval
+ result = func(*args, **kwargs)
+ rc = result[0] if type(result) is tuple else result
+
+ return result
return inner
+
+
+def deprecate(func):
+ """ Decorate the given function as deprecated
+
+ A warning message to notify the users about the function deprecation will
+ be displayed if the users have enabled the filter for this kind of warning
+
+ Args:
+ func (function): The deprecated function
+
+ Returns:
+ Return code/output of the decorated function
+ """
+ def inner(*args, **kwargs):
+ """ Call the deprecated function in the lexical scope """
+ warnings.warn("This function will be deprecated in future release. "
+ "Please consider using its OOP counterpart.",
+ PendingDeprecationWarning)
+ return func(*args, **kwargs)
+
+ return inner
+
+
+###############################
+# Common system logging utils #
+###############################
+def log_err(message):
+ """ Print a message to syslog at ERROR level
+
+ Args:
+ message (str): Message to be printed to syslog
+ """
+ syslog.syslog(syslog.LOG_ERR, "ER " + message)
+
+
+def log_warn(message):
+ """ Print a message to syslog at WARNING level
+
+ Args:
+ message (str): Message to be printed to syslog
+ """
+ syslog.syslog(syslog.LOG_WARNING, "WA " + message)
+
+
+def log_notice(message):
+ """ Print a message to syslog at NOTICE level
+ Args:
+ message (str): Message to be printed to syslog
+ """
+ syslog.syslog(syslog.LOG_NOTICE, "NO " + message)
+
+
+def log_info(message):
+ """ Print a message to syslog at INFO level
+
+ Args:
+ message (str): Message to be printed to syslog
+ """
+ syslog.syslog(syslog.LOG_INFO, "IN " + message)
+
+
+def log_debug(message):
+ """ Print a message to syslog at DEBUG level
+
+ Args:
+ message (str): Message to be printed to syslog
+ """
+ syslog.syslog(syslog.LOG_DEBUG, "DB " + message)
+
+
+def log_init(ident):
+ """ Initialize system logging function
+
+ Args:
+ ident(str): A string to be prepended to each message
+ """
+ syslog.openlog(ident, syslog.LOG_PID, syslog.LOG_USER)
diff --git a/python/pyosaf/utils/clm/__init__.py
b/python/pyosaf/utils/clm/__init__.py
index 53b86261f..f1a4b752b 100644
--- a/python/pyosaf/utils/clm/__init__.py
+++ b/python/pyosaf/utils/clm/__init__.py
@@ -17,10 +17,14 @@
############################################################################
# pylint: disable=unused-argument
""" CLM common utilities """
-from pyosaf.saAis import saAis, SaVersionT, eSaDispatchFlagsT
+from copy import deepcopy
+
+from pyosaf.saAis import saAis, SaVersionT, eSaAisErrorT, eSaDispatchFlagsT
from pyosaf import saClm
-from pyosaf.utils import decorate, initialize_decorate
+from pyosaf.utils import decorate, initialize_decorate, deprecate, \
+ bad_handle_retry, log_err, SafException
+_clm_agent = None
# Decorate pure saClm* API's with error-handling retry and exception raising
saClmInitialize = initialize_decorate(saClm.saClmInitialize)
@@ -30,93 +34,387 @@ saClmSelectionObjectGet =
decorate(saClm.saClmSelectionObjectGet)
saClmDispatch = decorate(saClm.saClmDispatch)
saClmFinalize = decorate(saClm.saClmFinalize)
saClmClusterTrack = decorate(saClm.saClmClusterTrack)
-saClmClusterNodeGet = decorate(saClm.saClmClusterNodeGet)
-saClmClusterNotificationFree = decorate(saClm.saClmClusterNotificationFree)
saClmClusterTrack_4 = decorate(saClm.saClmClusterTrack_4)
saClmClusterTrackStop = decorate(saClm.saClmClusterTrackStop)
+saClmClusterNotificationFree = decorate(saClm.saClmClusterNotificationFree)
saClmClusterNotificationFree_4 = decorate(saClm.saClmClusterNotificationFree_4)
+saClmClusterNodeGet = decorate(saClm.saClmClusterNodeGet)
saClmClusterNodeGet_4 = decorate(saClm.saClmClusterNodeGet_4)
saClmClusterNodeGetAsync = decorate(saClm.saClmClusterNodeGetAsync)
saClmResponse_4 = decorate(saClm.saClmResponse_4)
-# Create the handle
-handle = saClm.SaClmHandleT()
-track_function = None
+class ClmAgentManager(object):
+ """ This class manages the life cycle of a CLM agent, and also acts as
+ a proxy handler for CLM callbacks """
-def track_callback(c_notification_buffer, c_number_of_members, c_invocation_id,
- c_root_cause_entity, c_correlation_ids, c_step,
- c_time_supervision, c_error):
- """ This callback is invoked to get information about cluster membership
- changes in the structure to which the notificationBuffer parameter points.
+ def __init__(self, version=None):
+ """ Constructor for ClmAgentManager class
- Args:
- c_notification_buffer (SaClmClusterNotificationBufferT_4): Notification
- buffer
- c_number_of_members (SaUint32T): Number of members
- c_invocation_id (SaInvocationT): Invocation id
- c_root_cause_entity (SaNameT): Root cause entity
- c_correlation_ids (SaNtfCorrelationIdsT): Correlation ids
- c_step (SaClmChangeStepT): Change step
- c_time_supervision (SaTimeT): Time supervision
- c_error (SaAisErrorT): Return code
- """
+ Args:
+ version (SaVersionT): CLM API version
+ """
+ self.init_version = version
+ self.version = None
+ self.handle = None
+ self.callbacks = None
+ self.sel_obj = saClm.SaSelectionObjectT()
+ self.track_function = None
+ self.node_get_function = None
+
+ def track_callback(self, c_notification_buffer, c_number_of_members,
+ c_invocation_id, c_root_cause_entity, c_correlation_ids,
+ c_step, c_time_supervision, c_error):
+ """ This callback is invoked by CLM to notify the subscribed cluster
+ membership tracker about changes in the cluster membership, along with
+ detailed information of the changes.
- if track_function:
- added = []
- removed = []
- step = c_step
+ Args:
+ c_notification_buffer (SaClmClusterNotificationBufferT_4):
+ A pointer to a structure containing pointer to an array of
+ information structures about current member nodes and their
+ membership changes if any
+ c_number_of_members (SaUint32T): The current number of member nodes
+ c_invocation_id (SaInvocationT): Invocation id identifying this
+ particular callback invocation, and which shall be used in
+ saClmResponse_4() to respond to CLM in certain cases
+ c_root_cause_entity (SaNameT): A pointer to the DN of the CLM node
+ directly targeted by the action or event that caused the
+ membership change
+ c_correlation_ids (SaNtfCorrelationIdsT): A pointer to the
+ correlation identifiers associated with the root cause
+ c_step (SaClmChangeStepT): The tracking step in which this callback
+ is invoked
+ c_time_supervision (SaTimeT): The time specifying how long CLM will
+ wait for the process to provide the response for the callback
+ by invoking the saClmResponse_4() function
+ c_error (SaAisErrorT): Return code to indicate whether CLM was able
+ to perform the requested operation
+ """
+ if self.track_function:
+ invocation = c_invocation_id
+ step = c_step
+ num_of_members = c_number_of_members
+ error = c_error
+ # List of tuples (ClusterNode, clusterChange)
+ node_list = []
- if step == saClm.eSaClmChangeStepT.SA_CLM_CHANGE_COMPLETED:
notification_buffer = c_notification_buffer.contents
-
- i = 0
- for notification in notification_buffer.notification:
- if i == notification_buffer.numberOfItems:
- break
- else:
- i = i + 1
-
+ for i in range(notification_buffer.numberOfItems):
+ notification = notification_buffer.notification[i]
clm_cluster_node = notification.clusterNode
cluster_node = \
create_cluster_node_instance(clm_cluster_node)
+ node_state = (cluster_node, notification.clusterChange)
- if notification.clusterChange == \
- saClm.eSaClmClusterChangesT.SA_CLM_NODE_JOINED:
- added.append(cluster_node)
- elif notification.clusterChange == \
- saClm.eSaClmClusterChangesT.SA_CLM_NODE_LEFT:
- removed.append(cluster_node)
+ node_list.append(node_state)
- track_function(added, removed)
+ # Send the node list to user's callback function
+ self.track_function(node_list, invocation, step, num_of_members,
+ error)
+ def node_get_callback(self, c_invocation_id, c_cluster_node, c_error):
+ """ This callback is invoked by CLM to return information about the
+ requested member node to a registered client that had previously called
+ saClmClusterNodeGetAsync().
-def node_get_callback(*args):
- """ Dummy function used as a callback when no proper callbacks are set """
- pass
+ Args:
+ c_invocation_id(SaInvocationT): Invocation id associating this
+ callback invocation with the corresponding previous invocation
+ of saClmClusterNodeGetAsync()
+ c_cluster_node(SaClmClusterNodeT_4): A pointer to the structure
+ that contains information about the requested member node
+ c_error(SaAisErrorT): Return code to indicate whether the
+ saClmClusterNodeGetAsync() function was successful
+ """
+ if self.node_get_function:
+ invocation = c_invocation_id
+ error = c_error
+ cluster_node = \
+ create_cluster_node_instance(c_cluster_node.contents)
-def create_cluster_node_instance(clm_cluster_node):
- """ Create ClusterNode object from cluster node information
+ # Send the node info to user's callback function
+ self.node_get_function(invocation, cluster_node, error)
- Args:
- clm_cluster_node (SaClmClusterNodeT): Cluster node information
+ def initialize(self, track_func=None, node_get_func=None):
+ """ Initialize the CLM agent library
- Returns:
- ClusterNode: An object containing cluster node information
- """
- return ClusterNode(
- node_id=clm_cluster_node.nodeId,
- node_address=clm_cluster_node.nodeAddress,
- node_name=clm_cluster_node.nodeName,
- execution_environment=clm_cluster_node.executionEnvironment,
- member=clm_cluster_node.member,
- boot_timestamp=clm_cluster_node.bootTimestamp,
- initial_view_number=clm_cluster_node.initialViewNumber)
+ Args:
+ track_func (callback): Cluster track callback function
+ node_get_func (callback): Cluster node get function
+
+ Returns:
+ SaAisErrorT: Return code of the saClmInitialize_4() API call
+ """
+ self.track_function = track_func
+ self.node_get_function = node_get_func
+
+ self.callbacks = saClm.SaClmCallbacksT_4()
+ self.callbacks.saClmClusterTrackCallback = \
+ saClm.SaClmClusterTrackCallbackT_4(self.track_callback)
+ self.callbacks.saClmClusterNodeGetCallback = \
+ saClm.SaClmClusterNodeGetCallbackT_4(
+ self.node_get_callback)
+
+ self.handle = saClm.SaClmHandleT()
+ self.version = deepcopy(self.init_version)
+ rc = saClmInitialize_4(self.handle, self.callbacks, self.version)
+ if rc != eSaAisErrorT.SA_AIS_OK:
+ log_err("saClmInitialize_4 FAILED - %s" % eSaAisErrorT.whatis(rc))
+
+ return rc
+
+ def get_handle(self):
+ """ Return the CLM agent handle successfully initialized
+
+ Returns:
+ SaClmHandleT: CLM agent handle
+ """
+ return self.handle
+
+ def _fetch_sel_obj(self):
+ """ Obtain a selection object (OS file descriptor)
+
+ Returns:
+ SaAisErrorT: Return code of the saClmSelectionObjectGet() API call
+ """
+ rc = saClmSelectionObjectGet(self.handle, self.sel_obj)
+ if rc != eSaAisErrorT.SA_AIS_OK:
+ log_err("saClmSelectionObjectGet FAILED - %s" %
+ eSaAisErrorT.whatis(rc))
+
+ return rc
+
+ def get_selection_object(self):
+ """ Return the selection object associating with the CLM handle
+
+ Returns:
+ SaSelectionObjectT: Selection object associated with the CLM handle
+ """
+ return self.sel_obj
+
+ def dispatch(self, flags):
+ """ Invoke CLM callbacks for queued events
+
+ Args:
+ flags (eSaDispatchFlagsT): Flags specifying dispatch mode
+
+ Returns:
+ SaAisErrorT: Return code of the saClmDispatch() API call
+ """
+ rc = saClmDispatch(self.handle, flags)
+ if rc != eSaAisErrorT.SA_AIS_OK:
+ log_err("saClmDispatch FAILED - %s" % eSaAisErrorT.whatis(rc))
+
+ return rc
+
+ def finalize(self):
+ """ Finalize the CLM agent handle
+
+ Returns:
+ SaAisErrorT: Return code of the saClmFinalize() API call
+ """
+ rc = eSaAisErrorT.SA_AIS_OK
+ if self.handle:
+ rc = saClmFinalize(self.handle)
+ if rc != eSaAisErrorT.SA_AIS_OK:
+ log_err("saClmFinalize FAILED - %s" % eSaAisErrorT.whatis(rc))
+ elif rc == eSaAisErrorT.SA_AIS_OK \
+ or rc == eSaAisErrorT.SA_AIS_ERR_BAD_HANDLE:
+ # If the Finalize() call returned BAD_HANDLE, the handle should
+ # already become stale and invalid, so we reset it anyway.
+ self.handle = None
+
+ return rc
+
+
+class ClmAgent(ClmAgentManager):
+ """ This class acts as a high-level CLM agent, providing CLM functions to
+ the users at a higher level, and relieving the users of the need to manage
+ the life cycle of the CLM agent """
+
+ def __init__(self, version=None):
+ """ Constructor for ClmAgent class
+
+ Args:
+ version (SaVersionT): CLM API version
+ """
+ self.init_version = version if version else SaVersionT('B', 4, 1)
+ super(ClmAgent, self).__init__(self.init_version)
+
+ def __enter__(self):
+ """ Enter method for ClmAgent class
+
+ Finalize the CLM agent handle
+ """
+ return self
+
+ def __exit__(self):
+ """ Exit method for ClmAgent class
+
+ Finalize the CLM agent handle
+ """
+ if self.handle:
+ saClmFinalize(self.handle)
+
+ def __del__(self):
+ """ Destructor for ClmAgent class
+
+ Finalize the CLM agent handle
+ """
+ if self.handle:
+ saClmFinalize(self.handle)
+
+ @bad_handle_retry
+ def _re_init(self):
+ """ Internal function to re-initialize the CLM agent in case of getting
+ BAD_HANDLE during an operation
+
+ Returns:
+ SaAisErrorT: Return code of the corresponding CLM API call(s)
+ """
+ self.finalize()
+ self.handle = saClm.SaClmHandleT()
+ self.version = deepcopy(self.init_version)
+ rc = saClmInitialize_4(self.handle, self.callbacks, self.version)
+ if rc != eSaAisErrorT.SA_AIS_OK:
+ log_err("saClmInitialize_4 FAILED - %s" % eSaAisErrorT.whatis(rc))
+ else:
+ rc = self._fetch_sel_obj()
+
+ return rc
+
+ def init(self, track_func=None, node_get_func=None):
+ """ Initialize the CLM agent and fetch the selection object
+
+ Args:
+ track_func (callback): Cluster track callback function
+ node_get_func (callback): Cluster node get callback function
+
+ Returns:
+ SaAisErrorT: Return code of the corresponding CLM API call(s)
+ """
+ rc = self.initialize(track_func, node_get_func)
+ if rc == eSaAisErrorT.SA_AIS_OK:
+ rc = self._fetch_sel_obj()
+ if rc == eSaAisErrorT.SA_AIS_ERR_BAD_HANDLE:
+ rc = self._re_init()
+
+ return rc
+
+ @bad_handle_retry
+ def get_members(self):
+ """ Obtain information of each CLM cluster member node
+
+ Returns:
+ SaAisErrorT: Return code of the corresponding CLM API call(s)
+ list(ClusterNode): The list of ClusterNode structures containing
+ information of each CLM member node
+ """
+ cluster_nodes = []
+
+ notification_buffer = saClm.SaClmClusterNotificationBufferT_4()
+
+ rc = saClmClusterTrack_4(self.handle, saAis.SA_TRACK_CURRENT,
+ notification_buffer)
+ if rc == eSaAisErrorT.SA_AIS_OK:
+ for i in range(notification_buffer.numberOfItems):
+ notification = notification_buffer.notification[i]
+ clm_cluster_node = notification.clusterNode
+
+ cluster_node = create_cluster_node_instance(clm_cluster_node)
+
+ cluster_nodes.append(cluster_node)
+ else:
+ log_err("saClmClusterTrack_4 FAILED - %s" %
+ eSaAisErrorT.whatis(rc))
+
+ if rc == eSaAisErrorT.SA_AIS_ERR_BAD_HANDLE:
+ init_rc = self._re_init()
+ # If the re-initialization of agent handle succeeds, we still need
+ # to return BAD_HANDLE to the function decorator, so that it would
+ # re-try the failed operation. Otherwise, the true error code is
+ # returned to the user to decide further actions.
+ if init_rc != eSaAisErrorT.SA_AIS_OK:
+ rc = init_rc
+
+ return rc, cluster_nodes
+
+ @bad_handle_retry
+ def track_start(self, flags):
+ """ Start cluster membership tracking with the specified track flags
+
+ Args:
+ flags (SaUint8T): Type of cluster membership tracking
+
+ Returns:
+ SaAisErrorT: Return code of the corresponding CLM API call(s)
+ """
+ rc = saClmClusterTrack_4(self.handle, flags, None)
+ if rc != eSaAisErrorT.SA_AIS_OK:
+ log_err("saClmClusterTrack_4 FAILED - %s" %
+ eSaAisErrorT.whatis(rc))
+
+ if rc == eSaAisErrorT.SA_AIS_ERR_BAD_HANDLE:
+ init_rc = self._re_init()
+ # If the re-initialization of agent handle succeeds, we still need
+ # to return BAD_HANDLE to the function decorator, so that it would
+ # re-try the failed operation. Otherwise, the true error code is
+ # returned to the user to decide further actions.
+ if init_rc != eSaAisErrorT.SA_AIS_OK:
+ rc = init_rc
+
+ return rc
+
+ def track_stop(self):
+ """ Stop cluster membership tracking
+
+ Returns:
+ SaAisErrorT: Return code of the saClmClusterTrackStop() API call
+ """
+ rc = saClmClusterTrackStop(self.handle)
+ if rc != eSaAisErrorT.SA_AIS_OK:
+ log_err("saClmClusterTrack_4 FAILED - %s" %
+ eSaAisErrorT.whatis(rc))
+
+ if rc == eSaAisErrorT.SA_AIS_ERR_BAD_HANDLE:
+ init_rc = self._re_init()
+ if init_rc != eSaAisErrorT.SA_AIS_OK:
+ rc = init_rc
+ # No need to retry in case of BAD_HANDLE since the tracking should
+ # have already been stopped when the agent disconnected
+ return rc
+
+ def response(self, invocation, result):
+ """ Respond to CLM the result of execution of the requested callback
+
+ Args:
+ invocation (SaInvocationT): Invocation id associated with the
+ callback
+ result (SaAisErrorT): Result of callback execution
+
+ Returns:
+ SaAisErrorT: Return code of the saClmResponse_4() API call
+ """
+ rc = saClmResponse_4(self.handle, invocation, result)
+ if rc != eSaAisErrorT.SA_AIS_OK:
+ log_err("saClmResponse_4 FAILED - %s" % eSaAisErrorT.whatis(rc))
+
+ if rc == eSaAisErrorT.SA_AIS_ERR_BAD_HANDLE:
+ init_rc = self._re_init()
+ if init_rc != eSaAisErrorT.SA_AIS_OK:
+ rc = init_rc
+ # No need to retry in case of BAD_HANDLE since the corresponding
+ # callback response will no longer be valid with the new
+ # re-initialized handle
+
+ return rc
class ClusterNode(object):
""" Class representing a CLM cluster node """
+
def __init__(self, node_id, node_address, node_name, execution_environment,
member, boot_timestamp, initial_view_number):
""" Constructor for ClusterNode class
@@ -146,62 +444,181 @@ class ClusterNode(object):
self.initial_view_number = initial_view_number
-def initialize(track_func=None):
- """ Initialize the CLM library
+def create_cluster_node_instance(clm_cluster_node):
+ """ Create ClusterNode object from cluster node information
+
+ Args:
+ clm_cluster_node (SaClmClusterNodeT): Cluster node information
+
+ Returns:
+ ClusterNode: An object containing cluster node information
+ """
+ return ClusterNode(
+ node_id=clm_cluster_node.nodeId,
+ node_address=clm_cluster_node.nodeAddress,
+ node_name=clm_cluster_node.nodeName,
+ execution_environment=clm_cluster_node.executionEnvironment,
+ member=clm_cluster_node.member,
+ boot_timestamp=clm_cluster_node.bootTimestamp,
+ initial_view_number=clm_cluster_node.initialViewNumber)
+
+
+@deprecate
+def initialize(track_func=None, node_get_func=None, version=None):
+ """ Initialize the CLM agent library
Args:
track_func (callback): Cluster track callback function
+ node_get_func (callback): Cluster node get function
+ version (SaVersionT): Clm version being initialized
+
+ Raises:
+ SafException: If the return code of the corresponding CLM API call(s)
+ is not SA_AIS_OK
"""
- global track_function
- track_function = track_func
+ global _clm_agent
+ _clm_agent = ClmAgent(version)
- # Set up callbacks for cluster membership tracking
- callbacks = saClm.SaClmCallbacksT_4()
- callbacks.saClmClusterNodeGetCallback = \
- saClm.SaClmClusterNodeGetCallbackT_4(node_get_callback)
- callbacks.saClmClusterTrackCallback = \
- saClm.SaClmClusterTrackCallbackT_4(track_callback)
+ rc = _clm_agent.initialize(track_func, node_get_func)
+ if rc != eSaAisErrorT.SA_AIS_OK:
+ raise SafException(rc)
- # Define which version of the CLM API to use
- version = SaVersionT('B', 4, 1)
- # Initialize the CLM interface
- saClmInitialize_4(handle, callbacks, version)
+def get_handle():
+ """ Get this CLM agent handle
+ Returns:
+ SaClmHandleT: CLM agent handle if one was successfully initialized.
+ Otherwise, 'None' is returned.
+ """
+ if _clm_agent:
+ return _clm_agent.get_handle()
+
+ return None
+
+@deprecate
def track(flags=saAis.SA_TRACK_CHANGES_ONLY):
""" Start cluster membership tracking with specified flags
Args:
flags (SaUint8T): Type of cluster membership tracking
+
+ Raises:
+ SafException: If the return code of the corresponding CLM API call(s)
+ is not SA_AIS_OK
"""
- saClmClusterTrack_4(handle, flags, None)
+ if not _clm_agent:
+ # Return SA_AIS_ERR_INIT if user calls this function without first
+ # calling initialize()
+ return eSaAisErrorT.SA_AIS_ERR_INIT
+ rc = _clm_agent.track_start(flags)
+ if rc != eSaAisErrorT.SA_AIS_OK:
+ raise SafException(rc)
-def get_members():
- """ Get member notifications from notification buffer """
- notification_buffer = saClm.SaClmClusterNotificationBufferT_4()
- saClmClusterTrack_4(handle, saAis.SA_TRACK_CURRENT, notification_buffer)
+@deprecate
+def track_stop():
+ """ Stop cluster membership tracking
- cluster_nodes = []
+ Raises:
+ SafException: If the return code of the corresponding CLM API call(s)
+ is not SA_AIS_OK
+ """
+ if not _clm_agent:
+ # Return SA_AIS_ERR_INIT if user calls this function without first
+ # calling initialize()
+ raise SafException(eSaAisErrorT.SA_AIS_ERR_INIT)
- for i in range(notification_buffer.numberOfItems):
- notification = notification_buffer.notification[i]
- clm_cluster_node = notification.clusterNode
+ rc = _clm_agent.track_stop()
+ if rc != eSaAisErrorT.SA_AIS_OK:
+ raise SafException(rc)
- cluster_node = create_cluster_node_instance(clm_cluster_node)
- cluster_nodes.append(cluster_node)
+@deprecate
+def get_members():
+ """ Get member notifications from notification buffer
+
+ Returns:
+ ClusterNode: The node information
+
+ Raises:
+ SafException: If the return code of the corresponding CLM API call(s)
+ is not SA_AIS_OK
+ """
+ if not _clm_agent:
+ # SA_AIS_ERR_INIT is returned if user calls this function without first
+ # calling initialize()
+ rc = eSaAisErrorT.SA_AIS_ERR_INIT
+ else:
+ rc, cluster_nodes = _clm_agent.get_members()
+
+ if rc != eSaAisErrorT.SA_AIS_OK:
+ raise SafException(rc)
return cluster_nodes
+@deprecate
+def get_selection_object():
+ """ Get the selection object associated with this CLM agent
+
+ Returns:
+ SaSelectionObjectT: Selection object associated with the CLM handle
+
+ Raises:
+ SafException: If the return code of the corresponding CLM API call(s)
+ is not SA_AIS_OK
+ """
+ if not _clm_agent:
+ # SA_AIS_ERR_INIT is returned if user calls this function without first
+ # calling initialize()
+ raise SafException(eSaAisErrorT.SA_AIS_ERR_INIT)
+
+ return _clm_agent.get_selection_object()
+
+
+@deprecate
def dispatch(flags=eSaDispatchFlagsT.SA_DISPATCH_ALL):
- """ Invoke CLM callbacks for queued events. The default is to dispatch all
- available events.
+ """ Invoke CLM callbacks for queued events with the given dispatch flag.
+ If no dispatch flag is specified, the default is to dispatch all available
+ events.
Args:
flags (eSaDispatchFlagsT): Flags specifying dispatch mode
+
+ Raises:
+ SafException: If the return code of the corresponding CLM API call(s)
+ is not SA_AIS_OK
+ """
+ if not _clm_agent:
+ # SA_AIS_ERR_INIT is returned if user calls this function without first
+ # calling initialize()
+ rc = eSaAisErrorT.SA_AIS_ERR_INIT
+ else:
+ rc = _clm_agent.dispatch(flags)
+ if rc != eSaAisErrorT.SA_AIS_OK:
+ raise SafException(rc)
+
+
+@deprecate
+def response(invocation, result):
+ """ Respond to CLM the result of execution of the requested callback
+
+ Args:
+ invocation (SaInvocationT): Invocation id associated with the callback
+ result (SaAisErrorT): Result of callback execution
+
+ Raises:
+ SafException: If the return code of the corresponding CLM API call(s)
+ is not SA_AIS_OK
"""
- saClmDispatch(handle, flags)
+ if not _clm_agent:
+ # SA_AIS_ERR_INIT is returned if user calls this function without first
+ # calling initialize()
+ rc = eSaAisErrorT.SA_AIS_ERR_INIT
+ else:
+ rc = _clm_agent.response(invocation, result)
+ if rc != eSaAisErrorT.SA_AIS_OK:
+ raise SafException(rc)
--
2.11.0
------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
Opensaf-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/opensaf-devel