- Add more error handling - Refactor clm code - Keep raising exceptions for existing python methods --- python/pyosaf/utils/__init__.py | 267 ++++++++++++++--- python/pyosaf/utils/clm/__init__.py | 579 ++++++++++++++++++++++++++++++------ 2 files changed, 716 insertions(+), 130 deletions(-)
diff --git a/python/pyosaf/utils/__init__.py b/python/pyosaf/utils/__init__.py index 7a1c21b79..fac3c5b80 100644 --- a/python/pyosaf/utils/__init__.py +++ b/python/pyosaf/utils/__init__.py @@ -16,17 +16,29 @@ # ############################################################################ """ 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 -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 +56,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: + for c_error_string in c_error_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 - while error == eSaAisErrorT.SA_AIS_ERR_TRY_AGAIN: - if one_sec_sleeps == TRY_AGAIN_COUNT: + rc = func(*args) + 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 resource_abort: + 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 + if sleep_time_interval > 0: + 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 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 = deepcopy(backup_version) - args = args[:2] + (version,) - 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 + else: + break # Break out of the retry loop + + # Check sleep_time_interval to sleep and retry the function + if sleep_time_interval > 0: + 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 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,) + 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..fa9460182 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,372 @@ 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 __exit__(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 +429,182 @@ 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) + + rc = _clm_agent.initialize(track_func, node_get_func) + if rc != eSaAisErrorT.SA_AIS_OK: + raise SafException(rc) - # 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) - # Define which version of the CLM API to use - version = SaVersionT('B', 4, 1) +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() - # Initialize the CLM interface - saClmInitialize_4(handle, callbacks, version) + 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 + """ + 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) + + +@deprecate +def track_stop(): + """ Stop 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() + raise SafException(eSaAisErrorT.SA_AIS_ERR_INIT) + rc = _clm_agent.track_stop() + if rc != eSaAisErrorT.SA_AIS_OK: + raise SafException(rc) + +@deprecate def get_members(): - """ Get member notifications from notification buffer """ - notification_buffer = saClm.SaClmClusterNotificationBufferT_4() + """ Get member notifications from notification buffer - saClmClusterTrack_4(handle, saAis.SA_TRACK_CURRENT, notification_buffer) + Returns: + SaAisErrorT: Return code of the corresponding CLM API call(s) + ClusterNode: The node information + Raises: + SafException: If the return code of the corresponding CLM API call(s) + is not SA_AIS_OK + """ cluster_nodes = [] + 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) + else: + rc, cluster_nodes = _clm_agent.get_members() + + if rc != eSaAisErrorT.SA_AIS_OK: + raise SafException(rc) - for i in range(notification_buffer.numberOfItems): - notification = notification_buffer.notification[i] - clm_cluster_node = notification.clusterNode + return rc, cluster_nodes - cluster_node = create_cluster_node_instance(clm_cluster_node) - cluster_nodes.append(cluster_node) +@deprecate +def get_selection_object(): + """ Get the selection object associated with this CLM agent + + 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 cluster_nodes + rc = _clm_agent.get_selection_object() + if rc != eSaAisErrorT.SA_AIS_OK: + raise SafException(rc) +@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() + raise SafException(eSaAisErrorT.SA_AIS_ERR_INIT) + + 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() + raise SafException(eSaAisErrorT.SA_AIS_ERR_INIT) + + 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 Opensaf-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/opensaf-devel