Hi Hoa,

Please find my comments marked with [Quyen]

Thanks,
Quyen

On 10/26/2017 3:33 PM, Hoa Le wrote:
Improved implementation of LOG pyosaf utils

The following comments shall be removed when the patch is pushed.

- This is the 3rd patch in the series of patches to improve the
   implementation of the pyosaf utils of OpenSAF services.
- This patch is based on and uses the code change of the
   pyosaf/utils/__init__.py file in the following patch for CLM:

   [devel] [PATCH 1/1] pyosaf: High level python interfaces for CLM [#2602]
   https://sourceforge.net/p/opensaf/mailman/message/36088562/
---
  python/pyosaf/utils/log/logger.py | 437 +++++++++++++++++++++++++++++++++-----
  1 file changed, 378 insertions(+), 59 deletions(-)

diff --git a/python/pyosaf/utils/log/logger.py 
b/python/pyosaf/utils/log/logger.py
index fc7739c..27115ea 100644
--- a/python/pyosaf/utils/log/logger.py
+++ b/python/pyosaf/utils/log/logger.py
@@ -15,95 +15,414 @@
  # Author(s): Ericsson
  #
  ############################################################################
+# pylint: disable=unused-argument
  """ SAF Logger utility """
  import ctypes
+from copy import deepcopy
+from select import select
-from pyosaf import saLog, saAis
-from pyosaf.utils import log
+from pyosaf.saAis import eSaAisErrorT, SaVersionT, SaSelectionObjectT, \
+    eSaDispatchFlagsT, SaNameT, saAis, SaStringT
+from pyosaf.saNtf import saNtf, eSaNtfEventTypeT, SaNtfClassIdT
+from pyosaf.saLog import saLog, eSaLogFileFullActionT, \
+    SaLogFileCreateAttributesT_2, eSaLogHeaderTypeT, SaLogNtfLogHeaderT, \
+    SaLogGenericLogHeaderT, SaLogRecordT, SaLogCallbacksT, \
+    SaLogWriteLogCallbackT, SaLogHandleT, SaLogStreamHandleT, SaLogHeaderT, \
+    SaLogBufferT
+from pyosaf.utils import log, bad_handle_retry, log_err
+
+SYSTEM_STREAM = saLog.SA_LOG_STREAM_SYSTEM
+NOTIF_STREAM = saLog.SA_LOG_STREAM_NOTIFICATION
+ALARM_STREAM = saLog.SA_LOG_STREAM_ALARM
+
+
+class LoggerInfo(object):
+    """ This class encapsulates data structures used when opening a specific
+    log stream, or writing a log record to a log stream """
+    def __init__(self, exec_name=""):
+        self.stream_name = SYSTEM_STREAM
+        # log file create attributes
+        self.log_file_name = "saLogApplication"
+        self.log_file_path_name = "."
+        self.max_log_file_size = 5000000
+        self.max_log_record_size = 150
+        self.ha_property = True
+        self.log_file_full_action = \
+            eSaLogFileFullActionT.SA_LOG_FILE_FULL_ACTION_ROTATE
+        self.max_files_rotated = 4
+        self.log_file_format = "@Cr @Ch:@Cn:@Cs @Sv @Sl \"@Cb\""
+        # notification header info
+        self.event_type = eSaNtfEventTypeT.SA_NTF_ALARM_QOS
+        self.notification_object = ""
+        self.notifying_object = ""
+        self.ntf_class_id = SaNtfClassIdT(0, 0, 0)
+        self.event_time = saAis.SA_TIME_UNKNOWN
+        # generic info
+        self.log_severity = saLog.SA_LOG_SEV_INFO
+        self.log_service_user_name = exec_name
class SafLogger(object):
-    """ SafLogger class handling log record write to the system log stream """
-    @staticmethod
-    def write_log_callback(invocation, error):
-        """ This callback is triggered when the operation requested by the
-        invocation of saLogWriteLogAsync() completes
+    """ This class provides logging function of the LOG service """
+    def __init__(self, exec_name="", version=None):
[Quyen] Please consider change a better name for "exec_name" as this argument is to initialize the service_user_name
+        """ Constructor for SafLogger class
+
+        Args:
+            exec_name (str): Logger's service user name
+        """
+        self.exec_name = exec_name
+        self.init_version = version if version else SaVersionT('A', 2, 3)
+        self.version = None
+        self.invocation = 0
+        self.log_handle = None
+        self.stream_handle = None
+        self.sel_obj = SaSelectionObjectT()
+        self.record = SaLogRecordT()
+        self.log_write_error = eSaAisErrorT.SA_AIS_OK
+        self.callbacks = SaLogCallbacksT()
+        self.callbacks.saLogWriteLogCallback = \
+            SaLogWriteLogCallbackT(self._log_write_callback)
+        self.logger_info = LoggerInfo(exec_name)
+
+    def __enter__(self):
+        """ Enter method for SafLogger class """
+        return self
+
+    def __exit__(self, exception_type, exception_value, traceback):
+        """ Exit method for SafLogger class """
+        self._finalize()
+
+    def __del__(self):
+        """ Destructor for SafLogger class """
+        self._finalize()
+
+    def _log_write_callback(self, invocation, error):
+        """ This callback is invoked by the LOG service to acknowledge a
+        previous invocation of the saLogWriteLogAsync() function with the
+        SA_LOG_RECORD_WRITE_ACK flag, and also provides the result of the
+        respective log write operation.
Args:
              invocation (SaInvocationT): Invocation associated with an
                  invocation of saLogWriteLogAsync()
-            error (SaAisErrorT): Return code of the log write operation
+            error (SaAisErrorT): Result of the log write operation
          """
-        pass
+        self.log_write_error = error
- def __init__(self, exec_name=""):
-        """ Constructor for SafLogger class
+    def _finalize(self):
+        """ Finalize the log handle
+        The log stream associated with the handle will be closed as a result.
+        """
+        if self.log_handle:
[Quyen] General comment: Should use is or is not for checking with None type. Please update all the similar places
PEP8:

 *

   Comparisons to singletons like None should always be done with is or
   is not, never the equality operators.

   Also, beware of writing if x when you really mean if x is not None
   -- e.g. when testing whether a variable or argument that defaults to
   None was set to some other value. The other value might have a type
   (such as a container) that could be false in a boolean context!

+            rc = log.saLogFinalize(self.log_handle)
+            if rc != eSaAisErrorT.SA_AIS_OK:
+                log_err("saLogFinalize FAILED - %s" %
+                        eSaAisErrorT.whatis(rc))
+            if rc == eSaAisErrorT.SA_AIS_OK or \
+                    rc == eSaAisErrorT.SA_AIS_ERR_BAD_HANDLE:
+                self.log_handle = None
+                self.stream_handle = None
+
+    @bad_handle_retry
+    def init(self):
[Quyen] Should split this method into 2 methods as it does 2 things initialize and open log stream. Beside that I don't see the close method to close the opened stream. one more method should be added to support closing the stream explicitly.
+        """ Initialize the LOG agent library and open the specified log stream
+
+        Returns:
+            SaAisErrorT: Return code of the corresponding LOG API call(s)
+        """
+        # Clean up resources left from previous initialization (if any)
+        self._finalize()
+
+        self.log_handle = SaLogHandleT()
+        self.stream_handle = SaLogStreamHandleT()
+        self.version = deepcopy(self.init_version)
+
+        rc = log.saLogInitialize(self.log_handle, self.callbacks, self.version)
+        if rc != eSaAisErrorT.SA_AIS_OK:
+            log_err("saLogInitialize FAILED - %s" %
+                    eSaAisErrorT.whatis(rc))
+        else:
+            rc = log.saLogSelectionObjectGet(self.log_handle, self.sel_obj)
+            if rc != eSaAisErrorT.SA_AIS_OK:
+                log_err("saLogSelectionObjectGet FAILED - %s" %
+                        eSaAisErrorT.whatis(rc))
+        if rc == eSaAisErrorT.SA_AIS_OK:
+            stream_name = SaNameT(self.logger_info.stream_name)
+            create_attrs, open_flag = self._set_log_stream_open_config()
+            rc = log.saLogStreamOpen_2(self.log_handle, stream_name,
+                                       create_attrs, open_flag,
+                                       saAis.SA_TIME_ONE_SECOND,
+                                       self.stream_handle)
+            if rc != eSaAisErrorT.SA_AIS_OK:
+                log_err("saLogStreamOpen_2 FAILED - %s" %
+                        eSaAisErrorT.whatis(rc))
+        return rc
+
+    def _get_invocation(self):
+        """ Generate and return a new unique invocation number for use
+
+        Returns:
+            int: The new invocation number
+        """
+        self.invocation += 1
+        return self.invocation
+
+    def _set_log_stream_open_config(self):
[Quyen] The method name is "set" but what this method is to create the attribute structure for later use. please rename the method to better match with what it does.
+        """ Configure the attributes and flag used to open (or create, if not
+        yet existed) an application log stream
+
+        If the stream to open is a config log stream, setting of attributes and
+        flag is not allowed, and default values are enforced.
+
+        Returns:
+            SaLogFileCreateAttributesT_2: Log file create attributes
+            SaLogStreamOpenFlagsT: Log stream open flag
+        """
+        # Return default values of 'None' for log file create attributes and
+        # '0' for log stream open flag if the stream to open is a config stream
+        if "safLgStrCfg" in self.logger_info.stream_name:
+            return None, 0
+
+        create_attrs = SaLogFileCreateAttributesT_2()
+        create_attrs.logFileName = SaStringT(self.logger_info.log_file_name)
+        create_attrs.logFilePathName = SaStringT(
+            self.logger_info.log_file_path_name)
+        create_attrs.maxLogFileSize = self.logger_info.max_log_file_size
+        create_attrs.maxLogRecordSize = self.logger_info.max_log_record_size
+        create_attrs.haProperty = self.logger_info.ha_property
+        create_attrs.logFileFullAction = \
+            self.logger_info.log_file_full_action
+        create_attrs.maxFilesRotated = self.logger_info.max_files_rotated
+        create_attrs.logFileFmt = SaStringT(self.logger_info.log_file_format)
+
+        return create_attrs, saLog.SA_LOG_STREAM_CREATE
+
+    def _set_log_header(self):
[Quyen] The method name is "set" but what this method is to create the log header structure for later use. please rename the method to better match with what it does.
+        """ Fill in the header of the log record to be written with user-
+        provided information
+
+        Returns:
+            SaLogHeaderTypeT: Log header type
+            SaLogHeaderT: Log header structure filled with information
+        """
+        if self.logger_info.stream_name == ALARM_STREAM or \
+                self.logger_info.stream_name == NOTIF_STREAM:
+            header_type = eSaLogHeaderTypeT.SA_LOG_NTF_HEADER
+            ntf_header = SaLogNtfLogHeaderT()
+            ntf_header.notificationId = saNtf.SA_NTF_IDENTIFIER_UNUSED
+            ntf_header.eventType = self.logger_info.event_type
+            ntf_header.notificationObject.contents = SaNameT(
+                self.logger_info.notification_object)
+            ntf_header.notifyingObject.contents = SaNameT(
+                self.logger_info.notifying_object)
+            if self.logger_info.ntf_class_id:
+                ntf_header.notificationClassId.contents = \
+                    self.logger_info.ntf_class_id
+            ntf_header.eventTime = self.logger_info.event_time
+            log_header = SaLogHeaderT()
+            log_header.ntfHdr = ntf_header
+        else:
+            header_type = eSaLogHeaderTypeT.SA_LOG_GENERIC_HEADER
+            generic_header = SaLogGenericLogHeaderT()
+            if self.logger_info.ntf_class_id:
+                generic_header.notificationClassId.contents = \
+                    self.logger_info.ntf_class_id
+            generic_header.logSvcUsrName.contents = \
+                SaNameT(self.logger_info.log_service_user_name)
+            generic_header.logSeverity = self.logger_info.log_severity
+            log_header = SaLogHeaderT()
+            log_header.genericHdr = generic_header
+        return header_type, log_header
+
+    def set_log_stream_name(self, stream_name):
+        """ Set the name of the log stream to open/create
Args:
-            exec_name (str): User name
+            stream_name (str): DN of the log stream; must be one of the below:
+                1) saLog.SA_LOG_STREAM_SYSTEM
+                2) saLog.SA_LOG_STREAM_NOTIFICATION
+                3) saLog.SA_LOG_STREAM_ALARM
+                4) User-defined application stream name with DN format:
+                   safLgStr=<stream_name>,safApp=safLogService
          """
-        self.invocation = 0
-        self.log_handle = saLog.SaLogHandleT()
-        self.sel_obj = saAis.SaSelectionObjectT()
-        self.callbacks = saLog.SaLogCallbacksT()
-        self.stream_handle = saLog.SaLogStreamHandleT()
-        self.record = saLog.SaLogRecordT()
+        self.logger_info.stream_name = stream_name
- self.callbacks.saLogWriteLogCallback = \
-            saLog.SaLogWriteLogCallbackT(self.write_log_callback)
+    def set_log_file_name(self, file_name):
+        """ Set the logFileName field of SaLogFileCreateAttributesT_2
- log.saLogInitialize(self.log_handle, self.callbacks, log.LOG_VERSION)
-        log.saLogSelectionObjectGet(self.log_handle, self.sel_obj)
+        Args:
+            file_name (str): Log file name
+        """
+        self.logger_info.log_file_name = file_name
- stream_name = saAis.SaNameT(saLog.saLog.SA_LOG_STREAM_SYSTEM)
-        log.saLogStreamOpen_2(self.log_handle, stream_name, None, 0,
-                              saAis.saAis.SA_TIME_ONE_SECOND,
-                              self.stream_handle)
+    def set_log_file_path_name(self, path_name):
+        """ Set the logFilePathName field of SaLogFileCreateAttributesT_2
- user_name = ctypes.pointer(saAis.SaNameT(exec_name))
-        header_type = saLog.eSaLogHeaderTypeT.SA_LOG_GENERIC_HEADER
-        self.record.logTimeStamp = saAis.saAis.SA_TIME_UNKNOWN
-        self.record.logHdrType = header_type
-        self.record.logHeader.genericHdr.logSvcUsrName = user_name
+        Args:
+            path_name (str): Log file path name
+        """
+        self.logger_info.log_file_path_name = path_name
- def finalize(self):
-        """ Close a log stream and finalize the log handle corresponding to
-        the log stream
+    def set_max_log_file_size(self, max_file_size):
+        """ Set the maxLogFileSize field of SaLogFileCreateAttributesT_2
+
+        Args:
+            max_file_size (int): Max log file size in bytes
          """
-        log.saLogStreamClose(self.stream_handle)
-        log.saLogFinalize(self.log_handle)
+        self.logger_info.max_log_file_size = max_file_size
- def get_invocation(self):
-        """ Increase the invocation number and return it """
-        self.invocation += 1
-        return self.invocation
+    def set_max_log_record_size(self, max_record_size):
+        """ Set the maxLogRecordSize field of SaLogFileCreateAttributesT_2
- def __del__(self):
-        """ Destructor to automatically finalize log handle """
-        self.finalize()
+        Args:
+            max_record_size (int): Max log record size in bytes
+        """
+        self.logger_info.max_log_file_size = max_record_size
+
+    def set_ha_property(self, ha_property):
+        """ Set the haProperty field of SaLogFileCreateAttributesT_2
+
+        Args:
+            ha_property (bool): HA property of the log file
+        """
+        self.logger_info.ha_property = ha_property
+
+    def set_log_file_full_action(self, file_full_action):
+        """ Set the logFileFullAction field of SaLogFileCreateAttributesT_2
+
+        Args:
+            file_full_action (SaLogFileFullActionT): Log file-full action
+        """
+        self.logger_info.log_file_full_action = file_full_action
+
+    def set_max_files_rotated(self, max_files_rotated):
+        """ Set the maxFilesRotated field of SaLogFileCreateAttributesT_2
+
+        Args:
+            max_files_rotated (int): Max number of rotated log files
+        """
+        self.logger_info.max_files_rotated = max_files_rotated
+
+    def set_log_file_fmt(self, log_file_format):
+        """ Set the logFileFmt field of SaLogFileCreateAttributesT_2
+
+        Args:
+            log_file_format (str): Log record format expression
+        """
+        self.logger_info.log_file_format = log_file_format
+
+    def set_event_type(self, event_type):
+        """ Set the eventType field of SaLogNtfLogHeaderT
+
+        Args:
+            event_type (SaNtfEventTypeT): Alarm/Notification log event type
+        """
+        self.logger_info.event_type = event_type
+
+    def set_notification_object(self, notification_object):
+        """ Set the notificationObject field of SaLogNtfLogHeaderT
+
+        Args:
+            notification_object (str): Notification object's dn
+        """
+        self.logger_info.notification_object = notification_object
+
+    def set_notifying_object(self, notifying_object):
+        """ Set the notifyingObject field of SaLogNtfLogHeaderT
+ Args:
+            notifying_object (str): Notifying object's dn
+        """
+        self.logger_info.notifying_object = notifying_object
+
+    def set_ntf_class_id(self, ntf_class_id):
+        """ Set the notificationClassId field of SaLogNtfLogHeaderT and
+        SaLogGenericLogHeaderT
+
+        Args:
+            ntf_class_id (SaNtfClassIdT): Notification class id
+        """
+        self.logger_info.ntf_class_id = ntf_class_id
+
+    def set_ntf_event_time(self, event_time):
+        """ Set the eventTime field of SaLogNtfLogHeaderT
+
+        Args:
+            event_time (SaTimeT): Alarm/Notification log event time
+        """
+        self.logger_info.event_time = event_time
+
+    def set_log_severity(self, log_severity):
+        """ Set the logSeverity field of SaLogGenericLogHeaderT
+
+        Args:
+            log_severity (SaLogSeverityT): Log severity
+        """
+        self.logger_info.log_severity = log_severity
+
+    def set_service_user_name(self, service_user_name):
+        """ Set the logSvcUsrName field of SaLogGenericLogHeaderT
+
+        Args:
+            service_user_name (str): Logger's service user name
+        """
+        self.logger_info.log_service_user_name = service_user_name
+
+    @bad_handle_retry
      def log(self, message):
-        """ Write a log record to the system log stream
+        """ Write a message (log record) to a log stream
Args:
-            message (str): Message to write to system log stream
+            message (str): Message to write to the log stream
+
+        Returns:
+            SaAisErrorT: Return code of the corresponding LOG API call(s)
          """
+        header_type, log_header = self._set_log_header()
+        self.record.logTimeStamp = saAis.SA_TIME_UNKNOWN
+        self.record.logHdrType = header_type
+        self.record.logHeader = log_header
- log_buffer = ctypes.pointer(saLog.SaLogBufferT(message))
-        notice = saLog.saLog.SA_LOG_SEV_NOTICE
+        log_buffer = ctypes.pointer(SaLogBufferT(message))
          self.record.logBuffer = log_buffer
-        self.record.logHeader.genericHdr.logSeverity = notice
-        log.saLogWriteLogAsync(self.stream_handle, self.get_invocation(),
-                               saLog.saLog.SA_LOG_RECORD_WRITE_ACK,
-                               self.record)
+        rc = log.saLogWriteLogAsync(self.stream_handle,
+                                    self._get_invocation(),
+                                    saLog.SA_LOG_RECORD_WRITE_ACK,
+                                    self.record)
+        if rc != eSaAisErrorT.SA_AIS_OK:
+            log_err("saLogWriteLogAsync FAILED - %s" %
+                    eSaAisErrorT.whatis(rc))
+        else:
+            fds = [self.sel_obj.value]
+            io_evt = select(fds, [], [], 10)
+            if self.sel_obj.value in io_evt[0]:
+                rc = log.saLogDispatch(self.log_handle,
+                                       eSaDispatchFlagsT.SA_DISPATCH_ALL)
+                if rc != eSaAisErrorT.SA_AIS_OK:
+                    log_err("saLogDispatch FAILED - %s" %
+                            eSaAisErrorT.whatis(rc))
+            else:
+                self.log_write_error = eSaAisErrorT.SA_AIS_ERR_TIMEOUT
- def dispatch(self, read_fds):
-        """ Invoke pending callbacks
+        if rc == eSaAisErrorT.SA_AIS_OK and self.log_write_error == \
+                eSaAisErrorT.SA_AIS_ERR_BAD_HANDLE:
+            rc = eSaAisErrorT.SA_AIS_ERR_BAD_HANDLE
- Args:
-            read_fds (list): List of file descriptors associated with the
-                log 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
+
+    def get_log_write_result(self):
+        """ Return the result of the log write operation to the user
+
+        Returns:
+            SaAisErrorT: Result of the log write operation
          """
-        if self.sel_obj.value in read_fds:
-            log.saLogDispatch(self.log_handle,
-                              saAis.eSaDispatchFlagsT.SA_DISPATCH_ALL)
+        return self.log_write_error

------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
Opensaf-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/opensaf-devel

Reply via email to