Improve the implementation of NTF pyosaf utils --- python/Makefile.am | 6 +- python/pyosaf/saNtf.py | 4 +- python/pyosaf/utils/ntf/__init__.py | 855 ++++++++++--------------------- python/pyosaf/utils/ntf/agent.py | 500 ++++++++++++++++++ python/pyosaf/utils/ntf/producer.py | 711 ++++++++++++++++++++++++++ python/pyosaf/utils/ntf/reader.py | 187 +++++++ python/pyosaf/utils/ntf/subscriber.py | 923 ++++++++++++++++++++++++++++++++++ 7 files changed, 2599 insertions(+), 587 deletions(-) create mode 100644 python/pyosaf/utils/ntf/agent.py create mode 100644 python/pyosaf/utils/ntf/producer.py create mode 100644 python/pyosaf/utils/ntf/reader.py create mode 100644 python/pyosaf/utils/ntf/subscriber.py
diff --git a/python/Makefile.am b/python/Makefile.am index 390366a..0b79067 100644 --- a/python/Makefile.am +++ b/python/Makefile.am @@ -62,6 +62,10 @@ pkgpyosafutilsimmoi_PYTHON = \ python/pyosaf/utils/immoi/implementer.py pkgpyosafutilsntf_PYTHON = \ - python/pyosaf/utils/ntf/__init__.py + python/pyosaf/utils/ntf/__init__.py \ + python/pyosaf/utils/ntf/agent.py \ + python/pyosaf/utils/ntf/producer.py \ + python/pyosaf/utils/ntf/subscriber.py \ + python/pyosaf/utils/ntf/reader.py endif diff --git a/python/pyosaf/saNtf.py b/python/pyosaf/saNtf.py index 7438cdc..9ee096e 100644 --- a/python/pyosaf/saNtf.py +++ b/python/pyosaf/saNtf.py @@ -15,7 +15,7 @@ # ############################################################################ -from ctypes import POINTER, CFUNCTYPE, Structure, Union, CDLL, c_char +from ctypes import POINTER, CFUNCTYPE, Structure, Union, CDLL, c_char, cast from pyosaf.saAis import SaUint64T, SaEnumT, Enumeration, Const, BYREF, \ SaUint32T, SaUint16T, SaBoolT, SaStringT, SaNameT, SaTimeT, SaDoubleT, \ SaInt64T, SaUint8T, SaInt8T, SaInt16T, SaInt32T, SaFloatT, SaAnyT @@ -655,7 +655,7 @@ class SaNtfNotificationTypeFilterHandlesT_3(Structure): ('securityAlarmFilterHandle', SaNtfNotificationFilterHandleT), ('miscellaneousFilterHandle', SaNtfNotificationFilterHandleT)] -saNtf.SA_NTF_FILTER_HANDLE_NULL = None +saNtf.SA_NTF_FILTER_HANDLE_NULL = 0 #if defined(SA_NTF_A01) || defined(SA_NTF_A02) class _notification(Union): diff --git a/python/pyosaf/utils/ntf/__init__.py b/python/pyosaf/utils/ntf/__init__.py index a76867e..0687517 100644 --- a/python/pyosaf/utils/ntf/__init__.py +++ b/python/pyosaf/utils/ntf/__init__.py @@ -15,259 +15,49 @@ # Author(s): Ericsson # ############################################################################ -# pylint: disable=unused-argument,too-many-arguments +# pylint: disable=too-many-arguments """ NTF common utilities """ -import ctypes - -from pyosaf.saAis import saAis, SaVersionT, SaSelectionObjectT, eSaBoolT, \ - eSaDispatchFlagsT +from pyosaf.saAis import saAis, eSaAisErrorT, SaVersionT, eSaDispatchFlagsT from pyosaf import saNtf -from pyosaf.utils import decorate, initialize_decorate - - -# Decorate pure saNtf* API's with error-handling retry and exception raising -saNtfInitialize = initialize_decorate(saNtf.saNtfInitialize) -saNtfLocalizedMessageFree = decorate(saNtf.saNtfLocalizedMessageFree) -saNtfStateChangeNotificationFilterAllocate = \ - decorate(saNtf.saNtfStateChangeNotificationFilterAllocate) -saNtfNotificationUnsubscribe = decorate(saNtf.saNtfNotificationUnsubscribe) -saNtfNotificationReadInitialize = \ - decorate(saNtf.saNtfNotificationReadInitialize) -saNtfInitialize_2 = initialize_decorate(saNtf.saNtfInitialize_2) -saNtfNotificationReadInitialize_2 = \ - decorate(saNtf.saNtfNotificationReadInitialize_2) -saNtfNotificationSubscribe = decorate(saNtf.saNtfNotificationSubscribe) -saNtfInitialize_3 = initialize_decorate(saNtf.saNtfInitialize_3) -saNtfSelectionObjectGet = decorate(saNtf.saNtfSelectionObjectGet) -saNtfDispatch = decorate(saNtf.saNtfDispatch) -saNtfFinalize = decorate(saNtf.saNtfFinalize) -saNtfObjectCreateDeleteNotificationAllocate = \ - decorate(saNtf.saNtfObjectCreateDeleteNotificationAllocate) -saNtfAttributeChangeNotificationAllocate = \ - decorate(saNtf.saNtfAttributeChangeNotificationAllocate) -saNtfStateChangeNotificationAllocate = \ - decorate(saNtf.saNtfStateChangeNotificationAllocate) -saNtfStateChangeNotificationAllocate_3 = \ - decorate(saNtf.saNtfStateChangeNotificationAllocate_3) -saNtfAlarmNotificationAllocate = decorate(saNtf.saNtfAlarmNotificationAllocate) -saNtfSecurityAlarmNotificationAllocate = \ - decorate(saNtf.saNtfSecurityAlarmNotificationAllocate) -saNtfMiscellaneousNotificationAllocate = \ - decorate(saNtf.saNtfMiscellaneousNotificationAllocate) -saNtfPtrValAllocate = decorate(saNtf.saNtfPtrValAllocate) -saNtfArrayValAllocate = decorate(saNtf.saNtfArrayValAllocate) -saNtfIdentifierAllocate = decorate(saNtf.saNtfIdentifierAllocate) -saNtfNotificationSend = decorate(saNtf.saNtfNotificationSend) -saNtfNotificationSendWithId = decorate(saNtf.saNtfNotificationSendWithId) -saNtfNotificationFree = decorate(saNtf.saNtfNotificationFree) -saNtfVariableDataSizeGet = decorate(saNtf.saNtfVariableDataSizeGet) -saNtfLocalizedMessageGet = decorate(saNtf.saNtfLocalizedMessageGet) -saNtfLocalizedMessageFree_2 = decorate(saNtf.saNtfLocalizedMessageFree_2) -saNtfPtrValGet = decorate(saNtf.saNtfPtrValGet) -saNtfArrayValGet = decorate(saNtf.saNtfArrayValGet) -saNtfObjectCreateDeleteNotificationFilterAllocate = \ - decorate(saNtf.saNtfObjectCreateDeleteNotificationFilterAllocate) -saNtfAttributeChangeNotificationFilterAllocate = \ - decorate(saNtf.saNtfAttributeChangeNotificationFilterAllocate) -saNtfStateChangeNotificationFilterAllocate_2 = \ - decorate(saNtf.saNtfStateChangeNotificationFilterAllocate_2) -saNtfAlarmNotificationFilterAllocate = \ - decorate(saNtf.saNtfAlarmNotificationFilterAllocate) -saNtfSecurityAlarmNotificationFilterAllocate = \ - decorate(saNtf.saNtfSecurityAlarmNotificationFilterAllocate) -saNtfNotificationFilterFree = decorate(saNtf.saNtfNotificationFilterFree) -saNtfNotificationSubscribe_3 = decorate(saNtf.saNtfNotificationSubscribe_3) -saNtfNotificationReadInitialize_3 = \ - decorate(saNtf.saNtfNotificationReadInitialize_3) -saNtfNotificationUnsubscribe_2 = decorate(saNtf.saNtfNotificationUnsubscribe_2) -saNtfNotificationReadNext = decorate(saNtf.saNtfNotificationReadNext) -saNtfNotificationReadNext_3 = decorate(saNtf.saNtfNotificationReadNext_3) -saNtfNotificationReadFinalize = decorate(saNtf.saNtfNotificationReadFinalize) - - -handle = saNtf.SaNtfHandleT() -selection_object = SaSelectionObjectT() -callbacks = saNtf.SaNtfCallbacksT() - - -class AdditionalInfo(object): - """ Represent a piece of additional info to be included in a notification - """ - def __init__(self, info_id, info_type, info_value): - self.info_id = info_id - self.info_type = info_type - self.info_value = info_value - - -class StateChange(object): - """ Contain information about a state change event """ - def __init__(self): - pass - - -class AttributeChange(object): - """ Contain information about a change in an attribute """ - def __init__(self): - pass - - -class Attribute(object): - """ Contain information about the value and value type of an attribute """ - def __init__(self): - pass - - -class SecurityAlarmDetector(object): - """ Represent an instance of a security alarm detector """ - def __init__(self, value=None, value_type=None): - self.value = value - self.value_type = value_type +from pyosaf.utils import deprecate, SafException +from pyosaf.utils.ntf import agent as ntf +from pyosaf.utils.ntf.producer import NtfProducer +from pyosaf.utils.ntf.subscriber import NtfSubscriber - -class ServiceUser(object): - """ Represent a service user """ - def __init__(self, value=None, value_type=None): - self.value = value - self.value_type = value_type - - -class ServiceProvider(object): - """ Represent a service provider """ - def __init__(self, value=None, value_type=None): - self.value = value - self.value_type = value_type - - -def dummy_func(*args): - """ Dummy function used as a callback when no proper callbacks are set """ - pass +_ntf_producer = None +_ntf_subscriber = None +@deprecate def initialize(notification_callback=None): """ Initialize the NTF library Args: notification_callback (SaNtfNotificationCallbackT): Callback to be invoked by NTF server to deliver a notification to the subscriber - """ - # Assign default values for callbacks - callbacks.saNtfNotificationCallback = \ - saNtf.SaNtfNotificationCallbackT(dummy_func) - callbacks.saNtfNotificationDiscardedCallback = \ - saNtf.SaNtfNotificationDiscardedCallbackT(dummy_func) - - # Override the default notification subscribe callback if one is provided - if notification_callback: - callbacks.saNtfNotificationCallback = \ - saNtf.SaNtfNotificationCallbackT(notification_callback) - - # Define which version of the NTF API to use - version = SaVersionT('A', 1, 1) - - # Initialize the NTF interface - saNtfInitialize(handle, callbacks, version) - - # Get the selection object - saNtfSelectionObjectGet(handle, selection_object) - - -def assign_ntf_value_to_attribute(attr_value_field, value, value_type): - """ Assign the correct sub-field in the given attribute - - Args: - attr_value_field (SaNtfValueT): Object attribute value in an object - creation or deletion notification - value (variable-size C data type): Actual value of the object attribute - value_type (SaNtfValueTypeT): Type of the object attribute value + Raises: + SafException: If any NTF API call did not return SA_AIS_OK """ - if value_type == saNtf.eSaNtfValueTypeT.SA_NTF_VALUE_UINT8: - attr_value_field.uint8Val = value - - elif value_type == saNtf.eSaNtfValueTypeT.SA_NTF_VALUE_INT8: - attr_value_field.int8Val = value - - elif value_type == saNtf.eSaNtfValueTypeT.SA_NTF_VALUE_UINT16: - attr_value_field.uint16Val = value - - elif value_type == saNtf.eSaNtfValueTypeT.SA_NTF_VALUE_INT16: - attr_value_field.int16Val = value - - elif value_type == saNtf.eSaNtfValueTypeT.SA_NTF_VALUE_UINT32: - attr_value_field.uint32Val = value - - elif value_type == saNtf.eSaNtfValueTypeT.SA_NTF_VALUE_INT32: - attr_value_field.int32Val = value - - elif value_type == saNtf.eSaNtfValueTypeT.SA_NTF_VALUE_FLOAT: - attr_value_field.floatVal = value + # Set the NTF API version to initialize + version = SaVersionT('A', 1, 1) - elif value_type == saNtf.eSaNtfValueTypeT.SA_NTF_VALUE_UINT64: - attr_value_field.uint64Val = value + global _ntf_producer + _ntf_producer = NtfProducer(version) - elif value_type == saNtf.eSaNtfValueTypeT.SA_NTF_VALUE_INT64: - attr_value_field.int64Val = value + global _ntf_subscriber + _ntf_subscriber = NtfSubscriber(version) - elif value_type == saNtf.eSaNtfValueTypeT.SA_NTF_VALUE_DOUBLE: - attr_value_field.doubleVal = value + rc = _ntf_producer.init() + if rc != eSaAisErrorT.SA_AIS_OK: + raise SafException(rc) - -def fill_in_header(notification_handle, - header, notification_object, notifying_object, vendor_id, - major_id, minor_id, additional_text, event_type, event_time, - additional_info): - """ Fill in the given notification header with the provided information - - Args: - notification_handle (SaNtfNotificationHandleT): Notification handle - header (SaNtfNotificationHeaderT): Notification header - notification_object (str): Notification object's dn - notifying_object (str): Notifying object's dn - vendor_id (SaUint32T): Vendor id - major_id (SaUint16T): Major id - minor_id (SaUint16T): Minor id - additional_text (str): Additional text - event_type (SaNtfEventTypeT): Event type - event_time (SaTimeT): Event time - additional_info (list): List of AdditionalInfo class instances - """ - header.eventType.contents.value = event_type - header.notificationObject.contents.value = notification_object - header.notificationObject.contents.length = len(notification_object) - header.notifyingObject.contents.value = notifying_object - header.notifyingObject.contents.length = len(notifying_object) - header.notificationClassId.contents.vendorId = vendor_id - header.notificationClassId.contents.majorId = major_id - header.notificationClassId.contents.minorId = minor_id - header.eventTime.contents.value = event_time - header.numCorrelatedNotifications = 0 - header.lengthAdditionalText = len(additional_text) - - ctypes.memmove(header.additionalText, - additional_text, - len(additional_text)) - - header.numAdditionalInfo = len(additional_info) - header.thresholdInformation = None - - # Fill in additional info - if additional_info: - for i, add_info in enumerate(additional_info): - header.additionalInfo[i].infoId = add_info.info_id - header.additionalInfo[i].infoType = add_info.info_type - - dest_ptr = (ctypes.c_char * len(add_info.info_value))() - - saNtf.saNtfPtrValAllocate(notification_handle, - len(add_info.info_value) + 1, - dest_ptr, - header.additionalInfo[i].infoValue) - ctypes.memmove(ctypes.addressof(dest_ptr), add_info.info_value, - len(add_info.info_value) + 1) - else: - header.additionalInfo = None + rc = _ntf_subscriber.init(notification_callback) + if rc != eSaAisErrorT.SA_AIS_OK: + raise SafException(rc) +@deprecate def send_object_create_notification(vendor_id, major_id, minor_id, additional_text="", notification_object="", @@ -284,27 +74,42 @@ def send_object_create_notification(vendor_id, major_id, minor_id, additional_text (str): Additional text notification_object (str): Notification object's dn notifying_object (str): Notifying object's dn - attributes (list): List of Attribute class instances + attributes (list(Attribute)): List of Attribute structures event_time (SaTimeT): Event time - additional_info (list): List of AdditionalInfo class instances + additional_info (list(AdditionalInfo)): List of AdditionalInfo + structures + + Returns: + SaAisErrorT: Return code of the corresponding NTF API call(s) + + Raises: + SafException: If any NTF API call did not return SA_AIS_OK """ - if attributes is None: - attributes = [] + ntf_class_id = saNtf.SaNtfClassIdT(vendor_id, major_id, minor_id) + if _ntf_producer is None: + # SA_AIS_ERR_INIT is returned if user calls this function without first + # calling initialize() + return eSaAisErrorT.SA_AIS_ERR_INIT + + _ntf_producer.set_event_type(saNtf.eSaNtfEventTypeT.SA_NTF_OBJECT_CREATION) + _ntf_producer.set_class_id(ntf_class_id) + _ntf_producer.set_additional_text(additional_text) + _ntf_producer.set_notification_object(notification_object) + _ntf_producer.set_notifying_object(notifying_object) + if attributes: + _ntf_producer.set_object_attributes(attributes) + _ntf_producer.set_event_time(event_time) + if additional_info: + _ntf_producer.set_additional_info(additional_info) - if additional_info is None: - additional_info = [] + rc = _ntf_producer.send_object_create_delete_notification() + if rc != eSaAisErrorT.SA_AIS_OK: + raise SafException(rc) - _send_object_create_delete_notification( - saNtf.eSaNtfEventTypeT.SA_NTF_OBJECT_CREATION, - vendor_id, major_id, minor_id, - additional_text=additional_text, - notification_object=notification_object, - notifying_object=notifying_object, - attributes=attributes, - event_time=event_time, - additional_info=additional_info) + return rc +@deprecate def send_object_delete_notification(vendor_id, major_id, minor_id, additional_text="", notification_object="", @@ -321,88 +126,95 @@ def send_object_delete_notification(vendor_id, major_id, minor_id, additional_text (str): Additional text notification_object (str): Notification object's dn notifying_object (str): Notifying object's dn - attributes (list): List of Attribute class instances + attributes (list(Attribute)): List of Attribute structures event_time (SaTimeT): Event time - additional_info (list): List of AdditionalInfo class instances + additional_info (list(AdditionalInfo)): List of AdditionalInfo + structures + + Returns: + SaAisErrorT: Return code of the corresponding NTF API call(s) + + Raises: + SafException: If any NTF API call did not return SA_AIS_OK """ - if attributes is None: - attributes = [] - - if additional_info is None: - additional_info = [] - - _send_object_create_delete_notification( - saNtf.eSaNtfEventTypeT.SA_NTF_OBJECT_DELETION, - vendor_id, major_id, minor_id, - additional_text=additional_text, - notification_object=notification_object, - notifying_object=notifying_object, - attributes=attributes, - event_time=event_time, - additional_info=additional_info) - - -def _send_object_create_delete_notification(event_type, vendor_id, major_id, - minor_id, additional_text="", - notification_object="", - notifying_object="", - attributes=None, - event_time=saAis.SA_TIME_UNKNOWN, - additional_info=None): - """ Common function to send notification about an object creation/deletion + ntf_class_id = saNtf.SaNtfClassIdT(vendor_id, major_id, minor_id) + if _ntf_producer is None: + # SA_AIS_ERR_INIT is returned if user calls this function without first + # calling initialize() + return eSaAisErrorT.SA_AIS_ERR_INIT + + _ntf_producer.set_event_type(saNtf.eSaNtfEventTypeT.SA_NTF_OBJECT_DELETION) + _ntf_producer.set_class_id(ntf_class_id) + _ntf_producer.set_additional_text(additional_text) + _ntf_producer.set_notification_object(notification_object) + _ntf_producer.set_notifying_object(notifying_object) + if attributes: + _ntf_producer.set_object_attributes(attributes) + _ntf_producer.set_event_time(event_time) + if additional_info: + _ntf_producer.set_additional_info(additional_info) + + rc = _ntf_producer.send_object_create_delete_notification() + if rc != eSaAisErrorT.SA_AIS_OK: + raise SafException(rc) + + return rc + + +@deprecate +def send_attribute_change_notification( + vendor_id, major_id, minor_id, additional_text="", + notification_object="", notifying_object="", + event_type=saNtf.eSaNtfEventTypeT.SA_NTF_ATTRIBUTE_ADDED, + event_time=saAis.SA_TIME_UNKNOWN, additional_info=None, + changed_attributes=None): + """ Send notification about an attribute change event Args: - event_type (SaNtfEventTypeT): Event type vendor_id (SaUint32T): Vendor id major_id (SaUint16T): Major id minor_id (SaUint16T): Minor id additional_text (str): Additional text notification_object (str): Notification object's dn notifying_object (str): Notifying object's dn - attributes (list): List of Attribute class instances + event_type (SaNtfEventTypeT): Event type event_time (SaTimeT): Event time - additional_info (list): List of AdditionalInfo class instances - """ - - if attributes is None: - attributes = [] - - if additional_info is None: - additional_info = [] - - # Create the notification - notification = saNtf.SaNtfObjectCreateDeleteNotificationT() + additional_info (list(AdditionalInfo)): List of AdditionalInfo + structures + changed_attributes (list(AttributeChange)): List of AttributeChange + structures - saNtfObjectCreateDeleteNotificationAllocate(handle, notification, 0, - len(additional_text) + 1, - len(additional_info), - len(attributes), 0) + Returns: + SaAisErrorT: Return code of the corresponding NTF API call(s) - # Fill in the header - fill_in_header(notification.notificationHandle, - notification.notificationHeader, - notification_object, notifying_object, vendor_id, - major_id, minor_id, additional_text, - event_type, event_time, additional_info) - - # Fill in attributes - for i in range(notification.numAttributes): - ptr = notification.objectAttributes[i] - - ptr.attributeId = attributes[i].attribute_id - ptr.attributeType = attributes[i].attribute_type - - assign_ntf_value_to_attribute(ptr.attributeValue, - attributes[i].attribute_value, - attributes[i].attribute_type) + Raises: + SafException: If any NTF API call did not return SA_AIS_OK + """ + ntf_class_id = saNtf.SaNtfClassIdT(vendor_id, major_id, minor_id) + if _ntf_producer is None: + # SA_AIS_ERR_INIT is returned if user calls this function without first + # calling initialize() + return eSaAisErrorT.SA_AIS_ERR_INIT + + _ntf_producer.set_event_type(event_type) + _ntf_producer.set_class_id(ntf_class_id) + _ntf_producer.set_additional_text(additional_text) + _ntf_producer.set_notification_object(notification_object) + _ntf_producer.set_notifying_object(notifying_object) + _ntf_producer.set_event_time(event_time) + if additional_info: + _ntf_producer.set_additional_info(additional_info) + if changed_attributes: + _ntf_producer.set_changed_attributes(changed_attributes) - # Send the notification - saNtfNotificationSend(notification.notificationHandle) + rc = _ntf_producer.send_attribute_change_notification() + if rc != eSaAisErrorT.SA_AIS_OK: + raise SafException(rc) - # Free the notification - saNtfNotificationFree(notification.notificationHandle) + return rc +@deprecate def send_state_change_notification(vendor_id, major_id, minor_id, @@ -422,119 +234,92 @@ def send_state_change_notification(vendor_id, notification_object (str): Notification object's dn notifying_object (str): Notifying object's dn event_time (SaTimeT): Event time - additional_info (list): List of AdditionalInfo class instances - state_changes (list): List of StateChange class instances - """ - if additional_info is None: - additional_info = [] - - if state_changes is None: - state_changes = [] - - event_type = saNtf.eSaNtfEventTypeT.SA_NTF_OBJECT_STATE_CHANGE - - # Create the notification - notification = saNtf.SaNtfStateChangeNotificationT() + additional_info (list(AdditionalInfo)): List of AdditionalInfo + structures + state_changes (list(StateChange)): List of StateChange structures - saNtfStateChangeNotificationAllocate(handle, notification, 0, - len(additional_text) + 1, - len(additional_info), - len(state_changes), 0) + Returns: + SaAisErrorT: Return code of the corresponding NTF API call(s) - # Fill in the header - fill_in_header(notification.notificationHandle, - notification.notificationHeader, - notification_object, notifying_object, vendor_id, - major_id, minor_id, additional_text, event_type, - event_time, additional_info) - - # Fill in state changes - for i, state_change in enumerate(state_changes): - notification.changedStates[i].stateId = state_change.state_id - if state_change.old_state_present: - notification.changedStates[i].oldStatePresent = eSaBoolT.SA_TRUE - notification.changedStates[i].oldState = state_change.old_state - else: - notification.changedStates[i].oldStatePresent = eSaBoolT.SA_FALSE - - notification.changedStates[i].newState = state_change.new_state + Raises: + SafException: If any NTF API call did not return SA_AIS_OK + """ + ntf_class_id = saNtf.SaNtfClassIdT(vendor_id, major_id, minor_id) + if _ntf_producer is None: + # SA_AIS_ERR_INIT is returned if user calls this function without first + # calling initialize() + return eSaAisErrorT.SA_AIS_ERR_INIT + + _ntf_producer.set_event_type( + saNtf.eSaNtfEventTypeT.SA_NTF_OBJECT_STATE_CHANGE) + _ntf_producer.set_class_id(ntf_class_id) + _ntf_producer.set_additional_text(additional_text) + _ntf_producer.set_notification_object(notification_object) + _ntf_producer.set_notifying_object(notifying_object) + _ntf_producer.set_event_time(event_time) + if additional_info: + _ntf_producer.set_additional_info(additional_info) + if state_changes: + _ntf_producer.set_state_changes(state_changes) - # Send the notification - saNtfNotificationSend(notification.notificationHandle) + rc = _ntf_producer.send_state_change_notification() + if rc != eSaAisErrorT.SA_AIS_OK: + raise SafException(rc) - # Free the notification - saNtfNotificationFree(notification.notificationHandle) + return rc -def send_attribute_change_notification( - vendor_id, major_id, minor_id, additional_text="", +@deprecate +def send_alarm_notification( + vendor_id, major_id, minor_id, perceived_severity, additional_text="", notification_object="", notifying_object="", - event_type=saNtf.eSaNtfEventTypeT.SA_NTF_ATTRIBUTE_ADDED, - event_time=saAis.SA_TIME_UNKNOWN, additional_info=None, - changed_attributes=None): - """ Send notification about an attribute change event + event_type=saNtf.eSaNtfEventTypeT.SA_NTF_ALARM_PROCESSING, + event_time=saAis.SA_TIME_UNKNOWN, additional_info=None): + """ Send an alarm notification Args: vendor_id (SaUint32T): Vendor id major_id (SaUint16T): Major id minor_id (SaUint16T): Minor id + perceived_severity (SaNtfSeverityT): Perceived severity additional_text (str): Additional text notification_object (str): Notification object's dn notifying_object (str): Notifying object's dn event_type (SaNtfEventTypeT): Event type event_time (SaTimeT): Event time - additional_info (list): List of AdditionalInfo class instances - changed_attributes (list): List of AttributeChange class instances - """ - if additional_info is None: - additional_info = [] - - if changed_attributes is None: - changed_attributes = [] - - # Create the notification - notification = saNtf.SaNtfAttributeChangeNotificationT() - - saNtfAttributeChangeNotificationAllocate(handle, notification, 0, - len(additional_text) + 1, - len(additional_info), - len(changed_attributes), 0) - - # Fill in the header - fill_in_header(notification.notificationHandle, - notification.notificationHeader, - notification_object, notifying_object, vendor_id, - major_id, minor_id, additional_text, event_type, - event_time, additional_info) - - # Fill in attributes - for i, changed_attribute in enumerate(changed_attributes): - ptr = notification.changedAttributes[i] + additional_info (list(AdditionalInfo)): List of AdditionalInfo + structures - ptr.attributeId = changed_attribute.attribute_id - ptr.attributeType = changed_attribute.attribute_type + Returns: + SaAisErrorT: Return code of the corresponding NTF API call(s) - if changed_attribute.old_attribute_present: - ptr.oldAttributePresent = eSaBoolT.SA_TRUE - - assign_ntf_value_to_attribute( - ptr.oldAttributeValue, - changed_attribute.old_attribute_value, - changed_attribute.attribute_type) - else: - ptr.oldAttributePresent = eSaBoolT.SA_FALSE - - assign_ntf_value_to_attribute( - ptr.newAttributeValue, changed_attribute.new_attribute_value, - changed_attribute.attribute_type) + Raises: + SafException: If any NTF API call did not return SA_AIS_OK + """ + ntf_class_id = saNtf.SaNtfClassIdT(vendor_id, major_id, minor_id) + if _ntf_producer is None: + # SA_AIS_ERR_INIT is returned if user calls this function without first + # calling initialize() + return eSaAisErrorT.SA_AIS_ERR_INIT + + _ntf_producer.set_event_type(event_type) + _ntf_producer.set_class_id(ntf_class_id) + _ntf_producer.set_additional_text(additional_text) + _ntf_producer.set_notification_object(notification_object) + _ntf_producer.set_notifying_object(notifying_object) + _ntf_producer.set_event_time(event_time) + _ntf_producer.set_perceived_severity(perceived_severity) + if additional_info: + _ntf_producer.set_additional_info(additional_info) - # Send the notification - saNtfNotificationSend(notification.notificationHandle) + rc = _ntf_producer.send_alarm_notification() + if rc != eSaAisErrorT.SA_AIS_OK: + raise SafException(rc) - # Free the notification - saNtfNotificationFree(notification.notificationHandle) + return rc +@deprecate def send_security_alarm_notification( vendor_id, major_id, minor_id, severity, alarm_detector, user, provider, additional_text="", notification_object="", @@ -550,220 +335,122 @@ def send_security_alarm_notification( major_id (SaUint16T): Major id minor_id (SaUint16T): Minor id severity (SaNtfSeverityT): Severity - alarm_detector (SecurityAlarmDetector): Alarm detector information - user (ServiceUser): Service user information - provider (ServiceProvider): Service provider information + alarm_detector (SecurityAlarmDetector): SecurityAlarmDetector + information structure + user (ServiceUser): ServiceUser information structure + provider (ServiceProvider): ServiceProvider information structure additional_text (str): Additional text notification_object (str): Notification object's dn notifying_object (str): Notifying object's dn event_type (SaNtfEventTypeT): Event type event_time (SaTimeT): Event time - additional_info (list): List of AdditionalInfo class instances + additional_info (list(AdditionalInfo)): List of AdditionalInfo + structures probable_cause (SaNtfProbableCauseT): Probable cause - """ - if additional_info is None: - additional_info = [] - - # Create the notification - notification = saNtf.SaNtfSecurityAlarmNotificationT() - - saNtfSecurityAlarmNotificationAllocate(handle, notification, 0, - len(additional_text) + 1, - len(additional_info), 0) - - # Fill in the header - fill_in_header(notification.notificationHandle, - notification.notificationHeader, - notification_object, notifying_object, vendor_id, - major_id, minor_id, additional_text, event_type, - event_time, additional_info) - - # Fill in security alarm-specific fields - notification.probableCause.contents.value = probable_cause - notification.severity.contents.value = severity - - assign_ntf_value_to_attribute(notification.securityAlarmDetector, - alarm_detector.value, - alarm_detector.value_type) - assign_ntf_value_to_attribute(notification.serviceUser, - user.value, - user.value_type) + Returns: + SaAisErrorT: Return code of the corresponding NTF API call(s) - assign_ntf_value_to_attribute(notification.serviceProvider, - provider.value, - provider.value_type) - - # Send the notification - saNtfNotificationSend(notification.notificationHandle) - - # Free the notification - saNtfNotificationFree(notification.notificationHandle) - - -def send_alarm_notification( - vendor_id, major_id, minor_id, severity, additional_text="", - notification_object="", notifying_object="", - event_type=saNtf.eSaNtfEventTypeT.SA_NTF_ALARM_PROCESSING, - event_time=saAis.SA_TIME_UNKNOWN, additional_info=None): - """ Send an alarm notification - - Args: - vendor_id (SaUint32T): Vendor id - major_id (SaUint16T): Major id - minor_id (SaUint16T): Minor id - severity (SaNtfSeverityT): Severity - additional_text (str): Additional text - notification_object (str): Notification object's dn - notifying_object (str): Notifying object's dn - event_type (SaNtfEventTypeT): Event type - event_time (SaTimeT): Event time - additional_info (list): List of AdditionalInfo class instances + Raises: + SafException: If any NTF API call did not return SA_AIS_OK """ - if additional_info is None: - additional_info = [] - - # Create the notification - notification = saNtf.SaNtfAlarmNotificationT() - - saNtfAlarmNotificationAllocate(handle, notification, 0, - len(additional_text) + 1, - len(additional_info), 0, 0, 0, 0) - - # Fill in the header - fill_in_header(notification.notificationHandle, - notification.notificationHeader, - notification_object, notifying_object, vendor_id, - major_id, minor_id, additional_text, event_type, - event_time, additional_info) - - notification.numMonitoredAttributes = 0 - notification.numSpecificProblems = 0 - notification.numProposedRepairActions = 0 - notification.perceivedSeverity.contents.value = severity - notification.probableCause.contents.value = \ - saNtf.eSaNtfProbableCauseT.SA_NTF_DEGRADED_SIGNAL + ntf_class_id = saNtf.SaNtfClassIdT(vendor_id, major_id, minor_id) + if _ntf_producer is None: + # SA_AIS_ERR_INIT is returned if user calls this function without first + # calling initialize() + return eSaAisErrorT.SA_AIS_ERR_INIT + + _ntf_producer.set_event_type(event_type) + _ntf_producer.set_class_id(ntf_class_id) + _ntf_producer.set_additional_text(additional_text) + _ntf_producer.set_notification_object(notification_object) + _ntf_producer.set_notifying_object(notifying_object) + _ntf_producer.set_event_time(event_time) + if additional_info: + _ntf_producer.set_additional_info(additional_info) + _ntf_producer.set_severity(severity) + _ntf_producer.set_probable_cause(probable_cause) + _ntf_producer.set_security_alarm_detector(alarm_detector) + _ntf_producer.set_service_user(user) + _ntf_producer.set_service_provider(provider) - # Send the notification - saNtfNotificationSend(notification.notificationHandle) + rc = _ntf_producer.send_security_alarm_notification() + if rc != eSaAisErrorT.SA_AIS_OK: + raise SafException(rc) - # Free the notification - saNtfNotificationFree(notification.notificationHandle) + return rc +@deprecate def subscribe_for_notifications(notification_types=None): - """ Subscribe for notifications from NTF. The types of notifications to - subscribe to are passed in the notification_types list and subscriptions - are set up for all types if the list is empty. + """ Subscribe for notifications from NTF with the types specified in the + notification_types list + If the list is not provided, all types of notification are subscribed to + by default. Args: - notification_types (list): List of notification types - """ - filters = [] - - filter_handles = saNtf.SaNtfNotificationTypeFilterHandlesT() - filter_handles.objectCreateDeleteFilterHandle = \ - saNtf.SaNtfNotificationFilterHandleT(0) - filter_handles.attributeChangeFilterHandle = \ - saNtf.SaNtfNotificationFilterHandleT(0) - filter_handles.stateChangeFilterHandle = \ - saNtf.SaNtfNotificationFilterHandleT(0) - filter_handles.alarmFilterHandle = saNtf.SaNtfNotificationFilterHandleT(0) - filter_handles.securityFilterHandle = \ - saNtf.SaNtfNotificationFilterHandleT(0) - - # Create and allocate the alarm filter - if not notification_types or \ - saNtf.eSaNtfNotificationTypeT.SA_NTF_TYPE_ALARM in notification_types: - notification_filter = saNtf.SaNtfAlarmNotificationFilterT() + notification_types (list(SaNtfNotificationTypeT)): List of + notification types - saNtfAlarmNotificationFilterAllocate(handle, notification_filter, - 0, 0, 0, 0, 0, 0, 0) + Returns: + SaAisErrorT: Return code of the corresponding NTF API call(s) - filter_handles.alarmFilterHandle = \ - notification_filter.notificationFilterHandle - - filters.append(notification_filter.notificationFilterHandle) - - # Create and allocate the object create delete filter - if not notification_types or \ - saNtf.eSaNtfNotificationTypeT.SA_NTF_TYPE_OBJECT_CREATE_DELETE in \ - notification_types: - notification_filter = \ - saNtf.SaNtfObjectCreateDeleteNotificationFilterT() - - saNtfObjectCreateDeleteNotificationFilterAllocate(handle, - notification_filter, - 0, 0, 0, 0, 0) - - filter_handles.objectCreateDeleteFilterHandle = \ - notification_filter.notificationFilterHandle - filters.append(notification_filter.notificationFilterHandle) - - # Create and allocate the attribute change filter - if not notification_types or \ - saNtf.eSaNtfNotificationTypeT.SA_NTF_TYPE_ATTRIBUTE_CHANGE in \ - notification_types: - - notification_filter = saNtf.SaNtfAttributeChangeNotificationFilterT() - - saNtfAttributeChangeNotificationFilterAllocate(handle, - notification_filter, - 0, 0, 0, 0, 0) - - filter_handles.attributeChangeFilterHandle = \ - notification_filter.notificationFilterHandle - - filters.append(notification_filter.notificationFilterHandle) - - # Create and allocate the state change filter - if not notification_types or \ - saNtf.eSaNtfNotificationTypeT.SA_NTF_TYPE_STATE_CHANGE in \ - notification_types: - - notification_filter = saNtf.SaNtfStateChangeNotificationFilterT() - - saNtfStateChangeNotificationFilterAllocate(handle, - notification_filter, - 0, 0, 0, 0, 0, 0) + Raises: + SafException: If any NTF API call did not return SA_AIS_OK + """ + rc = _ntf_subscriber.subscribe(1, notification_types) + if rc != eSaAisErrorT.SA_AIS_OK: + raise SafException(rc) - filter_handles.stateChangeFilterHandle = \ - notification_filter.notificationFilterHandle + return rc - filters.append(notification_filter.notificationFilterHandle) - # Create and allocate the security alarm filter - if not notification_types or \ - saNtf.eSaNtfNotificationTypeT.SA_NTF_TYPE_SECURITY_ALARM in \ - notification_types: +@deprecate +def get_producer_handle(): + """ Get the NTF producer agent handle - notification_filter = saNtf.SaNtfSecurityAlarmNotificationFilterT() + Returns: + SaNtfHandleT: NTF producer agent handle + """ + return _ntf_producer.get_handle() - saNtfSecurityAlarmNotificationFilterAllocate(handle, - notification_filter, - 0, 0, 0, 0, 0, 0, 0, 0, 0) - filter_handles.securityAlarmFilterHandle = \ - notification_filter.notificationFilterHandle +@deprecate +def get_subscriber_handle(): + """ Get the NTF subscriber agent handle - filters.append(notification_filter.notificationFilterHandle) + Returns: + SaNtfHandleT: NTF subscriber agent handle + """ + return _ntf_subscriber.get_handle() - # Create a unique subscription id - sub_id = saNtf.SaNtfSubscriptionIdT(1) - # Start subscription - saNtfNotificationSubscribe(filter_handles, sub_id) +@deprecate +def get_subscriber_selection_object(): + """ Get the selection object associated with the subscriber agent handle - # Free up the filters - for filter_handle in filters: - saNtfNotificationFilterFree(filter_handle) + Returns: + SaSelectionObjectT: The NTF subscriber selection object + """ + return _ntf_subscriber.get_selection_object() +@deprecate def dispatch(flags=eSaDispatchFlagsT.SA_DISPATCH_ALL): """ Invoke NTF callbacks for queued events. The default is to dispatch all available events. Args: flags (eSaDispatchFlagsT): Flags specifying dispatch mode + + Returns: + SaAisErrorT: Return code of the corresponding NTF API call(s) + + Raises: + SafException: If any NTF API call did not return SA_AIS_OK """ - saNtfDispatch(handle, flags) + rc = _ntf_subscriber.dispatch(flags) + if rc != eSaAisErrorT.SA_AIS_OK: + raise SafException(rc) + + return rc diff --git a/python/pyosaf/utils/ntf/agent.py b/python/pyosaf/utils/ntf/agent.py new file mode 100644 index 0000000..dd2d218 --- /dev/null +++ b/python/pyosaf/utils/ntf/agent.py @@ -0,0 +1,500 @@ +############################################################################ +# +# (C) Copyright 2017 Ericsson AB. All rights reserved. +# +# 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. This file and program are licensed +# under the GNU Lesser General Public License Version 2.1, February 1999. +# The complete license can be accessed from the following location: +# http://opensource.org/licenses/lgpl-license.php +# See the Copying file included with the OpenSAF distribution for full +# licensing terms. +# +# Author(s): Ericsson +# +############################################################################ +# pylint: disable=unused-argument,too-few-public-methods +""" NTF utils common data structures and functions """ +from copy import deepcopy + +from pyosaf.saAis import saAis, SaVersionT, SaSelectionObjectT, eSaAisErrorT +from pyosaf import saNtf +from pyosaf.utils import decorate, initialize_decorate, log_err + +# Decorate pure saNtf* API's with error-handling retry and exception raising +# Library Life Cycle API's +saNtfInitialize = initialize_decorate(saNtf.saNtfInitialize) +saNtfInitialize_2 = initialize_decorate(saNtf.saNtfInitialize_2) +saNtfInitialize_3 = initialize_decorate(saNtf.saNtfInitialize_3) +saNtfSelectionObjectGet = decorate(saNtf.saNtfSelectionObjectGet) +saNtfDispatch = decorate(saNtf.saNtfDispatch) +saNtfFinalize = decorate(saNtf.saNtfFinalize) +# Producer API's +saNtfObjectCreateDeleteNotificationAllocate = \ + decorate(saNtf.saNtfObjectCreateDeleteNotificationAllocate) +saNtfAttributeChangeNotificationAllocate = \ + decorate(saNtf.saNtfAttributeChangeNotificationAllocate) +saNtfStateChangeNotificationAllocate = \ + decorate(saNtf.saNtfStateChangeNotificationAllocate) +saNtfStateChangeNotificationAllocate_3 = \ + decorate(saNtf.saNtfStateChangeNotificationAllocate_3) +saNtfAlarmNotificationAllocate = decorate(saNtf.saNtfAlarmNotificationAllocate) +saNtfSecurityAlarmNotificationAllocate = \ + decorate(saNtf.saNtfSecurityAlarmNotificationAllocate) +saNtfMiscellaneousNotificationAllocate = \ + decorate(saNtf.saNtfMiscellaneousNotificationAllocate) +saNtfPtrValAllocate = decorate(saNtf.saNtfPtrValAllocate) +saNtfArrayValAllocate = decorate(saNtf.saNtfArrayValAllocate) +saNtfIdentifierAllocate = decorate(saNtf.saNtfIdentifierAllocate) +saNtfNotificationSend = decorate(saNtf.saNtfNotificationSend) +saNtfNotificationSendWithId = decorate(saNtf.saNtfNotificationSendWithId) +saNtfNotificationFree = decorate(saNtf.saNtfNotificationFree) +saNtfVariableDataSizeGet = decorate(saNtf.saNtfVariableDataSizeGet) +# Consumer API's +saNtfLocalizedMessageGet = decorate(saNtf.saNtfLocalizedMessageGet) +saNtfLocalizedMessageFree = decorate(saNtf.saNtfLocalizedMessageFree) +saNtfLocalizedMessageFree_2 = decorate(saNtf.saNtfLocalizedMessageFree_2) +saNtfPtrValGet = decorate(saNtf.saNtfPtrValGet) +saNtfArrayValGet = decorate(saNtf.saNtfArrayValGet) +saNtfObjectCreateDeleteNotificationFilterAllocate = \ + decorate(saNtf.saNtfObjectCreateDeleteNotificationFilterAllocate) +saNtfAttributeChangeNotificationFilterAllocate = \ + decorate(saNtf.saNtfAttributeChangeNotificationFilterAllocate) +saNtfStateChangeNotificationFilterAllocate = \ + decorate(saNtf.saNtfStateChangeNotificationFilterAllocate) +saNtfStateChangeNotificationFilterAllocate_2 = \ + decorate(saNtf.saNtfStateChangeNotificationFilterAllocate_2) +saNtfAlarmNotificationFilterAllocate = \ + decorate(saNtf.saNtfAlarmNotificationFilterAllocate) +saNtfSecurityAlarmNotificationFilterAllocate = \ + decorate(saNtf.saNtfSecurityAlarmNotificationFilterAllocate) +saNtfNotificationFilterFree = decorate(saNtf.saNtfNotificationFilterFree) +# Subscriber API's +saNtfNotificationSubscribe = decorate(saNtf.saNtfNotificationSubscribe) +saNtfNotificationSubscribe_3 = decorate(saNtf.saNtfNotificationSubscribe_3) +saNtfNotificationUnsubscribe = decorate(saNtf.saNtfNotificationUnsubscribe) +saNtfNotificationUnsubscribe_2 = decorate(saNtf.saNtfNotificationUnsubscribe_2) +# Reader API's +saNtfNotificationReadInitialize = \ + decorate(saNtf.saNtfNotificationReadInitialize) +saNtfNotificationReadInitialize_2 = \ + decorate(saNtf.saNtfNotificationReadInitialize_2) +saNtfNotificationReadInitialize_3 = \ + decorate(saNtf.saNtfNotificationReadInitialize_3) +saNtfNotificationReadNext = decorate(saNtf.saNtfNotificationReadNext) +saNtfNotificationReadNext_3 = decorate(saNtf.saNtfNotificationReadNext_3) +saNtfNotificationReadFinalize = decorate(saNtf.saNtfNotificationReadFinalize) + + +class NotificationInfo(object): + """ This class encapsulates data structures for use by each specific + notification type """ + def __init__(self): + # Header info + self.event_type = 0 + self.notification_object = "" + self.notifying_object = "" + self.ntf_class_id = saNtf.SaNtfClassIdT(0, 0, 0) + self.event_time = saAis.SA_TIME_UNKNOWN + self.notification_id = None + self.additional_text = "" + self.additional_info = [] + # Object create/delete notification info + self.object_attributes = [] + self.source_indicator = \ + saNtf.eSaNtfSourceIndicatorT.SA_NTF_UNKNOWN_OPERATION + # Attribute change notification info + self.changed_attributes = [] + # State change notification info + self.state_changes = [] + # Alarm info + self.probable_cause = \ + saNtf.eSaNtfProbableCauseT.SA_NTF_UNSPECIFIED_REASON + self.specific_problems = [] + self.perceived_severity = saNtf.eSaNtfSeverityT.SA_NTF_SEVERITY_MINOR + self.trend = None + self.threshold_information = None + self.monitored_attrs = [] + self.proposed_repair_actions = [] + # Security alarm info + self.severity = saNtf.eSaNtfSeverityT.SA_NTF_SEVERITY_MINOR + self.security_alarm_detector = None + self.service_user = None + self.service_provider = None + + +class NotificationFilterInfo(object): + """ This class encapsulates the notification filter data structure for use + by each specific notification type """ + def __init__(self): + # Header info + self.obj_create_del_evt_list = [] + self.attr_change_evt_list = [] + self.state_change_evt_list = [] + self.alarm_evt_list = [] + self.sec_alarm_evt_list = [] + self.notification_object_list = [] + self.notifying_objects_list = [] + self.ntf_class_id_list = [] + # Filter info + self.source_indicator_list = [] + self.changed_state_list = [] + self.probable_cause_list = [] + self.perceived_severity_list = [] + self.trend_list = [] + self.severity_list = [] + + +class AdditionalInfo(object): + """ This class contains a piece of additional information to be included + in a notification """ + def __init__(self, info_id=None, info_type=None, info_value=None): + """ Constructor for AdditionalInfo class + + Args: + info_id (SaNtfElementIdT): infoId field of SaNtfAdditionalInfoT + info_type (SaNtfValueTypeT): infoType field of SaNtfAdditionalInfoT + info_value (Any type of eSaNtfValueTypeT): infoValue field of + SaNtfAdditionalInfoT + """ + self.info_id = info_id + self.info_type = info_type + self.info_value = info_value + + +class StateChange(object): + """ This class contains information about a state change event """ + def __init__(self, state_id=None, new_state=None, old_state_present=False, + old_state=None): + """ Constructor for StateChange class + + Args: + state_id (SaNtfElementIdT): stateId field of SaNtfStateChangeT + new_state (int): newState field of SaNtfStateChangeT + old_state_present (bool): oldStatePresent field of + SaNtfStateChangeT + old_state (int): oldState field of SaNtfStateChangeT + """ + self.state_id = state_id + self.new_state = new_state + self.old_state_present = old_state_present + self.old_state = old_state + + +class AttributeChange(object): + """ This class contains information about an object's attribute change """ + def __init__(self, attribute_id=None, attribute_type=None, + new_attribute_value=None, old_attribute_present=False, + old_attribute_value=None): + """ Constructor for AttributeChange class + + Args: + attribute_id (SaNtfElementIdT): attributeId field of + SaNtfAttributeChangeT + attribute_type (SaNtfValueTypeT): attributeType field of + SaNtfAttributeChangeT + new_attribute_value (Any type of eSaNtfValueTypeT): + newAttributeValue field of SaNtfAttributeChangeT + old_attribute_present (bool): oldAttributePresent field of + SaNtfAttributeChangeT + old_attribute_value (Any type of eSaNtfValueTypeT): + oldAttributeValue field of SaNtfAttributeChangeT + """ + self.attribute_id = attribute_id + self.attribute_type = attribute_type + self.new_attribute_value = new_attribute_value + self.old_attribute_present = old_attribute_present + self.old_attribute_value = old_attribute_value + + +class Attribute(object): + """ This class contains information about an object's attribute """ + def __init__(self, attribute_id=None, attribute_type=None, + attribute_value=None): + """ Constructor for Attribute class + + Args: + attribute_id (SaNtfElementIdT): attributeId field of + SaNtfAttributeT + attribute_type (SaNtfValueTypeT): attributeType field of + SaNtfAttributeT + attribute_value (Any type of eSaNtfValueTypeT): attributeValue + field of SaNtfAttributeT + """ + self.attribute_id = attribute_id + self.attribute_type = attribute_type + self.attribute_value = attribute_value + + +class SecurityAlarmDetector(object): + """ This class contains information about a security alarm detector """ + def __init__(self, value=None, value_type=None): + """ Constructor for SecurityAlarmDetector class + + Args: + value (Any type of eSaNtfValueTypeT): valueType field of + SaNtfSecurityAlarmDetectorT + value_type (SaNtfValueTypeT): value field of + SaNtfSecurityAlarmDetectorT + """ + self.value = value + self.value_type = value_type + + +class ServiceUser(object): + """ This class contains information about a service user """ + def __init__(self, value=None, value_type=None): + """ Constructor for ServiceUser class + + Args: + value (Any type of eSaNtfValueTypeT): valueType field of + SaNtfServiceUserT + value_type (SaNtfValueTypeT): value field of SaNtfServiceUserT + """ + self.value = value + self.value_type = value_type + + +class ServiceProvider(ServiceUser): + """ This class contains information about a service provider """ + pass + + +class ThresholdInformation(object): + """ This class contains information about a threshold """ + def __init__(self, threshold_id=None, threshold_value_type=None, + threshold_value=None, threshold_hysteresis=None, + observed_value=None, arm_time=None): + """ Constructor for ThresholdInformation class + + Args: + threshold_id (SaNtfElementIdT): thresholdId field of + SaNtfThresholdInformationT + threshold_value_type (SaNtfValueTypeT): thresholdValueType field of + SaNtfThresholdInformationT + threshold_value (Any type of eSaNtfValueTypeT): thresholdValue + field of SaNtfThresholdInformationT + threshold_hysteresis (SaNtfValueTypeT): thresholdHysteresis + field of SaNtfThresholdInformationT + observed_value (Any type of eSaNtfValueTypeT): observedValue field + of SaNtfThresholdInformationT + arm_time (SaTimeT): armTime field of SaNtfThresholdInformationT + """ + self.threshold_id = threshold_id + self.threshold_value_type = threshold_value_type + self.threshold_value = threshold_value + self.threshold_hysteresis = threshold_hysteresis + self.observed_value = observed_value + self.arm_time = arm_time + + +class SpecificProblem(object): + """ This class contains information about a specific problem """ + def __init__(self, problem_id=None, problem_class_id=None, + problem_type=None, problem_value=None): + """ Constructor for SpecificProblem class + + Args: + problem_id (SaNtfElementIdT): problemId field of + SaNtfSpecificProblemT + problem_class_id (SaNtfClassIdT): problemClassId field of + SaNtfSpecificProblemT + problem_type (SaNtfValueTypeT): problemType field of + SaNtfSpecificProblemT + problem_value (Any type of eSaNtfValueTypeT): problemValue field of + SaNtfSpecificProblemT + """ + self.problem_id = problem_id + self.problem_class_id = problem_class_id + self.problem_type = problem_type + self.problem_value = problem_value + + +class ProposedRepairAction(object): + """ This class contains information about a proposed repair action """ + def __init__(self, action_id=None, action_value_type=None, + action_value=None): + """ Constructor for ProposedRepairAction class + + Args: + action_id (SaNtfElementIdT): actionId field of + SaNtfProposedRepairActionT + action_value_type (SaNtfValueTypeT): actionValueType field of + SaNtfProposedRepairActionT + action_value (Any type of eSaNtfValueTypeT): actionValue field of + SaNtfProposedRepairActionT + """ + self.action_id = action_id + self.action_value_type = action_value_type + self.action_value = action_value + + +class NtfAgent(object): + """ This class manages the life cycle of an NTF agent """ + + def __init__(self, version=None): + """ Constructor for NtfAgent class + + Args: + version (SaVersionT): NTF API version + """ + self.init_version = version if version is not None \ + else SaVersionT('A', 1, 1) + self.version = None + self.handle = None + self.callbacks = None + self.sel_obj = SaSelectionObjectT() + self.ntf_notif_function = None + self.ntf_notif_discarded_function = None + + def __enter__(self): + """ Enter method for NtfAgent class """ + return self + + def __exit__(self, exception_type, exception_value, traceback): + """ Exit method for NtfAgent class + + Finalize the NTF agent handle + """ + if self.handle is not None: + saNtfFinalize(self.handle) + self.handle = None + + def __del__(self): + """ Destructor for NtfAgent class + + Finalize the NTF agent handle + """ + if self.handle is not None: + saNtf.saNtfFinalize(self.handle) + self.handle = None + + def _ntf_notif_callback(self, c_subscription_id, c_notif): + """ This callback is invoked by NTF to deliver a notification to a + subscriber of that notification type. + + Args: + c_subscription_id (SaNtfSubscriptionIdT): The subscription id + previously provided by the subscriber when subscribing for this + type of notification + c_notif (SaNtfNotificationsT): The notification delivered by this + callback + """ + pass + + def _ntf_notif_discarded_callback(self, c_subscription_id, + c_notification_type, c_number_discarded, + c_discarded_notification_identifiers): + """ This callback is invoked by NTF to notify a subscriber of a + particular notification type that one or more notifications of that + type have been discarded. + + Args: + c_subscription_id (SaNtfSubscriptionIdT): The subscription id + previously provided by the subscriber when subscribing for + discarded notifications + c_notification_type (SaNtfNotificationTypeT): The notification type + of the discarded notifications + c_number_discarded (SaUint32T): The number of discarded + notifications + c_discarded_notification_identifiers (SaNtfIdentifierT): The list + of notification identifiers of the discarded notifications + """ + pass + + def initialize(self, ntf_notif_func=None, ntf_notif_discarded_func=None): + """ Initialize the NTF agent library + + Args: + ntf_notif_func (callback): Callback function for subscribed + notifications + ntf_notif_discarded_func (callback): Callback function for + discarded notifications + + Returns: + SaAisErrorT: Return code of the saNtfInitialize() API call + """ + self.callbacks = None + # Set up callbacks if any + if ntf_notif_func is not None or ntf_notif_discarded_func is not None: + self.ntf_notif_function = ntf_notif_func + self.ntf_notif_discarded_function = ntf_notif_discarded_func + + self.callbacks = saNtf.SaNtfCallbacksT() + self.callbacks.saNtfNotificationCallback = \ + saNtf.SaNtfNotificationCallbackT(self._ntf_notif_callback) + self.callbacks.saNtfNotificationDiscardedCallback = \ + saNtf.SaNtfNotificationDiscardedCallbackT( + self._ntf_notif_discarded_callback) + + self.handle = saNtf.SaNtfHandleT() + self.version = deepcopy(self.init_version) + rc = saNtfInitialize(self.handle, self.callbacks, self.version) + if rc != eSaAisErrorT.SA_AIS_OK: + log_err("saNtfInitialize FAILED - %s" % eSaAisErrorT.whatis(rc)) + + return rc + + def get_handle(self): + """ Return the NTF agent handle successfully initialized + + Returns: + SaNtfHandleT: NTF agent handle + """ + return self.handle + + def _fetch_sel_obj(self): + """ Obtain a selection object (OS file descriptor) + + Returns: + SaAisErrorT: Return code of the saNtfSelectionObjectGet() API call + """ + rc = saNtfSelectionObjectGet(self.handle, self.sel_obj) + if rc != eSaAisErrorT.SA_AIS_OK: + log_err("saNtfSelectionObjectGet FAILED - %s" % + eSaAisErrorT.whatis(rc)) + + return rc + + def get_selection_object(self): + """ Return the selection object associated with the NTF handle + + Returns: + SaSelectionObjectT: Selection object associated with the NTF handle + """ + return self.sel_obj + + def dispatch(self, flags): + """ Invoke NTF callbacks for queued events + + Args: + flags (eSaDispatchFlagsT): Flags specifying dispatch mode + + Returns: + SaAisErrorT: Return code of the saNtfDispatch() API call + """ + rc = saNtfDispatch(self.handle, flags) + if rc != eSaAisErrorT.SA_AIS_OK: + log_err("saNtfDispatch FAILED - %s" % eSaAisErrorT.whatis(rc)) + + return rc + + def finalize(self): + """ Finalize the NTF agent handle + + Returns: + SaAisErrorT: Return code of the saNtfFinalize() API call + """ + rc = eSaAisErrorT.SA_AIS_OK + if self.handle: + rc = saNtfFinalize(self.handle) + if rc != eSaAisErrorT.SA_AIS_OK: + log_err("saNtfFinalize 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 diff --git a/python/pyosaf/utils/ntf/producer.py b/python/pyosaf/utils/ntf/producer.py new file mode 100644 index 0000000..cea5585 --- /dev/null +++ b/python/pyosaf/utils/ntf/producer.py @@ -0,0 +1,711 @@ +############################################################################ +# +# (C) Copyright 2017 Ericsson AB. All rights reserved. +# +# 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. This file and program are licensed +# under the GNU Lesser General Public License Version 2.1, February 1999. +# The complete license can be accessed from the following location: +# http://opensource.org/licenses/lgpl-license.php +# See the Copying file included with the OpenSAF distribution for full +# licensing terms. +# +# Author(s): Ericsson +# +############################################################################ +# pylint: disable=too-many-public-methods +""" NTF producer utilities """ +import ctypes + +from pyosaf import saNtf +from pyosaf.saAis import eSaBoolT, eSaAisErrorT, SaVoidPtr +from pyosaf.utils import log_warn, log_err, bad_handle_retry +from pyosaf.utils.ntf import agent as ntf + + +class NtfProducer(ntf.NtfAgent): + """ This class provides functions of the NTF Producer interface """ + + def __init__(self, version=None): + """ Constructor for NtfProducer class """ + super(NtfProducer, self).__init__(version) + self.ntf_info = ntf.NotificationInfo() + + def init(self): + """ Initialize the NTF agent + + Returns: + SaAisErrorT: Return code of the corresponding NTF API call(s) + """ + # This finalize() is needed for re-init case + self.finalize() + return self.initialize() + + @staticmethod + def _assign_ntf_value(ntf_handle, attr_value, value, value_type): + """ Assign the correct sub-field in the given attribute + + Args: + ntf_handle (SaNtfNotificationHandleT): Notification handle + attr_value (SaNtfValueT): Object attribute value in an object + creation or deletion notification + value (Any type of eSaNtfValueTypeT): Actual value of the object + attribute + value_type (SaNtfValueTypeT): Type of the object attribute value + """ + if value_type == saNtf.eSaNtfValueTypeT.SA_NTF_VALUE_UINT8: + attr_value.uint8Val = int(value) + + elif value_type == saNtf.eSaNtfValueTypeT.SA_NTF_VALUE_INT8: + attr_value.int8Val = int(value) + + elif value_type == saNtf.eSaNtfValueTypeT.SA_NTF_VALUE_UINT16: + attr_value.uint16Val = int(value) + + elif value_type == saNtf.eSaNtfValueTypeT.SA_NTF_VALUE_INT16: + attr_value.int16Val = int(value) + + elif value_type == saNtf.eSaNtfValueTypeT.SA_NTF_VALUE_UINT32: + attr_value.uint32Val = int(value) + + elif value_type == saNtf.eSaNtfValueTypeT.SA_NTF_VALUE_INT32: + attr_value.int32Val = int(value) + + elif value_type == saNtf.eSaNtfValueTypeT.SA_NTF_VALUE_FLOAT: + attr_value.floatVal = int(value) + + elif value_type == saNtf.eSaNtfValueTypeT.SA_NTF_VALUE_UINT64: + attr_value.uint64Val = int(value) + + elif value_type == saNtf.eSaNtfValueTypeT.SA_NTF_VALUE_INT64: + attr_value.int64Val = int(value) + + elif value_type == saNtf.eSaNtfValueTypeT.SA_NTF_VALUE_DOUBLE: + attr_value.doubleVal = int(value) + + elif value_type == saNtf.eSaNtfValueTypeT.SA_NTF_VALUE_LDAP_NAME or \ + value_type == saNtf.eSaNtfValueTypeT.SA_NTF_VALUE_STRING or \ + value_type == saNtf.eSaNtfValueTypeT.SA_NTF_VALUE_IPADDRESS: + len_value = len(value) + dest_ptr = SaVoidPtr() + rc = ntf.saNtfPtrValAllocate(ntf_handle, len_value + 1, dest_ptr, + attr_value) + if rc == eSaAisErrorT.SA_AIS_OK: + ctypes.memmove(dest_ptr, value, len_value + 1) + else: + log_warn("saNtfPtrValAllocate FAILED, rc = %s" % + eSaAisErrorT.whatis(rc)) + + def set_event_type(self, event_type): + """ Fill in the eventType field in the notification header + + Args: + event_type (SaNtfEventTypeT): Event type + """ + self.ntf_info.event_type = event_type + + def set_notification_object(self, notification_object): + """ Fill in the notificationObject field in the notification header + + Args: + notification_object (str): Notification object's dn + """ + self.ntf_info.notification_object = notification_object + + def set_notifying_object(self, notifying_object): + """ Fill in the notifyingObject field in the notification header + + Args: + notifying_object (str): Notifying object's dn + """ + self.ntf_info.notifying_object = notifying_object + + def set_class_id(self, notif_class_id): + """ Fill in the notificationClassId field in the notification header + + Args: + notif_class_id (SaNtfClassIdT): Notification class id + """ + self.ntf_info.ntf_class_id = notif_class_id + + def set_event_time(self, event_time): + """ Fill in the eventTime field in the notification header + + Args: + event_time (SaTimeT): Event time + """ + self.ntf_info.event_time = event_time + + def set_additional_text(self, additional_text): + """ Fill in the additionalText field in the notification header + + Args: + additional_text (str): Additional text + """ + self.ntf_info.additional_text = additional_text + + def set_additional_info(self, additional_info): + """ Fill in the additionalInfo field in the notification header + + Args: + additional_info (list(AdditionalInfo)): List of AdditionalInfo + structures + """ + self.ntf_info.additional_info = additional_info + + def set_source_indicator(self, source_indicator): + """ Fill in the sourceIndicator field in either object-create/delete + notification, attribute-change notification, or state-change + notification + + Args: + source_indicator (SaNtfSourceIndicatorT): Source indicator of the + notification + """ + self.ntf_info.source_indicator = source_indicator + + def set_object_attributes(self, attributes): + """ Fill in the objectAttributes field in object-create/delete + notification + + Args: + attributes (list(Attribute)): List of Attribute structures + """ + self.ntf_info.object_attributes = attributes + + def set_changed_attributes(self, changed_attributes): + """ Fill in the changedAttributes field in attribute-change + notification + + Args: + changed_attributes(list(AttributeChange)): List of AttributeChange + structures + """ + self.ntf_info.changed_attributes = changed_attributes + + def set_state_changes(self, state_changes): + """ Fill in the changedStates field in state-change notification + + Args: + state_changes (list(StateChange)): List of StateChange structures + """ + self.ntf_info.state_changes = state_changes + + def set_probable_cause(self, probable_cause): + """ Fill in the probableCause field in either alarm notification or + security-alarm notification + + Args: + probable_cause (SaNtfProbableCauseT): Probable cause of the alarm + """ + self.ntf_info.probable_cause = probable_cause + + def set_specific_problems(self, specific_problems): + """ Fill in the specificProblems field in alarm notification + + Args: + specific_problems (list(SpecificProblem)): List of SpecificProblem + structures + """ + self.ntf_info.specific_problems = specific_problems + + def set_perceived_severity(self, perceived_severity): + """ Fill in the perceivedSeverity field in alarm notification + + Args: + perceived_severity (SaNtfSeverityT): Severity of the alarm + """ + self.ntf_info.perceived_severity = perceived_severity + + def set_trend(self, trend): + """ Fill in the trend field in alarm notification + + Args: + trend (SaNtfSeverityTrendT): Trend of alarm severity + """ + self.ntf_info.trend = trend + + def set_threshold_information(self, threshold_information): + """ Fill in the thresholdInformation field in alarm notification + + Args: + threshold_information (list(ThresholdInformation)): List of + ThresholdInformation structures + """ + self.ntf_info.threshold_information = threshold_information + + def set_monitored_attributes(self, monitored_attributes): + """ Fill in the monitoredAttributes field in alarm notification + + Args: + monitored_attributes (list(Attribute)): List of monitored + Attribute structures + """ + self.ntf_info.monitored_attrs = monitored_attributes + + def set_proposed_repair_actions(self, proposed_repair_action): + """ Fill in the proposedRepairActions in alarm notification + + Args: + proposed_repair_action (list(ProposedRepairAction)): List of + ProposedRepairAction structures + """ + self.ntf_info.proposed_repair_actions = proposed_repair_action + + def set_severity(self, severity): + """ Fill in the severity field in security alarm notification + + Args: + severity (SaNtfSeverityT): Severity of the security alarm + """ + self.ntf_info.severity = severity + + def set_security_alarm_detector(self, security_alarm_detector): + """ Fill in the securityAlarmDetector field in security alarm + notification + + Args: + security_alarm_detector (SecurityAlarmDetector): + A SecurityAlarmDetector structure + """ + self.ntf_info.security_alarm_detector = security_alarm_detector + + def set_service_user(self, service_user): + """ Fill in the serviceUser field in security alarm notification + + Args: + service_user (ServiceUser): A ServiceUser structure + """ + self.ntf_info.service_user = service_user + + def set_service_provider(self, service_provider): + """ Fill in the serviceProvider field in security alarm notification + + Args: + service_provider (ServiceProvider): A ServiceProvider structure + """ + self.ntf_info.service_provider = service_provider + + def clear_info(self): + """ Reset the NotificationInfo field values """ + self.ntf_info = ntf.NotificationInfo() + + def _fill_in_header(self, ntf_handle, header): + """ Fill in the given notification header with the provided information + + Args: + ntf_handle (SaNtfNotificationHandleT): Notification handle + header (SaNtfNotificationHeaderT): Notification header + """ + header.eventType.contents.value = self.ntf_info.event_type + + header.notificationObject.contents.value = \ + self.ntf_info.notification_object + header.notificationObject.contents.length = \ + len(self.ntf_info.notification_object) + + header.notifyingObject.contents.value = self.ntf_info.notifying_object + header.notifyingObject.contents.length = \ + len(self.ntf_info.notifying_object) + + header.notificationClassId.contents.vendorId = \ + self.ntf_info.ntf_class_id.vendorId + header.notificationClassId.contents.majorId = \ + self.ntf_info.ntf_class_id.majorId + header.notificationClassId.contents.minorId = \ + self.ntf_info.ntf_class_id.minorId + + header.eventTime.contents.value = self.ntf_info.event_time + + header.lengthAdditionalText = len(self.ntf_info.additional_text) + ctypes.memmove(header.additionalText, + self.ntf_info.additional_text, + len(self.ntf_info.additional_text) + 1) + + if self.ntf_info.additional_info: + for i, add_info in enumerate(self.ntf_info.additional_info): + header.additionalInfo[i].infoId = add_info.info_id + header.additionalInfo[i].infoType = add_info.info_type + + self._assign_ntf_value(ntf_handle, + header.additionalInfo[i].infoValue, + add_info.info_value, add_info.info_type) + + @bad_handle_retry + def send_object_create_delete_notification(self): + """ Send an SaNtfObjectCreateDeleteNotificationT notification + + Returns: + SaAisErrorT: Return code of the corresponding NTF API call(s) + """ + # Create the notification + notification = saNtf.SaNtfObjectCreateDeleteNotificationT() + rc = ntf.saNtfObjectCreateDeleteNotificationAllocate( + self.handle, notification, 0, + len(self.ntf_info.additional_text) + 1, + len(self.ntf_info.additional_info), + len(self.ntf_info.object_attributes), + saNtf.saNtf.SA_NTF_ALLOC_SYSTEM_LIMIT) + if rc != eSaAisErrorT.SA_AIS_OK: + log_err("saNtfObjectCreateDeleteNotificationAllocate FAILED, " + "rc = %s" % eSaAisErrorT.whatis(rc)) + else: + # Fill in the header + self._fill_in_header(notification.notificationHandle, + notification.notificationHeader) + + # Fill in the notification + notification.sourceIndicator.contents.value = \ + self.ntf_info.source_indicator + + for i, attribute in enumerate(self.ntf_info.object_attributes): + ptr = notification.objectAttributes[i] + + ptr.attributeId = attribute.attribute_id + ptr.attributeType = attribute.attribute_type + + self._assign_ntf_value(notification.notificationHandle, + ptr.attributeValue, + attribute.attribute_value, + attribute.attribute_type) + + # Send the notification + rc = ntf.saNtfNotificationSend(notification.notificationHandle) + if rc != eSaAisErrorT.SA_AIS_OK: + log_err("saNtfNotificationSend FAILED, rc = %s" % + eSaAisErrorT.whatis(rc)) + else: + self.clear_info() + + # Free the notification + ntf.saNtfNotificationFree(notification.notificationHandle) + + if rc == eSaAisErrorT.SA_AIS_ERR_BAD_HANDLE: + init_rc = self.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 + + @bad_handle_retry + def send_attribute_change_notification(self): + """ Send an SaNtfAttributeChangeNotificationT notification + + Returns: + SaAisErrorT: Return code of the corresponding NTF API call(s) + """ + # Create the notification + notification = saNtf.SaNtfAttributeChangeNotificationT() + + rc = ntf.saNtfAttributeChangeNotificationAllocate( + self.handle, notification, 0, + len(self.ntf_info.additional_text) + 1, + len(self.ntf_info.additional_info), + len(self.ntf_info.changed_attributes), + saNtf.saNtf.SA_NTF_ALLOC_SYSTEM_LIMIT) + if rc != eSaAisErrorT.SA_AIS_OK: + log_err("saNtfAttributeChangeNotificationAllocate FAILED, " + "rc = %s" % eSaAisErrorT.whatis(rc)) + else: + # Fill in the header + self._fill_in_header(notification.notificationHandle, + notification.notificationHeader) + + # Fill in the notification + notification.sourceIndicator.contents.value = \ + self.ntf_info.source_indicator + + for i, changed_attr in enumerate(self.ntf_info.changed_attributes): + ptr = notification.changedAttributes[i] + + ptr.attributeId = changed_attr.attribute_id + ptr.attributeType = changed_attr.attribute_type + self._assign_ntf_value( + notification.notificationHandle, ptr.newAttributeValue, + changed_attr.new_attribute_value, + changed_attr.attribute_type) + + if changed_attr.old_attribute_present: + ptr.oldAttributePresent = eSaBoolT.SA_TRUE + + self._assign_ntf_value(notification.notificationHandle, + ptr.oldAttributeValue, + changed_attr.old_attribute_value, + changed_attr.attribute_type) + else: + ptr.oldAttributePresent = eSaBoolT.SA_FALSE + + # Send the notification + rc = ntf.saNtfNotificationSend(notification.notificationHandle) + if rc != eSaAisErrorT.SA_AIS_OK: + log_err("saNtfNotificationSend FAILED, rc = %s" % + eSaAisErrorT.whatis(rc)) + + else: + self.clear_info() + + # Free the notification + ntf.saNtfNotificationFree(notification.notificationHandle) + + if rc == eSaAisErrorT.SA_AIS_ERR_BAD_HANDLE: + init_rc = self.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 + + @bad_handle_retry + def send_state_change_notification(self): + """ Send an SaNtfStateChangeNotificationT notification + + Returns: + SaAisErrorT: Return code of the corresponding NTF API call(s) + """ + # Create the notification + notification = saNtf.SaNtfStateChangeNotificationT() + + rc = ntf.saNtfStateChangeNotificationAllocate( + self.handle, notification, 0, + len(self.ntf_info.additional_text) + 1, + len(self.ntf_info.additional_info), + len(self.ntf_info.state_changes), + saNtf.saNtf.SA_NTF_ALLOC_SYSTEM_LIMIT) + if rc != eSaAisErrorT.SA_AIS_OK: + log_err("saNtfStateChangeNotificationAllocate FAILED, " + "rc = %s" % eSaAisErrorT.whatis(rc)) + else: + # Fill in the header + self._fill_in_header(notification.notificationHandle, + notification.notificationHeader) + + # Fill in the notification + notification.sourceIndicator.contents.value = \ + self.ntf_info.source_indicator + + for i, state_change in enumerate(self.ntf_info.state_changes): + ptr = notification.changedStates[i] + + ptr.stateId = state_change.state_id + ptr.newState = state_change.new_state + + if state_change.old_state_present: + ptr.oldStatePresent = eSaBoolT.SA_TRUE + ptr.oldState = state_change.old_state + else: + ptr.oldStatePresent = eSaBoolT.SA_FALSE + + # Send the notification + rc = ntf.saNtfNotificationSend(notification.notificationHandle) + if rc != eSaAisErrorT.SA_AIS_OK: + log_err("saNtfNotificationSend FAILED, rc = %s" % + eSaAisErrorT.whatis(rc)) + else: + self.clear_info() + + # Free the notification + ntf.saNtfNotificationFree(notification.notificationHandle) + + if rc == eSaAisErrorT.SA_AIS_ERR_BAD_HANDLE: + init_rc = self.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 + + @bad_handle_retry + def send_alarm_notification(self): + """ Send an SaNtfAlarmNotificationT notification + + Returns: + SaAisErrorT: Return code of the corresponding NTF API call(s) + """ + # Create the notification + notification = saNtf.SaNtfAlarmNotificationT() + rc = ntf.saNtfAlarmNotificationAllocate( + self.handle, notification, 0, + len(self.ntf_info.additional_text) + 1, + len(self.ntf_info.additional_info), + len(self.ntf_info.specific_problems), + len(self.ntf_info.monitored_attrs), + len(self.ntf_info.proposed_repair_actions), + saNtf.saNtf.SA_NTF_ALLOC_SYSTEM_LIMIT) + if rc != eSaAisErrorT.SA_AIS_OK: + log_err("saNtfAlarmNotificationAllocate FAILED, " + "rc = %s" % eSaAisErrorT.whatis(rc)) + else: + # Fill in the header + self._fill_in_header(notification.notificationHandle, + notification.notificationHeader) + + # Fill in the notification + notification.probableCause.contents.value = \ + self.ntf_info.probable_cause + + for i, problem in enumerate(self.ntf_info.specific_problems): + ptr = notification.specificProblems[i] + ptr.problemId = problem.problem_id + ptr.problemClassId.vendorId = problem.problem_class_id.vendorId + ptr.problemClassId.majorId = problem.problem_class_id.majorId + ptr.problemClassId.minorId = problem.problem_class_id.minorId + ptr.problemType = problem.problem_type + self._assign_ntf_value( + notification.notificationHandle, ptr.problemValue, + problem.problem_value, problem.problem_type) + + notification.perceivedSeverity.contents.value = \ + self.ntf_info.perceived_severity + + if self.ntf_info.trend: + notification.trend.contents.value = self.ntf_info.trend + + if self.ntf_info.threshold_information: + ptr = notification.thresholdInformation.contents + ptr.thresholdId = \ + self.ntf_info.threshold_information.threshold_id + ptr.thresholdValueType = \ + self.ntf_info.threshold_information.threshold_value_type + + self._assign_ntf_value( + notification.notificationHandle, ptr.thresholdValue, + self.ntf_info.threshold_information.threshold_value, + ptr.thresholdValueType) + self._assign_ntf_value( + notification.notificationHandle, ptr.thresholdHysteresis, + self.ntf_info.threshold_information.threshold_hysteresis, + ptr.thresholdValueType) + self._assign_ntf_value( + notification.notificationHandle, ptr.observedValue, + self.ntf_info.threshold_information.observed_value, + ptr.thresholdValueType) + ptr.armTime = self.ntf_info.threshold_information.arm_time + + notification.perceivedSeverity.contents.value = \ + self.ntf_info.perceived_severity + + for i, attribute in enumerate(self.ntf_info.monitored_attrs): + ptr = notification.monitoredAttributes[i] + + ptr.attributeId = attribute.attribute_id + ptr.attributeType = attribute.attribute_type + + self._assign_ntf_value(notification.notificationHandle, + ptr.attributeValue, + attribute.attribute_value, + attribute.attribute_type) + + for i, action in enumerate(self.ntf_info.proposed_repair_actions): + ptr = notification.proposedRepairActions[i] + ptr.actionId = action.action_id + ptr.actionValueType = action.action_value_type + self._assign_ntf_value(notification.notificationHandle, + ptr.actionValue, + action.action_value, + action.action_value_type) + + # Send the notification + rc = ntf.saNtfNotificationSend(notification.notificationHandle) + if rc != eSaAisErrorT.SA_AIS_OK: + log_err("saNtfNotificationSend FAILED, rc = %s" % + eSaAisErrorT.whatis(rc)) + else: + self.clear_info() + + # Free the notification + ntf.saNtfNotificationFree(notification.notificationHandle) + + if rc == eSaAisErrorT.SA_AIS_ERR_BAD_HANDLE: + init_rc = self.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 + + @bad_handle_retry + def send_security_alarm_notification(self): + """ Send an SaNtfSecurityAlarmNotificationT notification + + Returns: + SaAisErrorT: Return code of the corresponding NTF API call(s) + """ + notification = saNtf.SaNtfSecurityAlarmNotificationT() + rc = ntf.saNtfSecurityAlarmNotificationAllocate( + self.handle, notification, 0, + len(self.ntf_info.additional_text) + 1, + len(self.ntf_info.additional_info), + saNtf.saNtf.SA_NTF_ALLOC_SYSTEM_LIMIT) + if rc != eSaAisErrorT.SA_AIS_OK: + log_err("saNtfSecurityAlarmNotificationAllocate FAILED, " + "rc = %s" % eSaAisErrorT.whatis(rc)) + else: + # Fill in the header + self._fill_in_header(notification.notificationHandle, + notification.notificationHeader) + + # Fill in security alarm-specific fields + notification.probableCause.contents.value = \ + self.ntf_info.probable_cause + notification.severity.contents.value = self.ntf_info.severity + + if self.ntf_info.security_alarm_detector: + notification.securityAlarmDetector.contents.valueType = \ + self.ntf_info.security_alarm_detector.value_type + self._assign_ntf_value( + notification.notificationHandle, + notification.securityAlarmDetector.contents.value, + self.ntf_info.security_alarm_detector.value, + self.ntf_info.security_alarm_detector.value_type) + + if self.ntf_info.service_user: + notification.serviceUser.contents.valueType = \ + self.ntf_info.service_user.value_type + self._assign_ntf_value(notification.notificationHandle, + notification.serviceUser.contents.value, + self.ntf_info.service_user.value, + self.ntf_info.service_user.value_type) + if self.ntf_info.service_provider: + notification.serviceProvider.contents.valueType = \ + self.ntf_info.service_provider.value_type + self._assign_ntf_value( + notification.notificationHandle, + notification.serviceProvider.contents.value, + self.ntf_info.service_provider.value, + self.ntf_info.service_provider.value_type) + + # Send the notification + rc = ntf.saNtfNotificationSend(notification.notificationHandle) + if rc != eSaAisErrorT.SA_AIS_OK: + log_err("saNtfNotificationSend FAILED, rc = %s" % + eSaAisErrorT.whatis(rc)) + else: + self.clear_info() + + # Free the notification + ntf.saNtfNotificationFree(notification.notificationHandle) + + if rc == eSaAisErrorT.SA_AIS_ERR_BAD_HANDLE: + init_rc = self.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 diff --git a/python/pyosaf/utils/ntf/reader.py b/python/pyosaf/utils/ntf/reader.py new file mode 100644 index 0000000..560ecd8 --- /dev/null +++ b/python/pyosaf/utils/ntf/reader.py @@ -0,0 +1,187 @@ +############################################################################ +# +# (C) Copyright 2017 Ericsson AB. All rights reserved. +# +# 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. This file and program are licensed +# under the GNU Lesser General Public License Version 2.1, February 1999. +# The complete license can be accessed from the following location: +# http://opensource.org/licenses/lgpl-license.php +# See the Copying file included with the OpenSAF distribution for full +# licensing terms. +# +# Author(s): Ericsson +# +############################################################################ +""" NTF reader utilities """ +from collections import Iterator +from pyosaf import saNtf +from pyosaf.saAis import eSaAisErrorT +from pyosaf.utils import log_err, bad_handle_retry +from pyosaf.utils.ntf import agent as ntf +from pyosaf.utils.ntf.subscriber import NtfConsumer + + +class NtfReader(NtfConsumer, Iterator): + """ This class provides functions of the NTF Reader interface """ + + def __init__(self, version=None): + """ Constructor for NtfReader class """ + super(NtfReader, self).__init__(version) + self.read_handle = None + self.filter_handles = \ + saNtf.SaNtfNotificationTypeFilterHandlesT(0, 0, 0, 0, 0) + self.search_direction = \ + saNtf.eSaNtfSearchDirectionT.SA_NTF_SEARCH_OLDER + self.search_criteria = saNtf.SaNtfSearchCriteriaT( + saNtf.eSaNtfSearchModeT.SA_NTF_SEARCH_ONLY_FILTER, 0, 0) + + def __enter__(self): + """ Enter method for NtfReader class """ + return self + + def __exit__(self, exception_type, exception_value, traceback): + """ Exit method for NtfReader class + + Finalize the NTF agent handle and clean up any allocated resources + """ + if self.read_handle is not None: + ntf.saNtfNotificationReadFinalize(self.read_handle) + self.read_handle = None + if self.handle is not None: + ntf.saNtfFinalize(self.handle) + self.handle = None + + def __del__(self): + """ Destructor for NtfReader class + + Finalize the NTF agent handle and clean up any allocated resources + """ + if self.read_handle is not None: + saNtf.saNtfNotificationReadFinalize(self.read_handle) + self.read_handle = None + if self.handle is not None: + saNtf.saNtfFinalize(self.handle) + self.handle = None + + def __iter__(self): + """ Iterator for NtfReader class """ + return self + + def next(self): + """ Override next() method of Iterator class """ + return self.__next__() + + def __next__(self): + """ Return the next element of the class iterator """ + notification = saNtf.SaNtfNotificationsT() + rc = ntf.saNtfNotificationReadNext(self.read_handle, + self.search_direction, + notification) + if rc != eSaAisErrorT.SA_AIS_OK: + if rc != eSaAisErrorT.SA_AIS_ERR_NOT_EXIST: + log_err("saNtfNotificationReadNext FAILED - %s" % + eSaAisErrorT.whatis(rc)) + raise StopIteration + + return self._parse_notification(notification) + + def init(self): + """ Initialize the NTF agent + + Returns: + SaAisErrorT: Return code of the corresponding NTF API call(s) + """ + self.finalize() + return self.initialize() + + def _parse_notification(self, notification): + """ Parse the read-out notification to retrieve its information + + Args: + notification (SaNtfNotificationsT): Notification to parse + + Returns: + SaNtfNotificationTypeT: Notification type + NotificationInfo: A NotificationInfo structure containing + information of the parsed notification + """ + _ntf_type = notification.notificationType + if _ntf_type == saNtf.eSaNtfNotificationTypeT.SA_NTF_TYPE_ALARM: + notification = notification.notification.alarmNotification + _ntf_info = self._parse_notification_header( + notification.notificationHandle, + notification.notificationHeader) + self._parse_alarm_ntf(notification, _ntf_info) + else: + notification = notification.notification.securityAlarmNotification + _ntf_info = self._parse_notification_header( + notification.notificationHandle, + notification.notificationHeader) + self._parse_security_alarm_ntf(notification, _ntf_info) + + return _ntf_type, _ntf_info + + def set_search_criteria(self, search_criteria): + """ Set the notification search criteria + + Args: + search_criteria (SaNtfSearchCriteriaT): Search criteria + """ + self.search_criteria = search_criteria + + @bad_handle_retry + def read(self, notification_types=None): + """ Start reading NTF notifications with the types specified in the + notification_types list. If the list is not provided, all types of + notification are read by default. + + Current NTF implementation of this function only supports read filters + for alarm and security alarm notification types. + + NOTE: Users have to set the wanted filters criteria via the set of + set_filter_*() methods before calling this method. Otherwise, the + filters criteria will be set with default values. + + Args: + notification_types (list(SaNtfNotificationTypeT)): List of + notification types + + Returns: + SaAisErrorT: Return code of the corresponding NTF API call(s) + """ + alarm_type = saNtf.eSaNtfNotificationTypeT.SA_NTF_TYPE_ALARM + security_alarm_type = \ + saNtf.eSaNtfNotificationTypeT.SA_NTF_TYPE_SECURITY_ALARM + + # Filters for notification types other than SA_NTF_TYPE_ALARM and + # SA_NTF_TYPE_SECURITY_ALARM are not supported + if notification_types is not None: + if alarm_type not in notification_types \ + and security_alarm_type not in notification_types: + return eSaAisErrorT.SA_AIS_ERR_NOT_SUPPORTED + + # Generate the alarm notification filter + if notification_types is None or alarm_type in notification_types: + self._generate_alarm_filter() + + # Generate the security alarm notification filter + if notification_types is None \ + or security_alarm_type in notification_types: + self._generate_security_alarm_filter() + + self.read_handle = saNtf.SaNtfReadHandleT() + rc = ntf.saNtfNotificationReadInitialize(self.search_criteria, + self.filter_handles, + self.read_handle) + if rc == eSaAisErrorT.SA_AIS_ERR_BAD_HANDLE: + init_rc = self.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 diff --git a/python/pyosaf/utils/ntf/subscriber.py b/python/pyosaf/utils/ntf/subscriber.py new file mode 100644 index 0000000..5eea9da --- /dev/null +++ b/python/pyosaf/utils/ntf/subscriber.py @@ -0,0 +1,923 @@ +############################################################################ +# +# (C) Copyright 2015 The OpenSAF Foundation +# (C) Copyright 2017 Ericsson AB. All rights reserved. +# +# 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. This file and program are licensed +# under the GNU Lesser General Public License Version 2.1, February 1999. +# The complete license can be accessed from the following location: +# http://opensource.org/licenses/lgpl-license.php +# See the Copying file included with the OpenSAF distribution for full +# licensing terms. +# +# Author(s): Ericsson +# +############################################################################ +# pylint: disable=unused-argument +""" NTF subscriber utilities """ +from __future__ import print_function +import ctypes +from copy import deepcopy + +from pyosaf import saNtf +from pyosaf.saAis import BYREF, eSaAisErrorT, SaVoidPtr, SaNameT, SaUint16T, \ + SaStringT +from pyosaf.utils import bad_handle_retry, log_err, log_warn +from pyosaf.utils.ntf import agent as ntf + +STRING_TYPES = [saNtf.eSaNtfValueTypeT.SA_NTF_VALUE_STRING, + saNtf.eSaNtfValueTypeT.SA_NTF_VALUE_LDAP_NAME, + saNtf.eSaNtfValueTypeT.SA_NTF_VALUE_IPADDRESS] +INT_TYPES = [saNtf.eSaNtfValueTypeT.SA_NTF_VALUE_UINT8, + saNtf.eSaNtfValueTypeT.SA_NTF_VALUE_INT8, + saNtf.eSaNtfValueTypeT.SA_NTF_VALUE_UINT16, + saNtf.eSaNtfValueTypeT.SA_NTF_VALUE_INT16, + saNtf.eSaNtfValueTypeT.SA_NTF_VALUE_UINT32, + saNtf.eSaNtfValueTypeT.SA_NTF_VALUE_INT32, + saNtf.eSaNtfValueTypeT.SA_NTF_VALUE_FLOAT, + saNtf.eSaNtfValueTypeT.SA_NTF_VALUE_UINT64, + saNtf.eSaNtfValueTypeT.SA_NTF_VALUE_INT64, + saNtf.eSaNtfValueTypeT.SA_NTF_VALUE_DOUBLE] + + +class NtfConsumer(ntf.NtfAgent): + """ This class provides functions of the NTF Consumer interface """ + def __init__(self, version=None): + """ Constructor for NtfConsumer class """ + super(NtfConsumer, self).__init__(version) + self.filter_info = ntf.NotificationFilterInfo() + self.filter_handles = None + + def __enter__(self): + """ Enter method for NtfConsumer class """ + return self + + def __exit__(self, exception_type, exception_value, traceback): + """ Exit method for NtfConsumer class + + Finalize the NTF agent handle + """ + if self.handle is not None: + ntf.saNtfFinalize(self.handle) + self.handle = None + + def __del__(self): + """ Destructor for NtfConsumer class + + Finalize the NTF agent handle + """ + if self.handle is not None: + saNtf.saNtfFinalize(self.handle) + self.handle = None + + def set_filter_event_types(self, event_type_list): + """ Set data for the eventTypes field in the notification filter header + of all notification types + + Args: + event_type_list (list(SaNtfEventTypeT)): List of notification event + types + """ + for event_type in event_type_list: + if event_type <= saNtf.eSaNtfEventTypeT.SA_NTF_OBJECT_DELETION: + self.filter_info.obj_create_del_evt_list.append(event_type) + elif event_type <= saNtf.eSaNtfEventTypeT.SA_NTF_ATTRIBUTE_RESET: + self.filter_info.attr_change_evt_list.append(event_type) + elif event_type <= \ + saNtf.eSaNtfEventTypeT.SA_NTF_OBJECT_STATE_CHANGE: + self.filter_info.state_change_evt_list.append(event_type) + elif event_type <= saNtf.eSaNtfEventTypeT.SA_NTF_ALARM_ENVIRONMENT: + self.filter_info.alarm_evt_list.append(event_type) + elif event_type <= saNtf.eSaNtfEventTypeT.SA_NTF_TIME_VIOLATION: + self.filter_info.sec_alarm_evt_list.append(event_type) + + def set_filter_notification_objects(self, notification_object_list): + """ Set data for the notificationObjects field in the notification + filter header of all notification types + + Args: + notification_object_list (list(str)): List of notification + objects' dns + """ + self.filter_info.notification_object_list = notification_object_list + + def set_filter_notifying_objects(self, notifying_objects_list): + """ Set data for the notifyingObjects field in the notification filter + header of all notification types + + Args: + notifying_objects_list (list(str)): List of notifying objects' dns + """ + self.filter_info.notifying_objects_list = notifying_objects_list + + def set_filter_ntf_class_ids(self, ntf_class_id_list): + """ Set data for the notificationClassIds field in the notification + filter header of all notification types + + Args: + ntf_class_id_list (list(SaNtfClassIdT)): List of notification + class ids + """ + self.filter_info.ntf_class_id_list = ntf_class_id_list + + def set_filter_source_indicators(self, source_indicators): + """ Set data for the sourceIndicators field in the notification filter + header of either one of object-create/delete, attribute-change, + state-change notification types + + Args: + source_indicators (list(SaNtfSourceIndicatorT)): List of + notification source indicators + """ + self.filter_info.source_indicator_list = source_indicators + + def set_filter_changed_states(self, changed_states): + """ Set data for the changedStates field in the notification filter + header of state-change notification type + + Args: + changed_states (list(StateChange)): List of StateChange structures + """ + self.filter_info.changed_state_list = changed_states + + def set_filter_probable_causes(self, probable_causes): + """ Set data for the probableCauses field in the notification filter + header of either of alarm or security alarm notification types + + Args: + probable_causes (list(SaNtfProbableCauseT)): List of alarm probable + causes + """ + self.filter_info.probable_cause_list = probable_causes + + def set_filter_perceived_severities(self, perceived_severities): + """ Set data for the perceivedSeverities field in the notification + filter header of alarm notification type + + Args: + perceived_severities (list(SaNtfSeverityT)): List of alarm + severities + """ + self.filter_info.probable_cause_list = perceived_severities + + def set_filter_trends(self, trends): + """ Set data for the trends field in the notification filter header of + alarm notification type + + Args: + trends (list(SaNtfSeverityTrendT)): List of alarm severity trends + """ + self.filter_info.trend_list = trends + + def set_filter_severities(self, severities): + """ Set data for the severities field in the notification filter header + of security alarm notification type + + Args: + severities (list(SaNtfSeverityT)): List of security alarm + severities + """ + self.filter_info.severity_list = severities + + def clear_filter_info(self): + """ Reset the NotificationInfo filter field values """ + self.filter_info = ntf.NotificationFilterInfo() + + def _fill_in_filter_header(self, filter_header): + """ Fill in the given notification filter header with user-provided + data """ + for i, info in enumerate(self.filter_info.notification_object_list): + filter_header.notificationObjects[i] = SaNameT(info) + for i, info in enumerate(self.filter_info.notifying_objects_list): + filter_header.notifyingObjects[i] = SaNameT(info) + for i, info in enumerate(self.filter_info.ntf_class_id_list): + filter_header.notificationClassIds[i] = info + + def _generate_object_create_delete_filter(self): + """ Allocate memory for the object-create/delete notification filter + and fill in the corresponding user-provided data + + Returns: + SaAisErrorT: Return code of the + saNtfObjectCreateDeleteNotificationFilterAllocate() API call + """ + notification_filter = \ + saNtf.SaNtfObjectCreateDeleteNotificationFilterT() + + rc = ntf.saNtfObjectCreateDeleteNotificationFilterAllocate( + self.handle, notification_filter, + len(self.filter_info.obj_create_del_evt_list), + len(self.filter_info.notification_object_list), + len(self.filter_info.notifying_objects_list), + len(self.filter_info.ntf_class_id_list), + len(self.filter_info.source_indicator_list)) + if rc != eSaAisErrorT.SA_AIS_OK: + log_err("saNtfObjectCreateDeleteNotificationFilterAllocate" + " FAILED, rc = %s" % eSaAisErrorT.whatis(rc)) + else: + self.filter_handles.objectCreateDeleteFilterHandle = \ + notification_filter.notificationFilterHandle + + self._fill_in_filter_header( + notification_filter.notificationFilterHeader) + + # Fill in the eventTypes array with user-provided data + for i, evt in enumerate(self.filter_info.obj_create_del_evt_list): + notification_filter.notificationFilterHeader.eventTypes[i] = \ + evt + + # Fill in the sourceIndicators array with user-provided data + for i, source_indicator in \ + enumerate(self.filter_info.source_indicator_list): + notification_filter.sourceIndicators[i] = source_indicator + + return rc + + def _generate_attribute_change_filter(self): + """ Allocate memory for the attribute change notification filter and + fill in the corresponding user-provided data + + Returns: + SaAisErrorT: Return code of the + saNtfAttributeChangeNotificationFilterAllocate() API call + """ + notification_filter = saNtf.SaNtfAttributeChangeNotificationFilterT() + + rc = ntf.saNtfAttributeChangeNotificationFilterAllocate( + self.handle, notification_filter, + len(self.filter_info.attr_change_evt_list), + len(self.filter_info.notification_object_list), + len(self.filter_info.notifying_objects_list), + len(self.filter_info.ntf_class_id_list), + len(self.filter_info.source_indicator_list)) + if rc != eSaAisErrorT.SA_AIS_OK: + log_err("saNtfAttributeChangeNotificationFilterAllocate FAILED, " + "rc = %s" % eSaAisErrorT.whatis(rc)) + else: + self.filter_handles.attributeChangeFilterHandle = \ + notification_filter.notificationFilterHandle + + self._fill_in_filter_header( + notification_filter.notificationFilterHeader) + + # Fill in the eventTypes array with user-provided data + for i, evt in enumerate(self.filter_info.attr_change_evt_list): + notification_filter.notificationFilterHeader.eventTypes[i] = \ + evt + + # Fill in the sourceIndicators array with user-provided data + for i, source_indicator in \ + enumerate(self.filter_info.source_indicator_list): + notification_filter.sourceIndicators[i] = source_indicator + + return rc + + def _generate_state_change_filter(self): + """ Allocate memory for the state change notification filter and fill + in the corresponding user-provided data + + Returns: + SaAisErrorT: Return code of the + saNtfStateChangeNotificationFilterAllocate() API call + """ + notification_filter = saNtf.SaNtfStateChangeNotificationFilterT() + + rc = ntf.saNtfStateChangeNotificationFilterAllocate( + self.handle, notification_filter, + len(self.filter_info.state_change_evt_list), + len(self.filter_info.notification_object_list), + len(self.filter_info.notifying_objects_list), + len(self.filter_info.ntf_class_id_list), + len(self.filter_info.source_indicator_list), + len(self.filter_info.changed_state_list)) + if rc != eSaAisErrorT.SA_AIS_OK: + log_err("saNtfStateChangeNotificationFilterAllocate FAILED, " + "rc = %s" % eSaAisErrorT.whatis(rc)) + else: + self.filter_handles.stateChangeFilterHandle = \ + notification_filter.notificationFilterHandle + + self._fill_in_filter_header( + notification_filter.notificationFilterHeader) + + # Fill in the eventTypes array with user-provided data + for i, evt in enumerate(self.filter_info.state_change_evt_list): + notification_filter.notificationFilterHeader.eventTypes[i] = \ + evt + + # Fill in the sourceIndicators array with user-provided data + for i, source_indicator in \ + enumerate(self.filter_info.source_indicator_list): + notification_filter.sourceIndicators[i] = source_indicator + + # Fill in the changedStates array with user-provided data + for i, state_change in \ + enumerate(self.filter_info.changed_state_list): + notification_filter.changedStates[i].stateId = \ + state_change.state_id + notification_filter.changedStates[i].newState = \ + state_change.new_state + notification_filter.changedStates[i].oldStatePresent = \ + state_change.old_state_present + if notification_filter.changedStates[i].oldStatePresent: + notification_filter.changedStates[i].oldState = \ + state_change.old_state + + return rc + + def _generate_alarm_filter(self): + """ Allocate memory for the alarm notification filter and fill in the + corresponding user-provided data + + Returns: + SaAisErrorT: Return code of the + saNtfAlarmNotificationFilterAllocate() API call + """ + notification_filter = saNtf.SaNtfAlarmNotificationFilterT() + + rc = ntf.saNtfAlarmNotificationFilterAllocate( + self.handle, notification_filter, + len(self.filter_info.alarm_evt_list), + len(self.filter_info.notification_object_list), + len(self.filter_info.notifying_objects_list), + len(self.filter_info.ntf_class_id_list), + len(self.filter_info.probable_cause_list), + len(self.filter_info.perceived_severity_list), + len(self.filter_info.trend_list)) + if rc != eSaAisErrorT.SA_AIS_OK: + log_err("saNtfAlarmNotificationFilterAllocate FAILED, " + "rc = %s" % eSaAisErrorT.whatis(rc)) + else: + self.filter_handles.alarmFilterHandle = \ + notification_filter.notificationFilterHandle + + self._fill_in_filter_header( + notification_filter.notificationFilterHeader) + + # Fill in the eventTypes array with user-provided data + for i, evt in enumerate(self.filter_info.alarm_evt_list): + notification_filter.notificationFilterHeader.eventTypes[i] = \ + evt + + # Fill in the probableCauses array with user-provided data + for i, probable_cause in \ + enumerate(self.filter_info.probable_cause_list): + notification_filter.probableCauses[i] = probable_cause + + # Fill in the perceivedSeverities array with user-provided data + for i, perceived_severity in \ + enumerate(self.filter_info.perceived_severity_list): + notification_filter.perceivedSeverities[i] = perceived_severity + + # Fill in the trends array with user-provided data + for i, trend in enumerate(self.filter_info.trend_list): + notification_filter.trends[i] = trend + + return rc + + def _generate_security_alarm_filter(self): + """ Allocate memory for the security alarm notification filter and fill + in the corresponding user-provided data + + Returns: + SaAisErrorT: Return code of the + saNtfSecurityAlarmNotificationFilterAllocate() API call + """ + notification_filter = saNtf.SaNtfSecurityAlarmNotificationFilterT() + + rc = ntf.saNtfSecurityAlarmNotificationFilterAllocate( + self.handle, notification_filter, + len(self.filter_info.sec_alarm_evt_list), + len(self.filter_info.notification_object_list), + len(self.filter_info.notifying_objects_list), + len(self.filter_info.ntf_class_id_list), + len(self.filter_info.probable_cause_list), + len(self.filter_info.severity_list), 0, 0, 0) + if rc != eSaAisErrorT.SA_AIS_OK: + log_err("saNtfAlarmNotificationFilterAllocate FAILED, " + "rc = %s" % eSaAisErrorT.whatis(rc)) + else: + self.filter_handles.securityAlarmFilterHandle = \ + notification_filter.notificationFilterHandle + + self._fill_in_filter_header( + notification_filter.notificationFilterHeader) + + # Fill in the eventTypes array with user-provided data + for i, evt in enumerate(self.filter_info.sec_alarm_evt_list): + notification_filter.notificationFilterHeader.eventTypes[i] = \ + evt + + # Fill in the probableCauses array with user-provided data + for i, probable_cause in \ + enumerate(self.filter_info.probable_cause_list): + notification_filter.probableCauses[i] = probable_cause + + # Fill in the severities array with user-provided data + for i, severity in enumerate(self.filter_info.severity_list): + notification_filter.severities[i] = severity + + return rc + + def _free_notification_filters(self): + """ Free the memory previously allocated for a notification filter """ + if self.filter_handles.objectCreateDeleteFilterHandle: + ntf.saNtfNotificationFilterFree( + self.filter_handles.objectCreateDeleteFilterHandle) + if self.filter_handles.attributeChangeFilterHandle: + ntf.saNtfNotificationFilterFree( + self.filter_handles.attributeChangeFilterHandle) + if self.filter_handles.stateChangeFilterHandle: + ntf.saNtfNotificationFilterFree( + self.filter_handles.stateChangeFilterHandle) + if self.filter_handles.alarmFilterHandle: + ntf.saNtfNotificationFilterFree( + self.filter_handles.alarmFilterHandle) + if self.filter_handles.securityAlarmFilterHandle: + ntf.saNtfNotificationFilterFree( + self.filter_handles.securityAlarmFilterHandle) + + @staticmethod + def _get_ptr_value(ntf_handle, value): + """ Get the correct string value from the given 'value' field in the + received notification + + Args: + ntf_handle (SaNtfNotificationHandleT): A handle to the internal + notification structure + value (SaNtfValueT): A 'value' field in the notification structure + + Returns: + SaStringT: The value in string format + """ + data_ptr = SaVoidPtr() + data_size = SaUint16T() + rc = ntf.saNtfPtrValGet(ntf_handle, value, data_ptr, data_size) + if rc != eSaAisErrorT.SA_AIS_OK: + log_warn("saNtfPtrValGet FAILED - %s" % (eSaAisErrorT.whatis(rc))) + return ctypes.cast(data_ptr, SaStringT).value + + def _get_ntf_value(self, ntf_handle, value, value_type): + """ Get the correct typed value from a specific 'value' field in the + received notification + + Args: + ntf_handle (SaNtfNotificationHandleT): A handle to the internal + notification structure + value (SaNtfValueT): A 'value' field in the notification structure + value_type (SaNtfValueTypeT): Value type + + Returns: + typed_value: Value of a specific type + """ + if value_type in STRING_TYPES: + return self._get_ptr_value(ntf_handle, value) + elif value_type in INT_TYPES: + return saNtf.unmarshalSaNtfValue(BYREF(value), value_type) + + return "This value type is currently not supported" + + def _parse_notification_header(self, ntf_handle, ntf_header): + """ Parse the notification header in the received notification to + retrieve information + + Args: + ntf_handle (SaNtfNotificationHandleT): A handle to the internal + notification structure + ntf_header (SaNtfNotificationHeaderT): Notification header + structure in the received notification + + Returns: + NotificationInfo: A NotificationInfo structure containing + information from the notification header + """ + ntf_info = ntf.NotificationInfo() + ntf_info.event_type = ntf_header.eventType.contents.value + ntf_info.notification_object = \ + ntf_header.notificationObject.contents.value + ntf_info.notifying_object = \ + ntf_header.notifyingObject.contents.value + ntf_info.ntf_class_id = \ + ntf_header.notificationClassId.contents + ntf_info.event_time = ntf_header.eventTime.contents.value + ntf_info.notification_id = \ + ntf_header.notificationId.contents.value + ntf_info.additional_text = \ + ntf_header.additionalText[0:ntf_header.lengthAdditionalText] + + for i in range(ntf_header.numAdditionalInfo): + c_add_info = ntf_header.additionalInfo[i] + add_info = ntf.AdditionalInfo() + add_info.info_id = c_add_info.infoId + add_info.info_type = c_add_info.infoType + add_info.info_value = self._get_ntf_value(ntf_handle, + c_add_info.infoValue, + c_add_info.infoType) + ntf_info.additional_info.append(add_info) + + return ntf_info + + def _parse_object_create_delete_ntf(self, c_ntf, ntf_info): + """ Parse the received object-create/delete notification to retrieve + its information + + Args: + c_ntf (SaNtfObjectCreateDeleteNotificationT): Object-create/delete + notification structure + ntf_info (NotificationInfo): NotificationInfo structure with + information from the notification + """ + ntf_handle = c_ntf.notificationHandle + ntf_info.source_indicator = \ + c_ntf.sourceIndicator.contents.value + for i in range(c_ntf.numAttributes): + c_attr = c_ntf.objectAttributes[i] + attr = ntf.Attribute() + attr.attribute_id = c_attr.attributeId + attr.attribute_type = c_attr.attributeType + attr.attribute_value = \ + self._get_ntf_value(ntf_handle, c_attr.attributeValue, + c_attr.attributeType) + ntf_info.object_attributes.append(attr) + + def _parse_attribute_change_ntf(self, c_ntf, ntf_info): + """ Parse the received attribute-change notification to retrieve its + information + + Args: + c_ntf (SaNtfAttributeChangeNotificationT): Attribute-change + notification structure + ntf_info (NotificationInfo): NotificationInfo structure with + information from the notification + """ + ntf_handle = c_ntf.notificationHandle + ntf_info.source_indicator = \ + c_ntf.sourceIndicator.contents.value + for i in range(c_ntf.numAttributes): + c_attr = c_ntf.changedAttributes[i] + attr = ntf.AttributeChange() + attr.attribute_id = c_attr.attributeId + attr.attribute_type = c_attr.attributeType + attr.new_attribute_value = \ + self._get_ntf_value(ntf_handle, c_attr.newAttributeValue, + c_attr.attributeType) + attr.old_attribute_present = c_attr.oldAttributePresent + if c_attr.oldAttributePresent: + attr.old_attribute_value = \ + self._get_ntf_value(ntf_handle, c_attr.oldAttributeValue, + c_attr.attributeType) + ntf_info.changed_attributes.append(attr) + + @staticmethod + def _parse_state_change_ntf(c_ntf, ntf_info): + """ Parse the received state-change notification to retrieve its + information + + Args: + c_ntf (SaNtfStateChangeNotificationT): State-change notification + structure + ntf_info (NotificationInfo): NotificationInfo structure with + information from the notification + """ + ntf_info.source_indicator = \ + c_ntf.sourceIndicator.contents.value + for i in range(c_ntf.numStateChanges): + c_attr = c_ntf.changedStates[i] + attr = ntf.StateChange() + attr.state_id = c_attr.stateId + attr.new_state = c_attr.newState + attr.old_state_present = c_attr.oldStatePresent + if c_attr.oldStatePresent: + attr.old_state = c_attr.oldState + ntf_info.state_changes.append(attr) + + def _parse_alarm_ntf(self, c_ntf, ntf_info): + """ Parse the received alarm notification to retrieve its information + + Args: + c_ntf (SaNtfAlarmNotificationT): Alarm notification structure + ntf_info (NotificationInfo): NotificationInfo structure with + information from the notification + """ + ntf_handle = c_ntf.notificationHandle + ntf_info.probable_cause = c_ntf.probableCause.contents.value + + for i in range(c_ntf.numSpecificProblems): + ntf_info.specific_problems.append( + c_ntf.specificProblems[i]) + ntf_info.perceived_severity = c_ntf.perceivedSeverity.contents.value + ntf_info.trend = c_ntf.trend.contents.value + + c_threshold_info = c_ntf.thresholdInformation.contents + if c_threshold_info: + threshold_info = ntf.ThresholdInformation() + c_threshold_info = c_ntf.thresholdInformation.contents + + threshold_info.threshold_id = c_threshold_info.thresholdId + threshold_info.threshold_value_type = \ + c_threshold_info.thresholdValueType + threshold_info.threshold_value = \ + self._get_ntf_value(ntf_handle, + c_threshold_info.thresholdValue, + c_threshold_info.thresholdValueType) + threshold_info.threshold_hysteresis = \ + self._get_ntf_value(ntf_handle, + c_threshold_info.thresholdHysteresis, + c_threshold_info.thresholdValueType) + threshold_info.observed_value = \ + self._get_ntf_value(ntf_handle, + c_threshold_info.observedValue, + c_threshold_info.thresholdValueType) + threshold_info.arm_time = c_threshold_info.armTime + + ntf_info.threshold_information = threshold_info + + for i in range(c_ntf.numMonitoredAttributes): + c_attr = c_ntf.monitoredAttributes[i] + attr = ntf.Attribute() + attr.attribute_id = c_attr.attributeId + attr.attribute_type = c_attr.attributeType + attr.attribute_value = \ + self._get_ntf_value(ntf_handle, c_attr.attributeValue, + c_attr.attributeType) + ntf_info.monitored_attrs.append(attr) + + for i in range(c_ntf.numProposedRepairActions): + c_action = c_ntf.proposedRepairActions[i] + action = ntf.ProposedRepairAction() + action.action_id = c_action.actionId + action.action_value_type = c_action.actionValueType + action.action_value = \ + self._get_ntf_value(ntf_handle, c_action.actionValue, + c_action.actionValueType) + ntf_info.proposed_repair_actions.append(action) + + def _parse_security_alarm_ntf(self, c_ntf, ntf_info): + """ Parse the received security alarm notification to retrieve its + information + + Args: + c_ntf (SaNtfSecurityAlarmNotificationT): Security alarm + notification structure + ntf_info (NotificationInfo): NotificationInfo structure with + information from the notification + """ + ntf_handle = c_ntf.notificationHandle + ntf_info.probable_cause = c_ntf.probableCause.contents.value + ntf_info.severity = c_ntf.severity.contents.value + if c_ntf.securityAlarmDetector.contents: + ntf_info.security_alarm_detector = ntf.SecurityAlarmDetector() + ntf_info.security_alarm_detector.value_type = \ + c_ntf.securityAlarmDetector.contents.valueType + ntf_info.security_alarm_detector.value = self._get_ntf_value( + ntf_handle, + c_ntf.securityAlarmDetector.contents.value, + c_ntf.securityAlarmDetector.contents.valueType) + if c_ntf.serviceUser.contents: + ntf_info.service_user = ntf.SecurityAlarmDetector() + ntf_info.service_user.value_type = \ + c_ntf.serviceUser.contents.valueType + ntf_info.service_user.value = self._get_ntf_value( + ntf_handle, + c_ntf.serviceUser.contents.value, + c_ntf.serviceUser.contents.valueType) + if c_ntf.serviceProvider.contents: + ntf_info.service_provider = ntf.SecurityAlarmDetector() + ntf_info.service_provider.value_type = \ + c_ntf.serviceProvider.contents.valueType + ntf_info.service_provider.value = self._get_ntf_value( + ntf_handle, + c_ntf.serviceProvider.contents.value, + c_ntf.serviceProvider.contents.valueType) + + +class NtfSubscriber(NtfConsumer): + """ This class provides functions of the NTF Subscriber interface """ + + def _ntf_notif_callback(self, c_subscription_id, c_notif): + """ This callback is invoked by NTF to deliver a notification to a + subscriber of that notification type. + + Args: + c_subscription_id (SaNtfSubscriptionIdT): The subscription id + previously provided by the subscriber when subscribing for this + type of notification + c_notif (SaNtfNotificationsT): The notification delivered by this + callback + """ + if not self.ntf_notif_function: + return + + subscription_id = c_subscription_id + notification_type = c_notif.contents.notificationType + if notification_type == \ + saNtf.eSaNtfNotificationTypeT.SA_NTF_TYPE_OBJECT_CREATE_DELETE: + notification = \ + c_notif.contents.notification.objectCreateDeleteNotification + ntf_handle = notification.notificationHandle + ntf_header = notification.notificationHeader + ntf_info = self._parse_notification_header(ntf_handle, ntf_header) + self._parse_object_create_delete_ntf(notification, ntf_info) + elif notification_type == \ + saNtf.eSaNtfNotificationTypeT.SA_NTF_TYPE_ATTRIBUTE_CHANGE: + notification = \ + c_notif.contents.notification.attributeChangeNotification + ntf_handle = notification.notificationHandle + ntf_header = notification.notificationHeader + ntf_info = self._parse_notification_header(ntf_handle, ntf_header) + self._parse_attribute_change_ntf(notification, ntf_info) + + elif notification_type == \ + saNtf.eSaNtfNotificationTypeT.SA_NTF_TYPE_STATE_CHANGE: + notification = \ + c_notif.contents.notification.stateChangeNotification + ntf_handle = notification.notificationHandle + ntf_header = notification.notificationHeader + ntf_info = self._parse_notification_header(ntf_handle, ntf_header) + self._parse_state_change_ntf(notification, ntf_info) + + elif notification_type == \ + saNtf.eSaNtfNotificationTypeT.SA_NTF_TYPE_ALARM: + notification = c_notif.contents.notification.alarmNotification + ntf_handle = notification.notificationHandle + ntf_header = notification.notificationHeader + ntf_info = self._parse_notification_header(ntf_handle, ntf_header) + self._parse_alarm_ntf(notification, ntf_info) + + elif notification_type == \ + saNtf.eSaNtfNotificationTypeT.SA_NTF_TYPE_SECURITY_ALARM: + notification = \ + c_notif.contents.notification.securityAlarmNotification + ntf_handle = notification.notificationHandle + ntf_header = notification.notificationHeader + ntf_info = self._parse_notification_header(ntf_handle, ntf_header) + self._parse_security_alarm_ntf(notification, ntf_info) + + else: + return + + # Send the ntf info to user's callback function + self.ntf_notif_function(subscription_id, notification_type, ntf_info) + + def _ntf_notif_discarded_callback(self, c_subscription_id, + c_notification_type, c_number_discarded, + c_discarded_notification_identifiers): + """ This callback is invoked by NTF to notify a subscriber of a + particular notification type that one or more notifications of that + type have been discarded. + + Args: + c_subscription_id (SaNtfSubscriptionIdT): The subscription id + previously provided by the subscriber when subscribing for + discarded notifications + c_notification_type (SaNtfNotificationTypeT): The notification type + of the discarded notifications + c_number_discarded (SaUint32T): The number of discarded + notifications + c_discarded_notification_identifiers (SaNtfIdentifierT): The list + of notification identifiers of the discarded notifications + """ + if not self.ntf_notif_discarded_function: + return + + # Send all info to user callback + self.ntf_notif_discarded_function(c_subscription_id, + c_notification_type, + c_number_discarded, + c_discarded_notification_identifiers) + + @bad_handle_retry + def _re_init(self): + """ Internal function to re-initialize the NTF agent and fetch a new + selection object in case of getting BAD_HANDLE during an operation + + Returns: + SaAisErrorT: Return code of the corresponding NTF API call(s) + """ + self.finalize() + self.version = deepcopy(self.init_version) + self.handle = saNtf.SaNtfHandleT() + rc = ntf.saNtfInitialize(self.handle, self.callbacks, self.version) + if rc != eSaAisErrorT.SA_AIS_OK: + log_err("saNtfInitialize FAILED - %s" % (eSaAisErrorT.whatis(rc))) + else: + rc = self._fetch_sel_obj() + + return rc + + def init(self, ntf_notif_func=None, ntf_notif_discarded_func=None): + """ Initialize the NTF agent and fetch the selection object + + Args: + ntf_notif_func (callback): Callback function for subscribed + notifications + ntf_notif_discarded_func (callback): Callback function for + discarded notifications + + Returns: + SaAisErrorT: Return code of the corresponding NTF API call(s) + """ + rc = self.initialize(ntf_notif_func, ntf_notif_discarded_func) + if rc == eSaAisErrorT.SA_AIS_OK: + rc = self._fetch_sel_obj() + if rc == eSaAisErrorT.SA_AIS_ERR_BAD_HANDLE: + self._re_init() + return rc + + @bad_handle_retry + def subscribe(self, subscription_id, notification_types=None): + """ Subscribe for notifications from NTF with the types specified in + the notification_types list. If the list is not provided, all types of + notification are subscribed to by default. + + NOTE: Users have to set the wanted filters criteria via the set of + set_filter_*() methods before calling this method. Otherwise, the + filters criteria will be set with default values. + + Args: + subscription_id (SaNtfSubscriptionIdT): Subscription id + notification_types (list(SaNtfNotificationTypeT)): List of + notification types + + Returns: + SaAisErrorT: Return code of the corresponding NTF API call(s) + """ + self.filter_handles = \ + saNtf.SaNtfNotificationTypeFilterHandlesT(0, 0, 0, 0, 0) + + create_delete = \ + saNtf.eSaNtfNotificationTypeT.SA_NTF_TYPE_OBJECT_CREATE_DELETE + attr_change = \ + saNtf.eSaNtfNotificationTypeT.SA_NTF_TYPE_ATTRIBUTE_CHANGE + state_change = \ + saNtf.eSaNtfNotificationTypeT.SA_NTF_TYPE_STATE_CHANGE + alarm = saNtf.eSaNtfNotificationTypeT.SA_NTF_TYPE_ALARM + security_alarm = \ + saNtf.eSaNtfNotificationTypeT.SA_NTF_TYPE_SECURITY_ALARM + + rc = eSaAisErrorT.SA_AIS_OK + # Create and allocate the object create delete filter + if notification_types is None or create_delete in notification_types: + rc = self._generate_object_create_delete_filter() + + if rc == eSaAisErrorT.SA_AIS_OK: + # Create and allocate the attribute change filter + if notification_types is None or attr_change in notification_types: + rc = self._generate_attribute_change_filter() + + if rc == eSaAisErrorT.SA_AIS_OK: + # Create and allocate the state change filter + if notification_types is None \ + or state_change in notification_types: + rc = self._generate_state_change_filter() + + if rc == eSaAisErrorT.SA_AIS_OK: + # Create and allocate the alarm filter + if notification_types is None or alarm in notification_types: + rc = self._generate_alarm_filter() + + if rc == eSaAisErrorT.SA_AIS_OK: + # Create and allocate the security alarm filter + if notification_types is None \ + or security_alarm in notification_types: + rc = self._generate_security_alarm_filter() + + if rc == eSaAisErrorT.SA_AIS_OK: + # Start subscription + rc = ntf.saNtfNotificationSubscribe(self.filter_handles, + subscription_id) + if rc != eSaAisErrorT.SA_AIS_OK: + log_err("saNtfNotificationSubscribe FAILED, rc = %s" % + eSaAisErrorT.whatis(rc)) + else: + self.clear_filter_info() + + # Free the notification filters + self._free_notification_filters() + + 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 + + @staticmethod + def unsubscribe(subscription_id): + """ Unsubscribe the previous notification subscription + + Args: + subscription_id (SaNtfSubscriptionIdT): Subscription id provided in + the previous notification subscription + + Returns: + SaAisErrorT: Return code of the saNtfNotificationUnsubscribe() + API call + """ + return ntf.saNtfNotificationUnsubscribe(subscription_id) -- 2.7.4 ------------------------------------------------------------------------------ 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