---
 python/pyosaf/utils/immoi/__init__.py    |  275 +++++---
 python/pyosaf/utils/immoi/implementer.py | 1006 ++++++++++++++++--------------
 python/pyosaf/utils/immom/__init__.py    |  242 ++++---
 python/pyosaf/utils/immom/accessor.py    |  112 ++++
 python/pyosaf/utils/immom/agent.py       |  404 ++++++++++++
 python/pyosaf/utils/immom/ccb.py         |  455 ++++++++------
 python/pyosaf/utils/immom/iterator.py    |   95 +--
 python/pyosaf/utils/immom/object.py      |   34 +-
 8 files changed, 1697 insertions(+), 926 deletions(-)
 create mode 100644 python/pyosaf/utils/immom/accessor.py
 create mode 100644 python/pyosaf/utils/immom/agent.py

diff --git a/python/pyosaf/utils/immoi/__init__.py 
b/python/pyosaf/utils/immoi/__init__.py
index 55cb1ff..d1ff870 100644
--- a/python/pyosaf/utils/immoi/__init__.py
+++ b/python/pyosaf/utils/immoi/__init__.py
@@ -29,24 +29,24 @@ from __future__ import print_function
 from ctypes import c_char_p, c_void_p, cast, pointer
 
 from pyosaf.saAis import SaStringT, SaVersionT, SaNameT, SaSelectionObjectT, \
-    unmarshalSaStringTArray, eSaDispatchFlagsT
+    unmarshalSaStringTArray, eSaDispatchFlagsT, eSaAisErrorT
 from pyosaf import saImm, saImmOi
 from pyosaf.saImm import unmarshalSaImmValue, SaImmAttrNameT,      \
     SaImmAttrValuesT_2, SaImmClassNameT, SaImmSearchParametersT_2, \
     eSaImmValueTypeT, SaImmAttrDefinitionT_2, SaImmClassCategoryT, \
     SaImmAttrModificationT_2, eSaImmAttrModificationTypeT
 from pyosaf.saImmOi import SaImmOiHandleT, SaImmOiImplementerNameT
-from pyosaf.utils import immom
+from pyosaf.utils import immom, log_err, bad_handle_retry
 from pyosaf.utils.immom.object import ImmObject
 from pyosaf.utils.immom.ccb import marshal_c_array
 from pyosaf.utils.immom.iterator import SearchIterator
-from pyosaf.utils import decorate, initialize_decorate
+from pyosaf.utils import decorate, deprecate, initialize_decorate, SafException
 
 
-selection_object = SaSelectionObjectT()
-handle = SaImmOiHandleT()
 TRY_AGAIN_CNT = 60
 OPENSAF_IMM_OBJECT = "opensafImm=opensafImm,safApp=safImmService"
+_oi_agent = None
+
 
 # Decorate pure saImmOi* API's with error-handling retry and exception raising
 saImmOiInitialize_2 = initialize_decorate(saImmOi.saImmOiInitialize_2)
@@ -71,68 +71,169 @@ saImmOiAugmentCcbInitialize = 
decorate(saImmOi.saImmOiAugmentCcbInitialize)
 saImmOiCcbSetErrorString = decorate(saImmOi.saImmOiCcbSetErrorString)
 
 
+class OiAgent(object):
+    """ This class acts as a high-level OI agent, providing OI functions to
+    the users as a higher level, and relieving the users of the need to manage
+    the life cycle of the OI agent and providing general interface for
+    Implementer or Applier used
+    """
+    def __init__(self, callbacks=None, version=None):
+        """ Constructor for OiAgent class
+
+        Args:
+            version (SaVersionT): OI API version
+        """
+        self.handle = None
+        self.version = version if version else SaVersionT('A', 2, 15)
+        self.selection_object = None
+        self.callbacks = callbacks
+        global _oi_agent
+        _oi_agent = self
+
+    def _fetch_sel_obj(self):
+        """ Obtain a selection object (OS file descriptor)
+
+        Returns:
+            SaAisErrorT: Return code of the saImmOiSelectionObjectGet() API
+        """
+        rc = saImmOiSelectionObjectGet(self.handle, self.selection_object)
+        if rc != eSaAisErrorT.SA_AIS_OK:
+            log_err("saImmOiSelectionObjectGet FAILED - %s" %
+                    eSaAisErrorT.whatis(rc))
+
+        return rc
+
+    def initialize(self, callbacks):
+        """ Initialize the IMM OI agent
+
+        Args:
+            callbacks (SaImmOiCallbacksT_2): OI callbacks to register with IMM
+
+        Returns:
+            SaAisErrorT: Return code of OI initialize
+        """
+        self.handle = SaImmOiHandleT()
+        self.selection_object = SaSelectionObjectT()
+        if callbacks is not None:
+            self.callbacks = callbacks
+        rc = saImmOi.saImmOiInitialize_2(self.handle, self.callbacks,
+                                         self.version)
+        if rc == eSaAisErrorT.SA_AIS_OK:
+            rc = self._fetch_sel_obj()
+            if rc == eSaAisErrorT.SA_AIS_ERR_BAD_HANDLE:
+                rc = self.re_initialize(self.callbacks)
+
+        return rc
+
+    @bad_handle_retry
+    def re_initialize(self, callbacks):
+        """ Re-initialize the IMM OI agent
+
+        Args:
+            callbacks (SaImmOiCallbacksT_2): OI callbacks to register with IMM
+
+        Returns:
+            SaAisErrorT: Return code of OI initialize
+        """
+        self.finalize()
+        rc = self.initialize(callbacks=callbacks)
+        if rc != eSaAisErrorT.SA_AIS_OK:
+            log_err("saImmOiInitialize_2 FAILED - %s" %
+                    eSaAisErrorT.whatis(rc))
+        else:
+            rc = self._fetch_sel_obj()
+        return rc
+
+    def finalize(self):
+        """ Finalize IMM OM agent handle
+
+        Returns:
+            SaAisErrorT: Return code of OI finalize
+        """
+        rc = eSaAisErrorT.SA_AIS_OK
+        if self.handle:
+            rc = saImmOiFinalize(self.handle)
+            if rc != eSaAisErrorT.SA_AIS_OK:
+                log_err("saImmOiFinalize 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
+
+    def get_selection_object(self):
+        """ Return the selection object associating with the OI handle
+
+        Returns:
+            SaSelectionObjectT: Selection object associated with the OI handle
+        """
+        return self.selection_object
+
+    def dispatch(self, flags=eSaDispatchFlagsT.SA_DISPATCH_ALL):
+        """ Dispatch all queued callbacks
+
+        Args:
+            flags (eSaDispatchFlagsT): Flags specifying dispatch mode
+
+        Returns:
+            SaAisErrorT: Return code of OI dispatch
+        """
+        rc = saImmOiDispatch(self.handle, flags)
+        return rc
+
+
+# Keep old user interfaces of OI pyosaf utils
+@deprecate
 def initialize(callbacks=None):
     """ Initialize the IMM OI library
 
     Args:
         callbacks (SaImmOiCallbacksT_2): OI callbacks to register with IMM
-    """
-    version = SaVersionT('A', 2, 15)
-    saImmOiInitialize_2(handle, callbacks, version)
-
 
-def register_applier(app_name):
-    """ Register as an applier OI
-
-    Args:
-        app_name (str): Applier name
+    Raises:
+        SafException: If the return code of the corresponding OI API call(s)
+            is not SA_AIS_OK
     """
-    applier_name = "@" + app_name
-    register_implementer(applier_name)
-
-
-def register_implementer(oi_name):
-    """ Register as an implementer
-
-    Args:
-        oi_name (str): Implementer name
-    """
-    implementer_name = SaImmOiImplementerNameT(oi_name)
-    saImmOiImplementerSet(handle, implementer_name)
+    global _oi_agent
+    if _oi_agent is None:
+        _oi_agent = OiAgent()
+    rc = _oi_agent.initialize(callbacks=callbacks)
+    if rc != eSaAisErrorT.SA_AIS_OK:
+        raise SafException(rc)
 
 
+@deprecate
 def get_selection_object():
-    """ Get an selection object for event polling """
-    global selection_object
-    saImmOiSelectionObjectGet(handle, selection_object)
-
+    """ Get an selection object for event polling
 
-def implement_class(class_name):
-    """ Register the implementer as an OI for the given class
+    Returns:
+        SaSelectionObjectT: Return code of selection object get
 
-    Args:
-        class_name (str): Name of the class to implement
+    Raises:
+        SafException: If the return code of the corresponding CLM API call(s)
+            is not SA_AIS_OK
     """
-    c_class_name = SaImmClassNameT(class_name)
-    saImmOiClassImplementerSet(handle, c_class_name)
+    if not _oi_agent:
+        # SA_AIS_ERR_INIT is returned if user calls this function without first
+        # calling initialize()
+        raise SafException(eSaAisErrorT.SA_AIS_ERR_INIT)
 
+    return _oi_agent.get_selection_object()
 
-def dispatch(flags=eSaDispatchFlagsT.SA_DISPATCH_ALL):
-    """ Dispatch all queued callbacks
 
-    Args:
-        flags (eSaDispatchFlagsT): Flags specifying dispatch mode
-    """
-    saImmOiDispatch(handle, flags)
-
-
-def create_rt_object(class_name, parent_name, runtime_obj):
+def create_runtime_object(class_name, parent_name, runtime_obj):
     """ Create a runtime object
 
     Args:
         class_name (str): Class name
         parent_name (str): Parent name
         runtime_obj (ImmObject): Runtime object to create
+
+    Raises:
+        SafException: If the return code of the corresponding OI API call(s)
+            is not SA_AIS_OK
     """
     # Marshall parameters
     c_class_name = SaImmClassNameT(class_name)
@@ -166,26 +267,42 @@ def create_rt_object(class_name, parent_name, 
runtime_obj):
 
         c_attr_values.append(c_attr)
 
-    saImmOiRtObjectCreate_2(handle, c_class_name, c_parent_name, c_attr_values)
+    rc = saImmOiRtObjectCreate_2(_oi_agent.handle, c_class_name,
+                                 c_parent_name, c_attr_values)
+    if rc != eSaAisErrorT.SA_AIS_OK:
+        raise SafException(rc)
 
 
-def delete_rt_object(dn):
+@deprecate
+def delete_runtime_object(dn):
     """ Delete a runtime object
 
     Args:
         dn (str): Runtime object dn
+
+    Raises:
+        SafException: If the return code of the corresponding OI API call(s)
+            is not SA_AIS_OK
     """
     # Marshall the parameter
     c_dn = SaNameT(dn)
-    saImmOiRtObjectDelete(handle, c_dn)
+    rc = saImmOiRtObjectDelete(_oi_agent.handle, c_dn)
+    if rc != eSaAisErrorT.SA_AIS_OK:
+        raise SafException(rc)
 
 
-def update_rt_object(dn, attributes):
-    """ Update the specified object with the requested attribute modifications
+@deprecate
+def update_runtime_object(dn, attributes):
+    """ Update the specified object with the requested attribute
+    modifications
 
     Args:
         dn (str): Object dn
         attributes (dict): Dictionary of attribute modifications
+
+    Raises:
+        SafException: If the return code of the corresponding OI API call(s)
+            is not SA_AIS_OK
     """
     # Get the class name for the object
     class_name = get_class_name_for_dn(dn)
@@ -211,7 +328,9 @@ def update_rt_object(dn, attributes):
         c_attr_mod.modAttr.attrValues = marshal_c_array(attr_type, values)
         attr_mods.append(c_attr_mod)
 
-    saImmOiRtObjectUpdate_2(handle, SaNameT(dn), attr_mods)
+    rc = saImmOiRtObjectUpdate_2(_oi_agent.handle, SaNameT(dn), attr_mods)
+    if rc != eSaAisErrorT.SA_AIS_OK:
+        raise SafException(rc)
 
 
 def report_admin_operation_result(invocation_id, result):
@@ -220,8 +339,14 @@ def report_admin_operation_result(invocation_id, result):
     Args:
         invocation_id (SaInvocationT): Invocation id
         result (SaAisErrorT): Result of admin operation
+
+    Raises:
+        SafException: If the return code of the corresponding OI API call(s)
+            is not SA_AIS_OK
     """
-    saImmOiAdminOperationResult(handle, invocation_id, result)
+    rc = saImmOiAdminOperationResult(_oi_agent.handle, invocation_id, result)
+    if rc != eSaAisErrorT.SA_AIS_OK:
+        raise SafException(rc)
 
 
 def set_error_string(ccb_id, error_string):
@@ -231,11 +356,15 @@ def set_error_string(ccb_id, error_string):
     Args:
         ccb_id (SaImmOiCcbIdT): CCB id
         error_string (str): Error string
-    """
 
+    Raises:
+        SafException: If the return code of the corresponding OI API call(s)
+            is not SA_AIS_OK
+    """
     c_error_string = SaStringT(error_string)
-
-    saImmOiCcbSetErrorString(handle, ccb_id, c_error_string)
+    rc = saImmOiCcbSetErrorString(_oi_agent.handle, ccb_id, c_error_string)
+    if rc != eSaAisErrorT.SA_AIS_OK:
+        raise SafException(rc)
 
 
 def get_class_category(class_name):
@@ -245,15 +374,9 @@ def get_class_category(class_name):
         class_name (str): Class name
 
     Returns:
-        SaAisErrorT: Return code of class category get
+        SaImmClassCategoryT: Class category
     """
-    c_attr_defs = pointer(pointer(SaImmAttrDefinitionT_2()))
-    c_category = SaImmClassCategoryT()
-    c_class_name = SaImmClassNameT(class_name)
-
-    immom.saImmOmClassDescriptionGet_2(immom.handle, c_class_name, c_category,
-                                       c_attr_defs)
-    return c_category.value
+    return immom.get_class_category(class_name)
 
 
 def get_parent_name_for_dn(dn):
@@ -273,7 +396,6 @@ def get_parent_name_for_dn(dn):
 
 def get_object_names_for_class(class_name):
     """ Return instances of the given class
-    This is safe to call from OI callbacks.
 
     Args:
         class_name (str): Class name
@@ -293,9 +415,14 @@ def get_object_names_for_class(class_name):
     # Create the search iterator
     found_objs = SearchIterator(search_param=c_search_param,
                                 attribute_names=['SaImmAttrClassName'])
-
+    found_objs.init()
     # Return the dn's of found objects
-    return [obj.dn for obj in found_objs]
+    object_names = []
+    for obj in found_objs:
+        object_names.append(obj.dn)
+        print(obj.dn.value)
+
+    return object_names
 
 
 def get_class_name_for_dn(dn):
@@ -317,12 +444,6 @@ def get_class_name_for_dn(dn):
 def get_object_no_runtime(dn):
     """ Return the IMM object with the given dn
 
-    This is safe to call from OI callbacks as only the config attributes will
-    be read.
-    The function needs to query the class to find config attributes. If the
-    class name is known by the caller it can be passed in. Otherwise, it will
-    be looked up.
-
     Args:
         dn (str): Object dn
 
@@ -334,7 +455,6 @@ def get_object_no_runtime(dn):
 
 def get_attribute_type(attribute, class_name):
     """ Return the type of the attribute in the given class
-    This is safe to use from OI callbacks.
 
     Args:
         attribute (str): Attribute name
@@ -344,14 +464,14 @@ def get_attribute_type(attribute, class_name):
         str: Attribute type
     """
     class_desc = immom.class_description_get(class_name)
-    attr_desc = [attr for attr in class_desc if attr.attrName == attribute][0]
+    attr_desc = [attr for attr in class_desc if
+                 attr.attrName == attribute][0]
 
     return attr_desc.attrValueType
 
 
 def get_rdn_attribute_for_class(class_name):
     """ Return the RDN attribute for the given class
-    This is safe to call from OI callbacks.
 
     Args:
         class_name (str): Class name
@@ -400,17 +520,16 @@ def unmarshal_len_array(c_array, length, value_type):
 
 def get_available_classes_in_imm():
     """ Return a list of all available classes in IMM
-    This is safe to call from OI callbacks.
 
     Returns:
         list: List of available classes
     """
     opensaf_imm = immom.get(OPENSAF_IMM_OBJECT)
-
     return opensaf_imm.opensafImmClassNames
 
 
-def create_non_existing_imm_object(class_name, parent_name, attributes):
+def create_non_existing_imm_object(class_name, parent_name,
+                                   attributes):
     """ Create an ImmObject instance for an object that has not yet existed
     in IMM
 
diff --git a/python/pyosaf/utils/immoi/implementer.py 
b/python/pyosaf/utils/immoi/implementer.py
index 3d44c9a..f57e326 100755
--- a/python/pyosaf/utils/immoi/implementer.py
+++ b/python/pyosaf/utils/immoi/implementer.py
@@ -21,133 +21,20 @@ from __future__ import print_function
 import select
 import itertools
 
-from pyosaf.saAis import eSaAisErrorT, unmarshalNullArray
+from pyosaf.saAis import eSaAisErrorT, unmarshalNullArray, SaVersionT
 from pyosaf import saImm, saImmOi
 from pyosaf.saImm import eSaImmValueTypeT, eSaImmAttrModificationTypeT, \
-    eSaImmClassCategoryT
-from pyosaf.utils import immom, immoi, SafException
+    eSaImmClassCategoryT, SaImmClassNameT
+from pyosaf.saImmOi import SaImmOiImplementerNameT
+from pyosaf.utils import immom, immoi, SafException, decorate, bad_handle_retry
+from pyosaf.utils.immoi import OiAgent
+from pyosaf.utils.immom.object import ImmObject
 
 
-implementer_instance = None
-completed_ccbs = {}
-ccbs = {}
-
-
-def _collect_full_transaction(ccb_id):
-    """ Go through a completed CCB and summarize the full transaction as seen
-    by the OI
-
-    Args:
-        ccb_id (str): CCB id
-
-    Returns:
-    {'instances_after' : instances,
-     'created': created,
-     'deleted': deleted,
-     'updated': updated}
-    """
-    # Collect current state
-    all_objects_now = []
-
-    created = []
-    deleted = []
-    updated = []
-
-    # Go through current instances
-    for class_name in implementer_instance.class_names:
-        dns = immoi.get_object_names_for_class(class_name)
-        for dn in dns:
-            obj = immoi.get_object_no_runtime(dn)
-
-            all_objects_now.append(obj)
-
-    # Collect proposed state by applying changes on current state
-    for operation in ccbs[ccb_id]:
-        operation_type = operation['type']
-
-        # Handle object create operation
-        if operation_type == 'CREATE':
-            parent = operation['parent']
-            class_name = operation['className']
-            attributes = operation['attributes']
-            rdn_attr = immoi.get_rdn_attribute_for_class(class_name)
-            rdn_value = attributes[rdn_attr][0]
-
-            if parent:
-                dn = '%s,%s' % (rdn_value, parent)
-            else:
-                dn = rdn_value
-
-            instance = immoi.create_non_existing_imm_object(class_name, parent,
-                                                            attributes)
-            created.append(instance)
-            deleted = [obj for obj in deleted if obj.dn != dn]
-
-        # Handle object delete operation
-        elif operation_type == 'DELETE':
-            dn = operation['dn']
-            deleted.append(immom.get(dn))
-            created = [obj for obj in created if obj.dn != dn]
-            updated = [obj for obj in updated if obj.dn != dn]
-
-        # Handle object modify operation
-        elif operation_type == 'MODIFY':
-            dn = operation['dn']
-            modifications = operation['modification']
-
-            for attr_modification in modifications:
-                mod_type = attr_modification['modification']
-                attribute = attr_modification['attribute']
-                values = attr_modification['values']
-
-                # Find affected object
-                affected_instance = None
-                affected_instances = [obj for obj in all_objects_now
-                                      if obj.dn == dn]
-
-                if not affected_instances:
-                    print('ERROR: Failed to find object %s affected by modify '
-                          'operation' % dn)
-                else:
-                    affected_instance = affected_instances[0]
-                    if affected_instance not in updated:
-                        updated.append(affected_instance)
-
-                if mod_type == \
-                        eSaImmAttrModificationTypeT.SA_IMM_ATTR_VALUES_ADD:
-                    curr_value = affected_instance.__getattr__(attribute)
-                    curr_value.append(values)
-                    affected_instance.__setattr__(attribute, curr_value)
-
-                elif mod_type == \
-                        eSaImmAttrModificationTypeT.SA_IMM_ATTR_VALUES_DELETE:
-                    for value in values:
-                        curr_value = affected_instance.__getattr__(attribute)
-                        curr_value.remove(value)
-                        affected_instance.__setattr__(attribute, curr_value)
-
-                elif mod_type == \
-                        eSaImmAttrModificationTypeT.SA_IMM_ATTR_VALUES_REPLACE:
-                    affected_instance.__setattr__(attribute, values)
+saImmOiClassImplementerSet = decorate(saImmOi.saImmOiClassImplementerSet)
 
-    # Return the summary
-    instances_after = []
 
-    for modify_obj in itertools.chain(all_objects_now, created):
-        is_deleted = False
-        for obj in deleted:
-            if obj.dn == modify_obj.dn:
-                is_deleted = True
-
-        if not is_deleted:
-            instances_after.append(modify_obj)
-
-    out = {'instances_after': instances_after,
-           'created': created,
-           'deleted': deleted,
-           'updated': updated}
-
-    return out
+implementer_instance = None
 
 
 class AdminOperationParameter(object):
@@ -159,312 +46,6 @@ class AdminOperationParameter(object):
         self.value = value
 
 
-# Set up callbacks
-def admin_operation_callback(oi_handle, c_invocation_id, c_name,
-                             c_operation_id, c_params):
-    """ Callback for administrative operations
-
-    Args:
-        oi_handle (SaImmOiHandleT): OI handle
-        c_invocation_id (SaInvocationT): Invocation id
-        c_name (SaNameT): Pointer to object name
-        c_operation_id (SaImmAdminOperationIdT): Operation id
-        c_params (SaImmAdminOperationParamsT_2): Pointer to an array of
-            pointers to parameter descriptors
-    """
-    # Unmarshal parameters
-    invocation_id = c_invocation_id
-    name = \
-        saImm.unmarshalSaImmValue(c_name, eSaImmValueTypeT.SA_IMM_ATTR_SANAMET)
-    operation_id = c_operation_id
-
-    params = []
-
-    for param in unmarshalNullArray(c_params):
-        param_name = param.paramName
-        param_type = param.paramType
-        param_buffer = param.paramBuffer
-
-        value = saImm.unmarshalSaImmValue(param_buffer, param_type)
-        parameter = AdminOperationParameter(param_name, param_type, value)
-        params.append(parameter)
-
-    # Invoke the operation
-    result = implementer_instance.admin_operation(operation_id, name, params)
-
-    # Report the result
-    try:
-        immoi.report_admin_operation_result(invocation_id, result)
-    except SafException as err:
-        print("ERROR: Failed to report that %s::%s returned %s (%s)" %
-              (name, invocation_id, result, err.msg))
-
-
-def ccb_abort_callback(oi_handle, ccb_id):
-    """ Callback for aborted CCB
-    The aborted CCB will be removed from the cache.
-
-    Args:
-        oi_handle (SaImmOiHandleT): OI handle
-        ccb_id (SaImmOiCcbIdT): CCB id
-    """
-    del ccbs[ccb_id]
-
-
-def ccb_apply_callback(oi_handle, ccb_id):
-    """ Callback for apply of CCBs
-
-    Args:
-        oi_handle (SaImmOiHandleT): OI handle
-        ccb_id (SaImmOiCcbIdT): CCB id
-    """
-    all_instances = []
-
-    for class_name in implementer_instance.class_names:
-        dns = immoi.get_object_names_for_class(class_name)
-        for dn in dns:
-            obj = immoi.get_object_no_runtime(dn)
-            all_instances.append(obj)
-
-    updated = completed_ccbs[ccb_id]['updated']
-    created = completed_ccbs[ccb_id]['added']
-    deleted = completed_ccbs[ccb_id]['removed']
-
-    # Remove the CCB from the caches
-    del ccbs[ccb_id]
-    del completed_ccbs[ccb_id]
-
-    # Tell the implementer to apply the changes
-    return implementer_instance.on_apply(all_instances, updated,
-                                         created, deleted)
-
-
-def attr_update_callback(oi_handle, c_name, c_attr_names):
-    """ Callback for attribute update operation
-
-    Args:
-        oi_handle (SaImmOiHandleT): OI handle
-        c_name (SaNameT): Pointer to object name
-        c_attr_names (SaImmAttrNameT): Pointer to array of attribute names
-
-    Returns:
-        SaAisErrorT: Return code of the attribute update callback
-    """
-    # Unmarshal parameters
-    name = \
-        saImm.unmarshalSaImmValue(c_name, eSaImmValueTypeT.SA_IMM_ATTR_SANAMET)
-    attr_names = unmarshalNullArray(c_attr_names)
-
-    # Get the class of the object
-    class_name = immoi.get_class_name_for_dn(name)
-
-    # Get the values from the user and report back
-    attributes = {}
-
-    for attr_name in attr_names:
-        values = implementer_instance.on_runtime_values_get(name, class_name,
-                                                            attr_name)
-        if values is None:
-            return eSaAisErrorT.SA_AIS_ERR_UNAVAILABLE
-        if not isinstance(values, list):
-            values = [values]
-
-        attributes[attr_name] = values
-
-    # Report the updated values for the attributes
-    try:
-        immoi.update_rt_object(name, attributes)
-        return eSaAisErrorT.SA_AIS_OK
-    except SafException:
-        return eSaAisErrorT.SA_AIS_ERR_FAILED_OPERATION
-
-
-def ccb_delete_callback(oi_handle, ccb_id, c_name):
-    """ Callback for object delete operation
-
-    Args:
-        oi_handle (SaImmOiHandleT): OI handle
-        ccb_id (SaImmOiCcbIdT): CCB id
-        c_name (SaNameT): Pointer to object name
-
-    Returns:
-        SaAisErrorT: Return code of the object delete callback
-    """
-    # Unmarshal the parameters
-    name = \
-        saImm.unmarshalSaImmValue(c_name, eSaImmValueTypeT.SA_IMM_ATTR_SANAMET)
-
-    # Create a new CCB in the cache if needed
-    if ccb_id not in list(ccbs.keys()):
-        ccbs[ccb_id] = []
-
-    # Cache the operation
-    ccbs[ccb_id].append({'type': 'DELETE',
-                         'dn': name})
-
-    # Tell the implementer about the operation
-    return implementer_instance.on_delete_added(name)
-
-
-def ccb_modify_callback(oi_handle, ccb_id, c_name, c_attr_modification):
-    """ Callback for object modify operation
-
-    Args:
-        oi_handle (SaImmOiHandleT): OI handle
-        ccb_id (SaImmOiCcbIdT): CCB id
-        c_name (SaNameT): Pointer to object name
-        c_attr_modification (SaImmAttrModificationT_2): Pointer to an array of
-            pointers to descriptors of the modifications to perform
-
-    Returns:
-        SaAisErrorT: Return code of the object modify callback
-    """
-    # Unmarshal the parameters
-    name = \
-        saImm.unmarshalSaImmValue(c_name, eSaImmValueTypeT.SA_IMM_ATTR_SANAMET)
-
-    attribute_modifications = []
-    implementer_objection = None
-
-    for attr in unmarshalNullArray(c_attr_modification):
-        attr_name = attr.modAttr.attrName
-        attr_type = attr.modAttr.attrValueType
-        mod_type = attr.modType
-        attr_values = immoi.unmarshal_len_array(attr.modAttr.attrValues,
-                                                attr.modAttr.attrValuesNumber,
-                                                attr.modAttr.attrValueType)
-        attribute_modifications.append({'attribute': attr_name,
-                                        'type': attr_type,
-                                        'modification': mod_type,
-                                        'values': attr_values})
-
-        # Tell the implementer about the modification
-        result = implementer_instance.on_modify_added(attr_name, mod_type,
-                                                      attr_values)
-        if result != eSaAisErrorT.SA_AIS_OK:
-            implementer_objection = result
-
-    # Create a new CCB in the cache if needed
-    if ccb_id not in list(ccbs.keys()):
-        ccbs[ccb_id] = []
-
-    # Store the modifications in the cache
-    ccbs[ccb_id].append({'type': 'MODIFY',
-                         'dn': name,
-                         'modification': attribute_modifications})
-
-    # Respond and say if this is accepted by the implementer
-    if implementer_objection:
-        return implementer_objection
-
-    return eSaAisErrorT.SA_AIS_OK
-
-
-def ccb_create_callback(oi_handle, ccb_id, class_name,
-                        c_parent, c_attr_values):
-    """ Callback for object create operation
-
-    Args:
-        oi_handle (SaImmOiHandleT): OI handle
-        ccb_id (SaImmOiCcbIdT): CCB id
-        class_name (SaImmClassNameT): Class name
-        c_parent (SaNameT): Pointer to name of object's parent
-        c_attr_values (SaImmAttrValuesT_2): Pointer to an array of pointers to
-            attribute descriptors
-
-    Returns:
-        SaAisErrorT: Return code of the object create callback
-    """
-    # Unmarshal parameters
-    parent = saImm.unmarshalSaImmValue(c_parent,
-                                       eSaImmValueTypeT.SA_IMM_ATTR_SANAMET)
-    attributes = {}
-
-    for attr in unmarshalNullArray(c_attr_values):
-        attr_name = attr.attrName
-        attr_type = attr.attrValueType
-        nr_values = attr.attrValuesNumber
-
-        attr_values = immoi.unmarshal_len_array(attr.attrValues, nr_values,
-                                                attr_type)
-        if attr_values:
-            attributes[attr_name] = attr_values
-        else:
-            attributes[attr_name] = None
-
-    # Fill in any missing attributes
-    description = immom.class_description_get(class_name)
-
-    for attribute in description:
-        if attribute.attrName not in attributes:
-            attributes[attribute.attrName] = None
-
-    # Create a new CCB in the cache if needed
-    if ccb_id not in list(ccbs.keys()):
-        ccbs[ccb_id] = []
-
-    # Cache the create operation
-    ccbs[ccb_id].append({'type': 'CREATE',
-                         'parent': parent,
-                         'className': class_name,
-                         'attributes': attributes})
-
-    # Tell the implementer about the operation
-    obj = immoi.create_non_existing_imm_object(class_name, parent, attributes)
-
-    return implementer_instance.on_create_added(class_name, parent, obj)
-
-
-def ccb_completed_callback(oi_handle, ccb_id):
-    """ Callback for completed CCB
-    Validate any configured containments and call the configured on_validate
-    function
-
-    Args:
-        oi_handle (SaImmOiHandleT): OI handle
-        ccb_id (SaImmOiCcbIdT): CCB id
-
-    Returns:
-        SaAisErrorT: Return code of the CCB-completed validation
-    """
-    # Get a summary of the changes in the CCB
-    summary = _collect_full_transaction(ccb_id)
-    instances = summary['instances_after']
-    created = summary['created']
-    deleted = summary['deleted']
-    updated = summary['updated']
-
-    # Store added, removed, updated for apply
-    completed_ccbs[ccb_id] = {'added': created,
-                              'removed': deleted,
-                              'updated': updated}
-
-    # Perform validation on the full transaction
-    return implementer_instance.validate(ccb_id, instances, updated,
-                                         created, deleted)
-
-
-# OI callbacks
-callbacks = saImmOi.SaImmOiCallbacksT_2()
-
-callbacks.saImmOiCcbAbortCallback = \
-    saImmOi.SaImmOiCcbAbortCallbackT(ccb_abort_callback)
-callbacks.saImmOiCcbApplyCallback = \
-    saImmOi.SaImmOiCcbApplyCallbackT(ccb_apply_callback)
-callbacks.saImmOiCcbCompletedCallback = \
-    saImmOi.SaImmOiCcbCompletedCallbackT(ccb_completed_callback)
-callbacks.saImmOiCcbObjectCreateCallback = \
-    saImmOi.SaImmOiCcbObjectCreateCallbackT_2(ccb_create_callback)
-callbacks.saImmOiCcbObjectDeleteCallback = \
-    saImmOi.SaImmOiCcbObjectDeleteCallbackT(ccb_delete_callback)
-callbacks.saImmOiCcbObjectModifyCallback = \
-    saImmOi.SaImmOiCcbObjectModifyCallbackT_2(ccb_modify_callback)
-callbacks.saImmOiRtAttrUpdateCallback = \
-    saImmOi.SaImmOiRtAttrUpdateCallbackT(attr_update_callback)
-callbacks.saImmOiAdminOperationCallback = \
-    saImmOi.SaImmOiAdminOperationCallbackT_2(admin_operation_callback)
-
-
 def admin_operation_decorate(class_name, op_id):
     """ Admin operation decorator
     Decorate the specified function with the provided class name and admin
@@ -588,7 +169,7 @@ class Constraints(object):
             return False
 
         # Validate containments affected by create or delete
-        deleted_objs = [immoi.get_object_no_runtime(obj) for obj in deleted]
+        deleted_objs = [immoi.get_object_no_runtime(dn) for dn in deleted]
 
         for obj in itertools.chain(created, deleted_objs):
             parent_name = immoi.get_parent_name_for_dn(obj.dn)
@@ -614,7 +195,7 @@ class Constraints(object):
             if parent_obj:
                 parent_class = parent_obj[0].class_name
             else:
-                parent_class = immoi.get_class_name_for_dn(parent_name)
+                parent_class = immoi.get_class_name_for_dn(dn=parent_name)
 
             # Ignore children where no constraint is defined for the child or
             # the parent
@@ -657,7 +238,7 @@ class Constraints(object):
                                    error_string)
 
 
-class Implementer(object):
+class Implementer(OiAgent):
     """ Class representing an object implementer """
     def __init__(self, class_names=None, name="wrapper", on_create=None,
                  on_delete=None, on_modify=None, on_validate=None,
@@ -674,30 +255,477 @@ class Implementer(object):
         self.on_runtime_values_get_cb = on_runtime_values_get
         self.admin_operations = admin_operations
         self.constraints = constraints
+        self.callbacks = None
+        self.completed_ccbs = {}
+        self.ccbs = {}
+        self.implemented_names = []
 
         global implementer_instance
         implementer_instance = self
 
+        # Register OI callbacks
+        self._register_callbacks()
+
+        # Initialize OI agent
+        self.version = SaVersionT('A', 2, 15)
+        super(Implementer, self).__init__(callbacks=self.callbacks,
+                                          version=self.version)
+
         # Initialize OI API and register as implementer for the classes
         self._register()
 
+    def _register_callbacks(self):
+        """ Register OI callbacks """
+        # OI callbacks
+        self.callbacks = saImmOi.SaImmOiCallbacksT_2()
+
+        self.callbacks.saImmOiCcbAbortCallback = \
+            saImmOi.SaImmOiCcbAbortCallbackT(self._ccb_abort_callback)
+        self.callbacks.saImmOiCcbApplyCallback = \
+            saImmOi.SaImmOiCcbApplyCallbackT(self._ccb_apply_callback)
+        self.callbacks.saImmOiCcbCompletedCallback = \
+            saImmOi.SaImmOiCcbCompletedCallbackT(self._ccb_completed_callback)
+        self.callbacks.saImmOiCcbObjectCreateCallback = \
+            saImmOi.SaImmOiCcbObjectCreateCallbackT_2(
+                self._ccb_create_callback)
+        self.callbacks.saImmOiCcbObjectDeleteCallback = \
+            saImmOi.SaImmOiCcbObjectDeleteCallbackT(self._ccb_delete_callback)
+        self.callbacks.saImmOiCcbObjectModifyCallback = \
+            saImmOi.SaImmOiCcbObjectModifyCallbackT_2(
+                self._ccb_modify_callback)
+        self.callbacks.saImmOiRtAttrUpdateCallback = \
+            saImmOi.SaImmOiRtAttrUpdateCallbackT(self._attr_update_callback)
+        self.callbacks.saImmOiAdminOperationCallback = \
+            saImmOi.SaImmOiAdminOperationCallbackT_2(
+                self._admin_operation_callback)
+
+    @staticmethod
+    def _admin_operation_callback(oi_handle, c_invocation_id, c_name,
+                                  c_operation_id, c_params):
+        """ Callback for administrative operations
+
+        Args:
+            oi_handle (SaImmOiHandleT): OI handle
+            c_invocation_id (SaInvocationT): Invocation id
+            c_name (SaNameT): Pointer to object name
+            c_operation_id (SaImmAdminOperationIdT): Operation id
+            c_params (SaImmAdminOperationParamsT_2): Pointer to an array of
+                pointers to parameter descriptors
+        """
+        # Unmarshal parameters
+        invocation_id = c_invocation_id
+        name = \
+            saImm.unmarshalSaImmValue(c_name,
+                                      eSaImmValueTypeT.SA_IMM_ATTR_SANAMET)
+        operation_id = c_operation_id
+
+        params = []
+
+        for param in unmarshalNullArray(c_params):
+            param_name = param.paramName
+            param_type = param.paramType
+            param_buffer = param.paramBuffer
+
+            value = saImm.unmarshalSaImmValue(param_buffer, param_type)
+            parameter = AdminOperationParameter(param_name, param_type, value)
+            params.append(parameter)
+
+        # Invoke the operation
+        result = implementer_instance.admin_operation(operation_id, name,
+                                                      params)
+
+        # Report the result
+        try:
+            immoi.report_admin_operation_result(invocation_id, result)
+        except SafException as err:
+            print("ERROR: Failed to report that %s::%s returned %s (%s)" %
+                  (name, invocation_id, result, err.msg))
+
+    def _ccb_abort_callback(self, oi_handle, ccb_id):
+        """ Callback for aborted CCB
+        The aborted CCB will be removed from the cache.
+
+        Args:
+            oi_handle (SaImmOiHandleT): OI handle
+            ccb_id (SaImmOiCcbIdT): CCB id
+        """
+        del self.ccbs[ccb_id]
+
+    def _ccb_apply_callback(self, oi_handle, ccb_id):
+        """ Callback for apply of CCBs
+
+        Args:
+            oi_handle (SaImmOiHandleT): OI handle
+            ccb_id (SaImmOiCcbIdT): CCB id
+        """
+        all_instances = []
+
+        for class_name in implementer_instance.class_names:
+            dns = immoi.get_object_names_for_class(class_name)
+            for dn in dns:
+                dn_in_str = str(dn.value)
+                obj = immoi.get_object_no_runtime(dn_in_str)
+                all_instances.append(obj)
+
+        updated = self.completed_ccbs[ccb_id]['updated']
+        created = self.completed_ccbs[ccb_id]['added']
+        deleted = self.completed_ccbs[ccb_id]['removed']
+
+        # Remove the CCB from the caches
+        del self.ccbs[ccb_id]
+        del self.completed_ccbs[ccb_id]
+
+        # Tell the implementer to apply the changes
+        return implementer_instance.on_apply(all_instances, updated,
+                                             created, deleted)
+
+    @staticmethod
+    def _attr_update_callback(oi_handle, c_name, c_attr_names):
+        """ Callback for attribute update operation
+
+        Args:
+            oi_handle (SaImmOiHandleT): OI handle
+            c_name (SaNameT): Pointer to object name
+            c_attr_names (SaImmAttrNameT): Pointer to array of attribute names
+
+        Returns:
+            SaAisErrorT: Return code of the attribute update callback
+        """
+        # Unmarshal parameters
+        name = \
+            saImm.unmarshalSaImmValue(c_name,
+                                      eSaImmValueTypeT.SA_IMM_ATTR_SANAMET)
+        attr_names = unmarshalNullArray(c_attr_names)
+
+        # Get the class of the object
+        class_name = immoi.get_class_name_for_dn(dn=name)
+
+        # Get the values from the user and report back
+        attributes = {}
+
+        for attr_name in attr_names:
+            values = implementer_instance.on_runtime_values_get(name,
+                                                                class_name,
+                                                                attr_name)
+            if values is None:
+                return eSaAisErrorT.SA_AIS_ERR_UNAVAILABLE
+            if not isinstance(values, list):
+                values = [values]
+
+            attributes[attr_name] = values
+
+        # Report the updated values for the attributes
+        try:
+            immoi.update_runtime_object(name, attributes)
+            return eSaAisErrorT.SA_AIS_OK
+        except SafException:
+            return eSaAisErrorT.SA_AIS_ERR_FAILED_OPERATION
+
+    def _ccb_delete_callback(self, oi_handle, ccb_id, c_name):
+        """ Callback for object delete operation
+
+        Args:
+            oi_handle (SaImmOiHandleT): OI handle
+            ccb_id (SaImmOiCcbIdT): CCB id
+            c_name (SaNameT): Pointer to object name
+
+        Returns:
+            SaAisErrorT: Return code of the object delete callback
+        """
+        # Unmarshal the parameters
+        name = \
+            saImm.unmarshalSaImmValue(c_name,
+                                      eSaImmValueTypeT.SA_IMM_ATTR_SANAMET)
+
+        # Create a new CCB in the cache if needed
+        if ccb_id not in list(self.ccbs.keys()):
+            self.ccbs[ccb_id] = []
+
+        # Cache the operation
+        self.ccbs[ccb_id].append({'type': 'DELETE', 'dn': name})
+
+        # Tell the implementer about the operation
+        return implementer_instance.on_delete_added(name)
+
+    def _ccb_modify_callback(self, oi_handle, ccb_id, c_name,
+                             c_attr_modification):
+        """ Callback for object modify operation
+
+        Args:
+            oi_handle (SaImmOiHandleT): OI handle
+            ccb_id (SaImmOiCcbIdT): CCB id
+            c_name (SaNameT): Pointer to object name
+            c_attr_modification (SaImmAttrModificationT_2): Pointer to an array
+                of pointers to descriptors of the modifications to perform
+
+        Returns:
+            SaAisErrorT: Return code of the object modify callback
+        """
+        # Unmarshal the parameters
+        name = \
+            saImm.unmarshalSaImmValue(c_name,
+                                      eSaImmValueTypeT.SA_IMM_ATTR_SANAMET)
+
+        attribute_modifications = []
+        implementer_objection = None
+
+        for attr in unmarshalNullArray(c_attr_modification):
+            attr_name = attr.modAttr.attrName
+            attr_type = attr.modAttr.attrValueType
+            mod_type = attr.modType
+            attr_values = immoi.unmarshal_len_array(
+                attr.modAttr.attrValues, attr.modAttr.attrValuesNumber,
+                attr.modAttr.attrValueType)
+            attribute_modifications.append({'attribute': attr_name,
+                                            'type': attr_type,
+                                            'modification': mod_type,
+                                            'values': attr_values})
+
+            # Tell the implementer about the modification
+            result = implementer_instance.on_modify_added(attr_name, mod_type,
+                                                          attr_values)
+            if result != eSaAisErrorT.SA_AIS_OK:
+                implementer_objection = result
+
+        # Create a new CCB in the cache if needed
+        if ccb_id not in list(self.ccbs.keys()):
+            self.ccbs[ccb_id] = []
+
+        # Store the modifications in the cache
+        self.ccbs[ccb_id].append({'type': 'MODIFY',
+                                  'dn': name,
+                                  'modification': attribute_modifications})
+
+        # Respond and say if this is accepted by the implementer
+        if implementer_objection:
+            return implementer_objection
+
+        return eSaAisErrorT.SA_AIS_OK
+
+    def _ccb_create_callback(self, oi_handle, ccb_id, class_name,
+                             c_parent, c_attr_values):
+        """ Callback for object create operation
+
+        Args:
+            oi_handle (SaImmOiHandleT): OI handle
+            ccb_id (SaImmOiCcbIdT): CCB id
+            class_name (SaImmClassNameT): Class name
+            c_parent (SaNameT): Pointer to name of object's parent
+            c_attr_values (SaImmAttrValuesT_2): Pointer to an array of pointers
+                                                to attribute descriptors
+
+        Returns:
+            SaAisErrorT: Return code of the object create callback
+        """
+        # Unmarshal parameters
+        parent = saImm.unmarshalSaImmValue(
+            c_parent, eSaImmValueTypeT.SA_IMM_ATTR_SANAMET)
+        attributes = {}
+
+        for attr in unmarshalNullArray(c_attr_values):
+            attr_name = attr.attrName
+            attr_type = attr.attrValueType
+            nr_values = attr.attrValuesNumber
+
+            attr_values = immoi.unmarshal_len_array(attr.attrValues,
+                                                    nr_values,
+                                                    attr_type)
+            if attr_values:
+                attributes[attr_name] = attr_values
+            else:
+                attributes[attr_name] = None
+
+        # Fill in any missing attributes
+        description = immom.class_description_get(class_name)
+
+        for attribute in description:
+            if attribute.attrName not in attributes:
+                attributes[attribute.attrName] = None
+
+        # Create a new CCB in the cache if needed
+        if ccb_id not in list(self.ccbs.keys()):
+            self.ccbs[ccb_id] = []
+
+        # Cache the create operation
+        self.ccbs[ccb_id].append({'type': 'CREATE',
+                                  'parent': parent,
+                                  'className': class_name,
+                                  'attributes': attributes})
+
+        # Tell the implementer about the operation
+        obj = immoi.create_non_existing_imm_object(class_name, parent,
+                                                   attributes)
+
+        return implementer_instance.on_create_added(class_name, parent, obj)
+
+    def _ccb_completed_callback(self, oi_handle, ccb_id):
+        """ Callback for completed CCB
+
+        Validate any configured containments and call the configured
+        on_validate function
+
+        Args:
+            oi_handle (SaImmOiHandleT): OI handle
+            ccb_id (SaImmOiCcbIdT): CCB id
+
+        Returns:
+            SaAisErrorT: Return code of the CCB-completed validation
+        """
+        # Get a summary of the changes in the CCB
+        summary = self._collect_full_transaction(ccb_id)
+        instances = summary['instances_after']
+        created = summary['created']
+        deleted = summary['deleted']
+        updated = summary['updated']
+
+        # Store added, removed, updated for apply
+        self.completed_ccbs[ccb_id] = {'added': created,
+                                       'removed': deleted,
+                                       'updated': updated}
+
+        # Perform validation on the full transaction
+        return implementer_instance.validate(ccb_id, instances, updated,
+                                             created, deleted)
+
+    def _collect_full_transaction(self, ccb_id):
+        """ Go through a completed CCB and summarize the full transaction as
+            seen by the OI
+
+        Args:
+            ccb_id (str): CCB id
+
+        Returns:
+        {'instances_after' : instances,
+         'created': created,
+         'deleted': deleted,
+         'updated': updated}
+        """
+        # Collect current state
+        all_objects_now = []
+
+        created = []
+        deleted = []
+        updated = []
+
+        # Go through current instances
+        for class_name in implementer_instance.class_names:
+            dns = immoi.get_object_names_for_class(class_name)
+            for dn in dns:
+                dn_in_str = str(dn.value)
+                obj = immoi.get_object_no_runtime(dn_in_str)
+                all_objects_now.append(obj)
+
+        # Collect proposed state by applying changes on current state
+        for operation in self.ccbs[ccb_id]:
+            operation_type = operation['type']
+
+            # Handle object create operation
+            if operation_type == 'CREATE':
+                parent = operation['parent']
+                class_name = operation['className']
+                attributes = operation['attributes']
+                rdn_attr = immoi.get_rdn_attribute_for_class(
+                    class_name=class_name)
+                rdn_value = attributes[rdn_attr][0]
+
+                if parent:
+                    dn = '%s,%s' % (rdn_value, parent)
+                else:
+                    dn = rdn_value
+
+                instance = immoi.create_non_existing_imm_object(
+                    class_name, parent, attributes)
+                created.append(instance)
+                deleted = [obj for obj in deleted if obj.dn != dn]
+
+            # Handle object delete operation
+            elif operation_type == 'DELETE':
+                dn = operation['dn']
+                deleted.append(ImmObject(class_name=class_name, dn=dn))
+                created = [obj for obj in created if obj.dn != dn]
+                updated = [obj for obj in updated if obj.dn != dn]
+
+            # Handle object modify operation
+            elif operation_type == 'MODIFY':
+                dn = operation['dn']
+                modifications = operation['modification']
+
+                for attr_modification in modifications:
+                    mod_type = attr_modification['modification']
+                    attribute = attr_modification['attribute']
+                    values = attr_modification['values']
+
+                    # Find affected object
+                    affected_instance = None
+                    affected_instances = [obj for obj in all_objects_now
+                                          if obj.dn == dn]
+
+                    if not affected_instances:
+                        print('ERROR: Failed to find object %s affected by '
+                              'modify operation' % dn)
+                    else:
+                        affected_instance = affected_instances[0]
+                        if affected_instance not in updated:
+                            updated.append(affected_instance)
+
+                    if mod_type == \
+                            eSaImmAttrModificationTypeT.SA_IMM_ATTR_VALUES_ADD:
+                        curr_value = affected_instance.__getattr__(attribute)
+                        curr_value.append(values)
+                        affected_instance.__setattr__(attribute, curr_value)
+
+                    elif mod_type == eSaImmAttrModificationTypeT.\
+                            SA_IMM_ATTR_VALUES_DELETE:
+                        for value in values:
+                            curr_value = affected_instance.__getattr__(
+                                attribute)
+                            curr_value.remove(value)
+                            affected_instance.__setattr__(
+                                attribute, curr_value)
+
+                    elif mod_type == eSaImmAttrModificationTypeT.\
+                            SA_IMM_ATTR_VALUES_REPLACE:
+                        affected_instance.__setattr__(attribute, values)
+
+        # Return the summary
+        instances_after = []
+
+        for modify_obj in itertools.chain(all_objects_now, created):
+            is_deleted = False
+            for obj in deleted:
+                if obj.dn == modify_obj.dn:
+                    is_deleted = True
+
+            if not is_deleted:
+                instances_after.append(modify_obj)
+
+        out = {'instances_after': instances_after,
+               'created': created,
+               'deleted': deleted,
+               'updated': updated}
+
+        return out
+
     def get_implemented_classes(self):
         """ Return a list of the classes this implementer implements
 
         Returns:
             list: List of class name
         """
-        return self.class_names
+        return self.implemented_names
 
-    def implement_class(self, class_name):
+    def set_class_implementer(self, class_name):
         """ Add the given class_name to the list of classes this implementer
         implements
 
         Args:
             class_name (str): Class name
         """
-        immoi.implement_class(class_name)
-        self.class_names.append(class_name)
+        c_class_name = SaImmClassNameT(class_name)
+        rc = saImmOi.saImmOiClassImplementerSet(self.handle, c_class_name)
+        if rc == eSaAisErrorT.SA_AIS_OK:
+            self.implemented_names.append(class_name)
+        return rc
 
     def set_constraints(self, constraints):
         """ Set constraints to be verified by the OI """
@@ -891,43 +919,60 @@ class Implementer(object):
         # Report that the operation is not supported
         return eSaAisErrorT.SA_AIS_ERR_NOT_SUPPORTED
 
+    @bad_handle_retry
     def _register(self):
         """ Initialize IMM OI and register as an OI for the configured classes
+
+        Returns:
+            SaAisErrorT: Return code of Implementer register
         """
         # Initialize the OI API
-        immoi.initialize(callbacks)
+        rc = self.initialize(callbacks=self.callbacks)
 
         # Ensure that all classes are configuration classes
-        runtime_classes = [item for item in self.class_names
-                           if immoi.get_class_category(item) ==
-                           eSaImmClassCategoryT.SA_IMM_CLASS_RUNTIME]
+        # print("self.class_names")
+        # print(self.class_names)
+        runtime_classes = None
+        if self.class_names is not None:
+            runtime_classes = [item for item in self.class_names
+                               if immoi.get_class_category(item) ==
+                               eSaImmClassCategoryT.SA_IMM_CLASS_RUNTIME]
         if runtime_classes:
             raise Exception("ERROR: Can't be an applier for runtime "
                             "classes %s" % runtime_classes)
 
         # Become an implementer
-        immoi.register_implementer(self.name)
-
-        # Get the selection objects
-        immoi.get_selection_object()
+        if rc == eSaAisErrorT.SA_AIS_OK:
+            rc = self._register_implementer(self.name)
 
         available_classes = immoi.get_available_classes_in_imm()
 
-        for class_name in self.class_names:
+        if rc == eSaAisErrorT.SA_AIS_OK:
+            if self.class_names is not None:
+                for class_name in self.class_names:
+                    if class_name in available_classes:
+                        rc = self.set_class_implementer(class_name)
+                        if rc != eSaAisErrorT.SA_AIS_OK:
+                            break
+                    else:
+                        print("WARNING: %s is missing in IMM. Not becoming "
+                              "implementer." % class_name)
+        return rc
+
+    def _register_implementer(self, oi_name):
+        """ Register as an implementer
 
-            if class_name in available_classes:
-                immoi.implement_class(class_name)
-            else:
-                print("WARNING: %s is missing in IMM. Not becoming "
-                      "implementer." % class_name)
+        Args:
+            oi_name (str): Implementer name
 
-    @staticmethod
-    def get_selection_object():
-        """ Return the selection object """
-        return immoi.selection_object.value
+        Returns:
+            SaAisErrorT: Return code of implementer set
+        """
+        implementer_name = SaImmOiImplementerNameT(oi_name)
+        rc = saImmOi.saImmOiImplementerSet(self.handle, implementer_name)
+        return rc
 
-    @staticmethod
-    def update_runtime_attributes(dn, attributes):
+    def update_runtime_attributes(self, dn, attributes):
         """ Update the given runtime attributes for the specified dn
 
         Args:
@@ -936,44 +981,47 @@ class Implementer(object):
         """
         # Report the updates
         try:
-            immoi.update_rt_object(dn, attributes)
+            immoi.update_runtime_object(dn, attributes)
         except SafException as err:
             print("ERROR: Failed to update runtime attributes of %s: %s" %
                   (dn, err))
 
-    @staticmethod
-    def create(obj):
+    def create(self, obj):
         """ Create the runtime object with provided information
 
         Args:
             obj (ImmObject): Object to create
+
+        Returns:
+            SaAisErrorT: Return code of implementer object create
         """
         # Get the parent name for the object
         parent_name = immoi.get_parent_name_for_dn(obj.dn)
         class_name = obj.class_name
 
         # Create the object
-        immoi.create_rt_object(class_name, parent_name, obj)
+        return immoi.create_runtime_object(class_name, parent_name, obj)
 
-    @staticmethod
-    def delete(dn):
+    def delete(self, dn):
         """ Delete a runtime object with the given dn
 
         Args:
             dn (str): Object dn
+
+        Returns:
+            SaAisErrorT: Return code of implementer object delete
         """
-        immoi.delete_rt_object(dn)
+        return immoi.delete_runtime_object(dn)
 
-    @staticmethod
-    def _start_dispatch_loop():
+    def _start_dispatch_loop(self):
         """ Start an infinite dispatch loop """
-        read_fds = [immoi.selection_object.value]
+        read_fds = [self.selection_object.value]
 
         # Handle updates
         while read_fds:
             read_evt, _, _ = select.select(read_fds, [], read_fds)
             if read_evt:
-                immoi.dispatch()
+                self.dispatch()
 
     def _validate_constraints(self, all_instances, updated, created, deleted):
         """ Validate configured constraints
@@ -1006,12 +1054,16 @@ class Applier(Implementer):
         """ Empty validate handler as appliers cannot validate """
         return eSaAisErrorT.SA_AIS_OK
 
+    @bad_handle_retry
     def _register(self):
         """ Initialize IMM-OI interface and register as an applier for the
         configured classes
+
+        Returns:
+            SaAisErrorT: Return code of applier register
         """
         # Initialize the OI API
-        immoi.initialize(callbacks)
+        rc = self.initialize(callbacks=self.callbacks)
 
         # Ensure that all classes are configuration classes
         runtime_classes = [item for item in self.class_names
@@ -1021,17 +1073,29 @@ class Applier(Implementer):
             raise Exception("ERROR: Can't be an applier for runtime classes %s"
                             % runtime_classes)
         # Become an applier
-        immoi.register_applier(self.name)
-
-        # Get the selection object
-        immoi.get_selection_object()
+        if rc == eSaAisErrorT.SA_AIS_OK:
+            rc = self._register_applier(self.name)
 
         # Register as applier for each class
         available_classes = immoi.get_available_classes_in_imm()
 
         for class_name in self.class_names:
             if class_name in available_classes:
-                immoi.implement_class(class_name)
+                self.set_class_implementer(class_name)
             else:
                 print("WARNING: %s is missing in IMM. Not becoming applier." %
                       class_name)
+        return rc
+
+    def _register_applier(self, app_name):
+        """ Register as an applier OI
+
+        Args:
+            app_name (str): Applier name
+
+        Returns:
+            SaAisErrorT: Return code of applier set
+        """
+        applier_name = "@" + app_name
+        rc = self._register_implementer(applier_name)
+        return rc
diff --git a/python/pyosaf/utils/immom/__init__.py 
b/python/pyosaf/utils/immom/__init__.py
index 922ce7a..646e935 100644
--- a/python/pyosaf/utils/immom/__init__.py
+++ b/python/pyosaf/utils/immom/__init__.py
@@ -16,70 +16,69 @@
 #
 ############################################################################
 """ IMM OM common utilities """
-import os
-from ctypes import pointer, POINTER
-
-from pyosaf.saAis import saAis, SaVersionT, SaNameT, SaStringT, SaAisErrorT, \
-    eSaAisErrorT, eSaBoolT, unmarshalNullArray
-from pyosaf import saImmOm, saImm
-from pyosaf.saImm import eSaImmScopeT, unmarshalSaImmValue, SaImmAttrNameT, \
-    SaImmAttrValuesT_2
-from pyosaf.saImmOm import SaImmAccessorHandleT
-from pyosaf.utils import decorate, initialize_decorate
-from pyosaf.utils import SafException
-from pyosaf.utils.immom.object import ImmObject
+from pyosaf.saAis import SaAisErrorT, eSaAisErrorT
 
+from pyosaf.utils import deprecate, SafException
+from pyosaf.utils.immom import agent
+from pyosaf.utils.immom.object import ImmObject
+from pyosaf.utils.immom.accessor import ImmOmAccessor
 
-handle = saImmOm.SaImmHandleT()
-accessor_handle = SaImmAccessorHandleT()
 
 # Decorate pure saImmOm* API's with error-handling retry and exception raising
-saImmOmInitialize = initialize_decorate(saImmOm.saImmOmInitialize)
-saImmOmSelectionObjectGet = decorate(saImmOm.saImmOmSelectionObjectGet)
-saImmOmDispatch = decorate(saImmOm.saImmOmDispatch)
-saImmOmFinalize = decorate(saImmOm.saImmOmFinalize)
-saImmOmClassCreate_2 = decorate(saImmOm.saImmOmClassCreate_2)
-saImmOmClassDescriptionGet_2 = decorate(saImmOm.saImmOmClassDescriptionGet_2)
+saImmOmInitialize = agent.saImmOmInitialize
+saImmOmSelectionObjectGet = agent.saImmOmSelectionObjectGet
+saImmOmDispatch = agent.saImmOmDispatch
+saImmOmFinalize = agent.saImmOmFinalize
+saImmOmClassCreate_2 = agent.saImmOmClassCreate_2
+saImmOmClassDescriptionGet_2 = agent.saImmOmClassDescriptionGet_2
 saImmOmClassDescriptionMemoryFree_2 = \
-    decorate(saImmOm.saImmOmClassDescriptionMemoryFree_2)
-saImmOmClassDelete = decorate(saImmOm.saImmOmClassDelete)
-saImmOmSearchInitialize_2 = decorate(saImmOm.saImmOmSearchInitialize_2)
-saImmOmSearchNext_2 = decorate(saImmOm.saImmOmSearchNext_2)
-saImmOmSearchFinalize = decorate(saImmOm.saImmOmSearchFinalize)
-saImmOmAccessorInitialize = decorate(saImmOm.saImmOmAccessorInitialize)
-saImmOmAccessorGet_2 = decorate(saImmOm.saImmOmAccessorGet_2)
-saImmOmAccessorFinalize = decorate(saImmOm.saImmOmAccessorFinalize)
-saImmOmAdminOwnerInitialize = decorate(saImmOm.saImmOmAdminOwnerInitialize)
-saImmOmAdminOwnerSet = decorate(saImmOm.saImmOmAdminOwnerSet)
-saImmOmAdminOwnerRelease = decorate(saImmOm.saImmOmAdminOwnerRelease)
-saImmOmAdminOwnerFinalize = decorate(saImmOm.saImmOmAdminOwnerFinalize)
-saImmOmAdminOwnerClear = decorate(saImmOm.saImmOmAdminOwnerClear)
-saImmOmCcbInitialize = decorate(saImmOm.saImmOmCcbInitialize)
-saImmOmCcbObjectCreate_2 = decorate(saImmOm.saImmOmCcbObjectCreate_2)
-saImmOmCcbObjectDelete = decorate(saImmOm.saImmOmCcbObjectDelete)
-saImmOmCcbObjectModify_2 = decorate(saImmOm.saImmOmCcbObjectModify_2)
-saImmOmCcbApply = decorate(saImmOm.saImmOmCcbApply)
-saImmOmCcbFinalize = decorate(saImmOm.saImmOmCcbFinalize)
-saImmOmCcbGetErrorStrings = decorate(saImmOm.saImmOmCcbGetErrorStrings)
-saImmOmAdminOperationInvoke_2 = decorate(saImmOm.saImmOmAdminOperationInvoke_2)
-saImmOmAdminOperationInvokeAsync_2 = \
-    decorate(saImmOm.saImmOmAdminOperationInvokeAsync_2)
-saImmOmAdminOperationContinue = decorate(saImmOm.saImmOmAdminOperationContinue)
-saImmOmAdminOperationContinueAsync = \
-    decorate(saImmOm.saImmOmAdminOperationContinueAsync)
+    agent.saImmOmClassDescriptionMemoryFree_2
+saImmOmClassDelete = agent.saImmOmClassDelete
+saImmOmSearchInitialize_2 = agent.saImmOmSearchInitialize_2
+saImmOmSearchNext_2 = agent.saImmOmSearchNext_2
+saImmOmSearchFinalize = agent.saImmOmSearchFinalize
+saImmOmAccessorInitialize = agent.saImmOmAccessorInitialize
+saImmOmAccessorGet_2 = agent.saImmOmAccessorGet_2
+saImmOmAccessorFinalize = agent.saImmOmAccessorFinalize
+saImmOmAdminOwnerInitialize = agent.saImmOmAdminOwnerInitialize
+saImmOmAdminOwnerSet = agent.saImmOmAdminOwnerSet
+saImmOmAdminOwnerRelease = agent.saImmOmAdminOwnerRelease
+saImmOmAdminOwnerFinalize = agent.saImmOmAdminOwnerFinalize
+saImmOmAdminOwnerClear = agent.saImmOmAdminOwnerClear
+saImmOmCcbInitialize = agent.saImmOmCcbInitialize
+saImmOmCcbObjectCreate_2 = agent.saImmOmCcbObjectCreate_2
+saImmOmCcbObjectDelete = agent.saImmOmCcbObjectDelete
+saImmOmCcbObjectModify_2 = agent.saImmOmCcbObjectModify_2
+saImmOmCcbApply = agent.saImmOmCcbApply
+saImmOmCcbFinalize = agent.saImmOmCcbFinalize
+saImmOmCcbGetErrorStrings = agent.saImmOmCcbGetErrorStrings
+saImmOmAdminOperationInvoke_2 = agent.saImmOmAdminOperationInvoke_2
+saImmOmAdminOperationInvokeAsync_2 = agent.saImmOmAdminOperationInvokeAsync_2
+saImmOmAdminOperationContinue = agent.saImmOmAdminOperationContinue
+saImmOmAdminOperationContinueAsync = agent.saImmOmAdminOperationContinueAsync
 saImmOmAdminOperationContinuationClear = \
-    decorate(saImmOm.saImmOmAdminOperationContinuationClear)
+    agent.saImmOmAdminOperationContinuationClear
 
+_om_agent = None
 
+
+@deprecate
 def initialize():
-    """ Initialize the IMM OM library """
-    version = SaVersionT('A', 2, 15)
-    # Initialize IMM OM handle
-    saImmOmInitialize(handle, None, version)
-    # Initialize Accessor Handle
-    saImmOmAccessorInitialize(handle, accessor_handle)
+    """ Initialize the IMM OM library
 
+    Raises:
+        SafException: If the return code of the corresponding CLM API call(s)
+            is not SA_AIS_OK
+    """
+    global _om_agent
+    _om_agent = agent.ImmOmAgent()
 
+    # Initialize IMM OM handle and return the API return code
+    rc = _om_agent.init()
+    if rc != eSaAisErrorT.SA_AIS_OK:
+        raise SafException(rc)
+
+@deprecate
 def get(object_name, attr_name_list=None, class_name=None):
     """ Obtain values of some attributes of the specified object
 
@@ -90,41 +89,22 @@ def get(object_name, attr_name_list=None, class_name=None):
 
     Returns:
         ImmObject: Imm object
+
+    Raises:
+        SafException: If the return code of the corresponding CLM API call(s)
+            is not SA_AIS_OK
     """
-    # Always request the SaImmAttrClassName attribute if needed
-    if attr_name_list and not class_name \
-            and 'SaImmAttrClassName' not in attr_name_list \
-            and not attr_name_list == ['SA_IMM_SEARCH_GET_CONFIG_ATTR']:
-        attr_name_list.append('SaImmAttrClassName')
-
-    attr_names = [SaImmAttrNameT(attr) for attr in attr_name_list] \
-        if attr_name_list else None
-
-    attributes = pointer(pointer(SaImmAttrValuesT_2()))
-
-    try:
-        saImmOmAccessorGet_2(accessor_handle, SaNameT(object_name),
-                             attr_names, attributes)
-    except SafException as err:
-        if err.value == eSaAisErrorT.SA_AIS_ERR_NOT_EXIST:
-            return None
-        else:
-            raise err
-
-    attrs = {}
-    attr_list = unmarshalNullArray(attributes)
-    for attr in attr_list:
-        attr_range = list(range(attr.attrValuesNumber))
-        attrs[attr.attrName] = [attr.attrValueType,
-                                [unmarshalSaImmValue(attr.attrValues[val],
-                                                     attr.attrValueType)
-                                 for val in attr_range]]
-    if 'SaImmAttrClassName' not in attrs and class_name:
-        attrs['SaImmAttrClassName'] = class_name
-
-    return ImmObject(object_name, attrs)
+    _accessor = ImmOmAccessor()
+    _accessor.init()
+    rc, imm_object = _accessor.get(object_name, attr_name_list, class_name)
+
+    if rc != eSaAisErrorT.SA_AIS_OK:
+        raise SafException(rc)
 
+    return imm_object
 
+
+@deprecate
 def class_description_get(class_name):
     """ Get class description as a Python list
 
@@ -133,14 +113,26 @@ def class_description_get(class_name):
 
     Returns:
         list: List of class attributes
+
+    Raises:
+        SafException: If the return code of the corresponding CLM API call(s)
+            is not SA_AIS_OK
     """
-    attr_defs = pointer(pointer(saImm.SaImmAttrDefinitionT_2()))
-    category = saImm.SaImmClassCategoryT()
-    saImmOmClassDescriptionGet_2(handle, class_name, category, attr_defs)
+    class_attrs = []
+    if not _om_agent:
+        # SA_AIS_ERR_INIT is returned if user calls this function without first
+        # calling initialize()
+        rc = eSaAisErrorT.SA_AIS_ERR_INIT
+    else:
+        rc, class_attrs = _om_agent.get_class_description(class_name)
+
+    if rc != eSaAisErrorT.SA_AIS_OK:
+        raise SafException(rc)
 
-    return unmarshalNullArray(attr_defs)
+    return class_attrs
 
 
+@deprecate
 def admin_op_invoke(dn, op_id, params=None):
     """ Invoke admin op for dn
 
@@ -148,61 +140,53 @@ def admin_op_invoke(dn, op_id, params=None):
         dn (str): Object dn
         op_id (str): Operation id
         params (list): List of parameters
-    """
-    owner_handle = saImmOm.SaImmAdminOwnerHandleT()
-    owner_name = saImmOm.SaImmAdminOwnerNameT(os.getlogin())
-    saImmOmAdminOwnerInitialize(handle, owner_name, eSaBoolT.SA_TRUE,
-                                owner_handle)
-    index = dn.rfind(",")
-    parent_name = SaNameT(dn[index+1:])
-    object_names = [parent_name]
-    saImmOmAdminOwnerSet(owner_handle, object_names,
-                         eSaImmScopeT.SA_IMM_SUBTREE)
-    if params is None:
-        params = []
-
-    object_dn = SaNameT(dn)
-    retval = SaAisErrorT()
-
-    saImmOmAdminOperationInvoke_2(owner_handle, object_dn, 0, op_id, params,
-                                  retval, saAis.SA_TIME_ONE_SECOND * 10)
 
-    if retval.value != eSaAisErrorT.SA_AIS_OK:
-        print("saImmOmAdminOperationInvoke_2: %s" %
-              eSaAisErrorT.whatis(retval.value))
-        raise SafException(retval.value)
+    Raises:
+        SafException: If the return code of the corresponding CLM API call(s)
+            is not SA_AIS_OK
+    """
+    if not _om_agent:
+        # SA_AIS_ERR_INIT is returned if user calls this function without first
+        # calling initialize()
+        rc = eSaAisErrorT.SA_AIS_ERR_INIT
+    else:
+        rc = _om_agent.invoke_admin_operation(dn, op_id, params)
 
-    saImmOmAdminOwnerFinalize(owner_handle)
+    if rc != eSaAisErrorT.SA_AIS_OK:
+        raise SafException(rc)
 
 
-def get_error_strings(ccb_handle):
-    """ Return the current CCB error strings
+@deprecate
+def get_rdn_attribute_for_class(class_name):
+    """ Return the RDN attribute for the given class
+    This is safe to call from OI callbacks.
 
     Args:
-        ccb_handle (str): CCB handle
+        class_name (str): Class name
 
     Returns:
-        list: List of error strings
+        str: RDN attribute of the class
     """
-    c_strings = POINTER(SaStringT)()
+    if not _om_agent:
+        # SA_AIS_ERR_INIT is returned if user calls this function without first
+        # calling initialize()
+        return None
 
-    saImmOmCcbGetErrorStrings(ccb_handle, c_strings)
+    return _om_agent.get_rdn_attribute_for_class(class_name)
 
-    return unmarshalNullArray(c_strings)
 
-
-def get_rdn_attribute_for_class(class_name):
-    """ Return the RDN attribute for the given class
-    This is safe to call from OI callbacks.
+def get_class_category(class_name):
+    """ Return the category of the given class
 
     Args:
         class_name (str): Class name
 
     Returns:
-        str: RDN attribute of the class
+        SaImmClassCategoryT: Class category
     """
-    desc = class_description_get(class_name)
-    for attr_desc in desc:
-        if attr_desc.attrFlags & saImm.saImm.SA_IMM_ATTR_RDN:
-            return attr_desc.attrName
-    return None
+    if not _om_agent:
+        # SA_AIS_ERR_INIT is returned if user calls this function without first
+        # calling initialize()
+        return ""
+
+    return _om_agent.get_class_category(class_name)
diff --git a/python/pyosaf/utils/immom/accessor.py 
b/python/pyosaf/utils/immom/accessor.py
new file mode 100644
index 0000000..51bfd52
--- /dev/null
+++ b/python/pyosaf/utils/immom/accessor.py
@@ -0,0 +1,112 @@
+############################################################################
+#
+# (C) Copyright 2014 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
+#
+############################################################################
+""" IMM OM common utilities """
+from ctypes import pointer
+
+from pyosaf.saAis import SaNameT, eSaAisErrorT, unmarshalNullArray
+from pyosaf.saImm import unmarshalSaImmValue, SaImmAttrNameT, \
+    SaImmAttrValuesT_2
+from pyosaf.saImmOm import SaImmAccessorHandleT
+
+from pyosaf.utils import bad_handle_retry, log_err
+from pyosaf.utils.immom import agent
+from pyosaf.utils.immom.object import ImmObject
+
+
+class ImmOmAccessor(agent.ImmOmAgentManager):
+    """ ImmOmAccessor class """
+    def __init__(self, version=None):
+        """ Constructor for ImmOmAdminOwner class
+
+        Args:
+            version (SaVersionT): IMM OM version
+        """
+        super(ImmOmAccessor, self).__init__(version)
+        self.accessor_handle = None
+
+    def __exit__(self, exception_type, exception_value, traceback):
+        """ Destructor for ImmOmAccessor class
+
+        Finalize the Accessor handle and the IMM OM handle
+        """
+        if self.accessor_handle:
+            agent.saImmOmAccessorFinalize(self.accessor_handle)
+        if self.handle:
+            agent.saImmOmFinalize(self.handle)
+
+    @bad_handle_retry
+    def init(self):
+        """ Initialize Accessor """
+        self.finalize()
+        rc = self.initialize()
+        if rc == eSaAisErrorT.SA_AIS_OK:
+            self.accessor_handle = SaImmAccessorHandleT()
+            # Initialize Accessor Handle
+            rc = agent.saImmOmAccessorInitialize(self.handle,
+                                                 self.accessor_handle)
+            if rc != eSaAisErrorT.SA_AIS_OK:
+                log_err("saImmOmAccessorInitialize FAILED - %s" %
+                        eSaAisErrorT.whatis(rc))
+        return rc
+
+    @bad_handle_retry
+    def get(self, object_name, attr_name_list=None, class_name=None):
+        """ Obtain values of some attributes of the specified object
+
+        Args:
+            object_name (str): Object name
+            attr_name_list (list): List of attributes
+            class_name (str): Class name
+
+        Returns:
+            ImmObject: Imm object
+        """
+        imm_obj = None
+        # Always request the SaImmAttrClassName attribute if needed
+        if attr_name_list and not class_name \
+                and 'SaImmAttrClassName' not in attr_name_list \
+                and not attr_name_list == \
+                    ['SA_IMM_SEARCH_GET_CONFIG_ATTR']:
+            attr_name_list.append('SaImmAttrClassName')
+
+        attr_names = [SaImmAttrNameT(attr) for attr in attr_name_list] \
+            if attr_name_list else None
+
+        attributes = pointer(pointer(SaImmAttrValuesT_2()))
+
+        rc = agent.saImmOmAccessorGet_2(self.accessor_handle,
+                                        SaNameT(object_name),
+                                        attr_names, attributes)
+        if rc != eSaAisErrorT.SA_AIS_OK:
+            log_err("saImmOmAccessorGet_2 FAILED - %s" %
+                    eSaAisErrorT.whatis(rc))
+        else:
+            attrs = {}
+            attr_list = unmarshalNullArray(attributes)
+            for attr in attr_list:
+                attr_range = list(range(attr.attrValuesNumber))
+                attrs[attr.attrName] = [attr.attrValueType,
+                                        [unmarshalSaImmValue(
+                                            attr.attrValues[val],
+                                            attr.attrValueType)
+                                         for val in attr_range]]
+            if 'SaImmAttrClassName' not in attrs and class_name:
+                attrs['SaImmAttrClassName'] = class_name
+            imm_obj = ImmObject(object_name, attrs)
+
+        return rc, imm_obj
diff --git a/python/pyosaf/utils/immom/agent.py 
b/python/pyosaf/utils/immom/agent.py
new file mode 100644
index 0000000..6422f01
--- /dev/null
+++ b/python/pyosaf/utils/immom/agent.py
@@ -0,0 +1,404 @@
+############################################################################
+#
+# (C) Copyright 2014 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
+#
+############################################################################
+""" IMM OM common utilities """
+import uuid
+from copy import deepcopy
+from ctypes import pointer
+
+from pyosaf import saImmOm, saImm
+from pyosaf.saAis import saAis, SaVersionT, SaNameT, SaAisErrorT, \
+    eSaAisErrorT, eSaBoolT, unmarshalNullArray
+from pyosaf.saImm import eSaImmScopeT
+from pyosaf.utils import decorate, initialize_decorate, log_err
+
+# Decorate pure saImmOm* API's with error-handling retry and exception raising
+saImmOmInitialize = initialize_decorate(saImmOm.saImmOmInitialize)
+saImmOmSelectionObjectGet = decorate(saImmOm.saImmOmSelectionObjectGet)
+saImmOmDispatch = decorate(saImmOm.saImmOmDispatch)
+saImmOmFinalize = decorate(saImmOm.saImmOmFinalize)
+saImmOmClassCreate_2 = decorate(saImmOm.saImmOmClassCreate_2)
+saImmOmClassDescriptionGet_2 = decorate(saImmOm.saImmOmClassDescriptionGet_2)
+saImmOmClassDescriptionMemoryFree_2 = \
+    decorate(saImmOm.saImmOmClassDescriptionMemoryFree_2)
+saImmOmClassDelete = decorate(saImmOm.saImmOmClassDelete)
+saImmOmSearchInitialize_2 = decorate(saImmOm.saImmOmSearchInitialize_2)
+saImmOmSearchNext_2 = decorate(saImmOm.saImmOmSearchNext_2)
+saImmOmSearchFinalize = decorate(saImmOm.saImmOmSearchFinalize)
+saImmOmAccessorInitialize = decorate(saImmOm.saImmOmAccessorInitialize)
+saImmOmAccessorGet_2 = decorate(saImmOm.saImmOmAccessorGet_2)
+saImmOmAccessorFinalize = decorate(saImmOm.saImmOmAccessorFinalize)
+saImmOmAdminOwnerInitialize = decorate(saImmOm.saImmOmAdminOwnerInitialize)
+saImmOmAdminOwnerSet = decorate(saImmOm.saImmOmAdminOwnerSet)
+saImmOmAdminOwnerRelease = decorate(saImmOm.saImmOmAdminOwnerRelease)
+saImmOmAdminOwnerFinalize = decorate(saImmOm.saImmOmAdminOwnerFinalize)
+saImmOmAdminOwnerClear = decorate(saImmOm.saImmOmAdminOwnerClear)
+saImmOmCcbInitialize = decorate(saImmOm.saImmOmCcbInitialize)
+saImmOmCcbObjectCreate_2 = decorate(saImmOm.saImmOmCcbObjectCreate_2)
+saImmOmCcbObjectDelete = decorate(saImmOm.saImmOmCcbObjectDelete)
+saImmOmCcbObjectModify_2 = decorate(saImmOm.saImmOmCcbObjectModify_2)
+saImmOmCcbApply = decorate(saImmOm.saImmOmCcbApply)
+saImmOmCcbFinalize = decorate(saImmOm.saImmOmCcbFinalize)
+saImmOmCcbGetErrorStrings = decorate(saImmOm.saImmOmCcbGetErrorStrings)
+saImmOmAdminOperationInvoke_2 = decorate(saImmOm.saImmOmAdminOperationInvoke_2)
+saImmOmAdminOperationInvokeAsync_2 = \
+    decorate(saImmOm.saImmOmAdminOperationInvokeAsync_2)
+saImmOmAdminOperationContinue = decorate(saImmOm.saImmOmAdminOperationContinue)
+saImmOmAdminOperationContinueAsync = \
+    decorate(saImmOm.saImmOmAdminOperationContinueAsync)
+saImmOmAdminOperationContinuationClear = \
+    decorate(saImmOm.saImmOmAdminOperationContinuationClear)
+
+
+class ImmOmAgentManager(object):
+    """ This class manages the life cycle of an IMM OM agent
+    """
+    def __init__(self, version=None):
+        """ Constructor for ImmOmAgentManager class
+
+        Args:
+            version (SaVersionT): IMM OM API version
+        """
+        self.init_version = version if version else SaVersionT('A', 2, 15)
+        self.version = None
+        self.handle = None
+        self.callbacks = None
+        self.selection_object = None
+
+    def __exit__(self, exception_type, exception_value, traceback):
+        """ Destructor for ImmOmAgent class
+
+        Finalize the IMM OM agent handle
+        """
+        if self.handle:
+            saImmOmFinalize(self.handle)
+
+    def __del__(self):
+        """ Destructor for ImmOmAgent class
+
+        Finalize the IMM OM agent handle
+        """
+        if self.handle:
+            saImmOmFinalize(self.handle)
+
+    def initialize(self):
+        """ Initialize the IMM OM library
+
+        Returns:
+            SaAisErrorT: Return code of OM initialize
+        """
+        self.handle = saImmOm.SaImmHandleT()
+        self.version = deepcopy(self.init_version)
+        rc = saImmOmInitialize(self.handle, None, self.version)
+        if rc != eSaAisErrorT.SA_AIS_OK:
+            log_err("saImmOmInitialize FAILED - %s" % eSaAisErrorT.whatis(rc))
+        return rc
+
+    def get_handle(self):
+        """ Return the IMM OM agent handle successfully initialized
+
+        Returns:
+            SaImmHandleT: IMM OM agent handle
+        """
+        return self.handle
+
+    def dispatch(self, flags):
+        """ Invoke IMM callbacks for queued events
+
+        Args:
+            flags (eSaDispatchFlagsT): Flags specifying dispatch mode
+
+        Returns:
+            SaAisErrorT: Return code of the saImmOmDispatch() API call
+        """
+        rc = saImmOmDispatch(self.handle, flags)
+        if rc != eSaAisErrorT.SA_AIS_OK:
+            log_err("saImmOmDispatch FAILED - %s" % eSaAisErrorT.whatis(rc))
+
+        return rc
+
+    def finalize(self):
+        """ Finalize the IMM OM agent handle
+
+        Returns:
+            SaAisErrorT: Return code of the saImmOmFinalize() API call
+        """
+        rc = eSaAisErrorT.SA_AIS_OK
+        if self.handle:
+            rc = saImmOmFinalize(self.handle)
+            if rc != eSaAisErrorT.SA_AIS_OK:
+                log_err("saImmOmFinalize FAILED - %s" %
+                        eSaAisErrorT.whatis(rc))
+            elif rc == eSaAisErrorT.SA_AIS_OK \
+                    or rc == eSaAisErrorT.SA_AIS_ERR_BAD_HANDLE:
+                # If the Finalize() call returned BAD_HANDLE, the handle should
+                # already become stale and invalid, so we reset it anyway.
+                self.handle = None
+        return rc
+
+
+class ImmOmAgent(ImmOmAgentManager):
+    """ This class acts as a high-level IMM OM agent, providing IMM OM
+    functions to the users at a higher level, and relieving the users of the
+    need to manage the life cycle of the IMM OM agent """
+
+    def init(self):
+        """ Initialize the IMM OM agent
+
+        Returns:
+            SaAisErrorT: Return code of the corresponding CLM API call(s)
+        """
+        # Clean previous resource if any
+        self.finalize()
+        return self.initialize()
+
+    def clear_admin_owner(self, obj_name, scope=eSaImmScopeT.SA_IMM_SUBTREE):
+        """ Clear the administrative owner for the set of object identified
+        by the scope and obj_name parameters
+
+        Args:
+            obj_name (str): Object name
+            scope (SaImmScopeT): Scope of the clear operation
+
+        Returns:
+            SaAisErrorT: Return code of the corresponding API call(s)
+        """
+        if not obj_name:
+            rc = eSaAisErrorT.SA_AIS_ERR_NOT_EXIST
+        else:
+            obj_name = SaNameT(obj_name)
+            obj_name = [obj_name]
+
+            rc = saImmOmAdminOwnerClear(self.handle, obj_name, scope)
+            if rc != eSaAisErrorT.SA_AIS_OK:
+                log_err("saImmOmAdminOwnerClear FAILED - %s" %
+                        eSaAisErrorT.whatis(rc))
+
+        return rc
+
+    def get_class_description(self, class_name):
+        """ Get class description as a Python list
+
+        Args:
+            class_name (str): Class name
+
+        Returns:
+            SaAisErrorT: Return code of the corresponding API call(s)
+            list: List of class attributes
+        """
+        # Perform a deep copy
+        def attr_def_copy(attr_def):
+            """ Deep copy attributes """
+            attr_def_cpy = saImm.SaImmAttrDefinitionT_2()
+            attr_def_cpy.attrName = attr_def.attrName[:]
+            attr_def_cpy.attrValueType = attr_def.attrValueType
+            attr_def_cpy.attrFlags = attr_def.attrFlags
+
+            return attr_def_cpy
+
+        class_attrs = []
+        attr_defs = pointer(pointer(saImm.SaImmAttrDefinitionT_2()))
+        category = saImm.SaImmClassCategoryT()
+        rc = saImmOmClassDescriptionGet_2(self.handle, class_name, category,
+                                          attr_defs)
+        if rc != eSaAisErrorT.SA_AIS_OK:
+            log_err("saImmOmClassDescriptionGet_2 FAILED - %s" %
+                    eSaAisErrorT.whatis(rc))
+        else:
+            _class_attrs = unmarshalNullArray(attr_defs)
+
+            # Make copy of attr_defs list
+            class_attrs = [attr_def_copy(item) for item in _class_attrs]
+
+            # Free the original memory
+            saImmOmClassDescriptionMemoryFree_2(self.handle, 
attr_defs.contents)
+
+        return rc, class_attrs
+
+    def get_rdn_attribute_for_class(self, class_name):
+        """ Return the RDN attribute for the given class
+        This is safe to call from OI callbacks.
+
+        Args:
+            class_name (str): Class name
+
+        Returns:
+            str: RDN attribute of the class
+        """
+        rc, class_attrs = self.get_class_description(class_name)
+        if rc != eSaAisErrorT.SA_AIS_OK:
+            return ""
+        else:
+            for attr_desc in class_attrs:
+                if attr_desc.attrFlags & saImm.saImm.SA_IMM_ATTR_RDN:
+                    return attr_desc.attrName
+
+    def invoke_admin_operation(self, dn, op_id, params=None):
+        """ Invoke admin op for dn
+
+        Args:
+            dn (str): Object dn
+            op_id (str): Operation id
+            params (list): List of parameters
+
+        Returns:
+            SaAisErrorT: Return code of admin operation invoke
+        """
+        _owner = ImmOmAdminOwner(self.handle)
+        rc = _owner.initialize()
+        if rc == eSaAisErrorT.SA_AIS_OK:
+            index = dn.rfind(",")
+            object_rdn = dn[index+1:]
+            rc = _owner.set_owner(object_rdn)
+
+        if rc == eSaAisErrorT.SA_AIS_OK:
+            owner_handle = _owner.get_handle()
+            if params is None:
+                params = []
+
+            object_dn = SaNameT(dn)
+            operation_rc = SaAisErrorT()
+
+            rc = saImmOmAdminOperationInvoke_2(owner_handle, object_dn, 0,
+                                               op_id, params, operation_rc,
+                                               saAis.SA_TIME_ONE_SECOND * 10)
+            if rc != eSaAisErrorT.SA_AIS_OK:
+                log_err("saImmOmAdminOperationInvoke_2 FAILED - %s" %
+                        eSaAisErrorT.whatis(rc))
+            elif operation_rc.value != eSaAisErrorT.SA_AIS_OK:
+                log_err("Administrative operation(%s) on %s FAILED - %s" %
+                        (op_id, object_dn, eSaAisErrorT.whatis(operation_rc)))
+
+        return rc
+
+    def get_class_category(self, class_name):
+        """ Return the category of the given class
+
+        Args:
+            class_name (str): Class name
+
+        Returns:
+            SaImmClassCategoryT: Class category
+        """
+        c_attr_defs = pointer(pointer(saImmOm.SaImmAttrDefinitionT_2()))
+        c_category = saImmOm.SaImmClassCategoryT()
+        c_class_name = saImmOm.SaImmClassNameT(class_name)
+
+        rc = saImmOmClassDescriptionGet_2(self.handle, c_class_name,
+                                          c_category, c_attr_defs)
+        if rc != eSaAisErrorT.SA_AIS_OK:
+            log_err("saImmOmClassDescriptionGet_2 FAILED - %s" %
+                    eSaAisErrorT.whatis(rc))
+            return ""
+        return c_category.value
+
+
+class ImmOmAdminOwner(object):
+    """ ImmOmAdminOwner class """
+    def __init__(self, imm_handle, owner_name=""):
+        """ Constructor for ImmOmAdminOwner class
+
+        Args:
+            imm_handle (SaImmHandleT): IMM OM agent handle
+            owner_name (str): Name of the administrative owner
+        """
+        self.imm_handle = imm_handle
+        if owner_name:
+            self.owner_name = owner_name
+        else:
+            self.owner_name = "pyosaf_" + str(uuid.uuid4())
+
+        self.owner_name = saImmOm.SaImmAdminOwnerNameT(self.owner_name)
+        self.owner_handle = None
+
+    def __exit__(self, exception_type, exception_value, traceback):
+        """ Destructor for ImmOmAdminOwner class
+
+        Finalize the administrative owner handle
+        """
+        if self.owner_handle:
+            saImmOm.saImmOmAdminOwnerFinalize(self.owner_handle)
+
+    def __del__(self):
+        if self.owner_handle:
+            saImmOm.saImmOmAdminOwnerFinalize(self.owner_handle)
+
+    def initialize(self):
+        """ Initialize the administrative owner.
+
+        Return:
+            SaAisErrorT: Return code of admin operation invoke
+        """
+        self.owner_handle = saImmOm.SaImmAdminOwnerHandleT()
+        rc = saImmOmAdminOwnerInitialize(self.imm_handle, self.owner_name,
+                                         eSaBoolT.SA_TRUE, self.owner_handle)
+        if rc != eSaAisErrorT.SA_AIS_OK:
+            log_err("saImmOmAdminOwnerInitialize FAILED - %s" %
+                    eSaAisErrorT.whatis(rc))
+        return rc
+
+    def get_handle(self):
+        """ Return the administrative owner handle successfully initialized """
+        return self.owner_handle
+
+    def set_owner(self, obj_name, scope=eSaImmScopeT.SA_IMM_SUBTREE):
+        """ Set the administrative owner to the set of objects identified by
+        the scope and the obj_name parameters.
+
+        Args:
+            obj_name (str): Object name
+            scope (SaImmScopeT): Scope of the admin owner set operation
+
+        Return:
+            SaAisErrorT: Return code of admin operation invoke
+        """
+        if not obj_name:
+            rc = eSaAisErrorT.SA_AIS_ERR_NOT_EXIST
+        else:
+            obj_name = SaNameT(obj_name)
+            obj_name = [obj_name]
+
+            rc = saImmOmAdminOwnerSet(self.owner_handle, obj_name, scope)
+            if rc != eSaAisErrorT.SA_AIS_OK:
+                log_err("saImmOmAdminOwnerSet FAILED - %s" %
+                        eSaAisErrorT.whatis(rc))
+
+        return rc
+
+    def release_owner(self, obj_name, scope=eSaImmScopeT.SA_IMM_SUBTREE):
+        """ Release the administrative owner of the set of objects
+        identified by the scope and obj_name parameters
+
+        Args:
+            obj_name (str): Object name
+            scope (SaImmScopeT): Scope of the admin owner release operation
+
+        Return:
+            SaAisErrorT: Return code of admin operation invoke
+        """
+        if not obj_name:
+            rc = eSaAisErrorT.SA_AIS_ERR_NOT_EXIST
+        else:
+            obj_name = SaNameT(obj_name)
+            obj_name = [obj_name]
+
+            rc = saImmOmAdminOwnerRelease(self.owner_handle, obj_name, scope)
+            if rc != eSaAisErrorT.SA_AIS_OK:
+                log_err("saImmOmAdminOwnerRelease FAILED - %s" %
+                        eSaAisErrorT.whatis(rc))
+
+        return rc
diff --git a/python/pyosaf/utils/immom/ccb.py b/python/pyosaf/utils/immom/ccb.py
index e5e8de5..6b8373d 100644
--- a/python/pyosaf/utils/immom/ccb.py
+++ b/python/pyosaf/utils/immom/ccb.py
@@ -17,16 +17,17 @@
 ############################################################################
 """ Class representing an IMM CCB """
 from __future__ import print_function
-import sys
-from ctypes import c_void_p, pointer, cast
+from ctypes import c_void_p, pointer, cast, POINTER
 
-from pyosaf.saAis import eSaAisErrorT, eSaBoolT, SaNameT, SaStringT, \
-    SaDoubleT, SaTimeT, SaUint64T, SaInt64T, SaUint32T, SaInt32T, SaFloatT
+from pyosaf.saAis import eSaAisErrorT, SaNameT, SaStringT, SaFloatT, \
+    unmarshalNullArray, SaDoubleT, SaTimeT, SaUint64T, SaInt64T, SaUint32T, \
+    SaInt32T, SaVersionT
 from pyosaf.saImm import eSaImmScopeT, eSaImmValueTypeT, SaImmAttrValuesT_2
 from pyosaf import saImm
 from pyosaf import saImmOm
-from pyosaf.utils import immom
-from pyosaf.utils import SafException
+from pyosaf.utils.immom import agent
+from pyosaf.utils.immom.accessor import ImmOmAccessor
+from pyosaf.utils import log_err, bad_handle_retry
 
 
 def _value_to_ctype_ptr(value_type, value):
@@ -39,8 +40,6 @@ def _value_to_ctype_ptr(value_type, value):
     Returns:
         c_void_p: ctype pointer which points to value
     """
-    if sys.version_info > (3,):
-        long = int
     if value_type is eSaImmValueTypeT.SA_IMM_ATTR_SAINT32T:
         ctypeptr = cast(pointer(SaInt32T(value)), c_void_p)
     elif value_type is eSaImmValueTypeT.SA_IMM_ATTR_SAUINT32T:
@@ -86,21 +85,15 @@ def marshal_c_array(value_type, value_list):
 
 class Ccb(object):
     """ Class representing an ongoing CCB """
-    def __init__(self, flags=saImm.saImm.SA_IMM_CCB_REGISTERED_OI):
-        self.owner_handle = saImmOm.SaImmAdminOwnerHandleT()
-        owner_name = saImmOm.SaImmAdminOwnerNameT("DummyName")
-
-        immom.saImmOmAdminOwnerInitialize(immom.handle, owner_name,
-                                          eSaBoolT.SA_TRUE, self.owner_handle)
-        self.ccb_handle = saImmOm.SaImmCcbHandleT()
-
-        if flags:
-            ccb_flags = saImmOm.SaImmCcbFlagsT(flags)
-        else:
-            ccb_flags = saImmOm.SaImmCcbFlagsT(0)
-
-        immom.saImmOmCcbInitialize(self.owner_handle, ccb_flags,
-                                   self.ccb_handle)
+    def __init__(self, flags=saImm.saImm.SA_IMM_CCB_REGISTERED_OI,
+                 version=None):
+        self.init_version = version if version else SaVersionT('A', 2, 15)
+        self.imm_om = None
+        self.admin_owner = None
+        self.accessor = None
+        self.ccb_handle = None
+        self.ccb_flags = saImmOm.SaImmCcbFlagsT(flags) if flags else \
+            saImmOm.SaImmCcbFlagsT(0)
 
     def __enter__(self):
         """ Called when Ccb is used in a 'with' statement as follows:
@@ -123,54 +116,226 @@ class Ccb(object):
             self.__del__()
 
     def __del__(self):
-        """ Finalize the CCB """
-        immom.saImmOmAdminOwnerFinalize(self.owner_handle)
+        """ Destructor for CBB class
+
+        Finalize the the CCB
+        """
+        if self.ccb_handle:
+            agent.saImmOmCcbFinalize(self.ccb_handle)
+        if self.admin_owner:
+            del self.admin_owner
+        if self.imm_om:
+            del self.imm_om
+
+    def clear_admin_owner(self, obj_name, scope=eSaImmScopeT.SA_IMM_SUBTREE):
+        """ Clear the administrative owner for the set of object identified
+        by the scope and obj_name parameters
+
+        Args:
+            obj_name (str): Object name
+            scope (SaImmScopeT): Scope of the clear operation
+
+        Returns:
+            SaAisErrorT: Return code of the corresponding API call(s)
+        """
+        return self.imm_om.clear_admin_owner(obj_name, scope)
 
+
+    def finalize(self):
+        """ Finalize the CCB handle
+
+        Returns:
+            SaAisErrorT: Return code of the API call
+        """
+        rc = eSaAisErrorT.SA_AIS_OK
+        if self.ccb_handle:
+            rc = agent.saImmOmCcbFinalize(self.ccb_handle)
+            if rc != eSaAisErrorT.SA_AIS_OK:
+                log_err("saImmOmCcbFinalize 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.ccb_handle = None
+
+        return rc
+
+    @bad_handle_retry
+    def init(self, owner_name=""):
+        """ Initialize the IMM OM, administrative owner, and CCB handles
+
+        Args:
+            owner_name (str): Name of the administrative owner
+
+        Return:
+            SaAisErrorT: Return code of the APIs
+        """
+        # Clean previous resources if any
+        self.finalize()
+        self.imm_om = agent.ImmOmAgent(self.init_version)
+        rc = self.imm_om.init()
+        if rc == eSaAisErrorT.SA_AIS_OK:
+            _om_handle = self.imm_om.get_handle()
+            self.admin_owner = agent.ImmOmAdminOwner(_om_handle, owner_name)
+            self.admin_owner.initialize()
+            if rc == eSaAisErrorT.SA_AIS_OK:
+                _owner_handle = self.admin_owner.get_handle()
+                self.ccb_handle = saImmOm.SaImmCcbHandleT()
+
+                rc = agent.saImmOmCcbInitialize(
+                    _owner_handle, self.ccb_flags, self.ccb_handle)
+                if rc != eSaAisErrorT.SA_AIS_OK:
+                    log_err("saImmOmCcbInitialize FAILED - %s" %
+                            eSaAisErrorT.whatis(rc))
+        return rc
+
+    @bad_handle_retry
     def create(self, obj, parent_name=None):
         """ Create the CCB object
 
         Args:
             obj (ImmObject): Imm object
             parent_name (str): Parent name
+
+        Return:
+            SaAisErrorT: Return code of the APIs
         """
+        rc = eSaAisErrorT.SA_AIS_OK
         if parent_name is not None:
+            rc = self.admin_owner.set_owner(parent_name)
             parent_name = SaNameT(parent_name)
-            object_names = [parent_name]
-            immom.saImmOmAdminOwnerSet(self.owner_handle, object_names,
-                                       eSaImmScopeT.SA_IMM_SUBTREE)
-        else:
-            parent_name = None
-
-        attr_values = []
-        for attr_name, type_values in obj.attrs.items():
-            values = type_values[1]
-            attr = SaImmAttrValuesT_2()
-            attr.attrName = attr_name
-            attr.attrValueType = type_values[0]
-            attr.attrValuesNumber = len(values)
-            attr.attrValues = marshal_c_array(attr.attrValueType, values)
-            attr_values.append(attr)
-
-        immom.saImmOmCcbObjectCreate_2(self.ccb_handle,
-                                       obj.class_name,
-                                       parent_name,
-                                       attr_values)
 
+        if rc == eSaAisErrorT.SA_AIS_OK:
+            attr_values = []
+            for attr_name, type_values in obj.attrs.items():
+                values = type_values[1]
+                attr = SaImmAttrValuesT_2()
+                attr.attrName = attr_name
+                attr.attrValueType = type_values[0]
+
+                attr.attrValuesNumber = len(values)
+                attr.attrValues = marshal_c_array(attr.attrValueType, values)
+                attr_values.append(attr)
+
+            rc = agent.saImmOmCcbObjectCreate_2(self.ccb_handle,
+                                                obj.class_name,
+                                                parent_name,
+                                                attr_values)
+            if rc != eSaAisErrorT.SA_AIS_OK:
+                log_err("saImmOmCcbObjectCreate_2 FAILED - %s" %
+                        eSaAisErrorT.whatis(rc))
+
+        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 users, so that they 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 delete(self, object_name):
         """ Add a delete operation of the object with the given DN to the CCB
 
         Args:
             object_name (str): Object name
+
+        Return:
+            SaAisErrorT: Return code of the APIs
         """
         if object_name is None:
-            raise SafException(eSaAisErrorT.SA_AIS_ERR_NOT_EXIST)
+            return eSaAisErrorT.SA_AIS_ERR_NOT_EXIST
+
+        rc = self.admin_owner.set_owner(object_name)
+        if rc == eSaAisErrorT.SA_AIS_OK:
+            obj_name = SaNameT(object_name)
+            rc = agent.saImmOmCcbObjectDelete(self.ccb_handle, obj_name)
+            if rc != eSaAisErrorT.SA_AIS_OK:
+                log_err("saImmOmCcbObjectDelete FAILED - %s" %
+                        eSaAisErrorT.whatis(rc))
+
+        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 users, so that they 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 _modify(self, object_name, attr_name, values, mod_type):
+        """ Modify an existing object
 
-        object_name = SaNameT(object_name)
-        object_names = [object_name]
-
-        immom.saImmOmAdminOwnerSet(self.owner_handle, object_names,
-                                   eSaImmScopeT.SA_IMM_SUBTREE)
-        immom.saImmOmCcbObjectDelete(self.ccb_handle, object_name)
+        Args:
+            object_name (str): Object name
+            attr_name (str): Attribute name
+            values (list): List of attribute values
+            mod_type (eSaImmAttrModificationTypeT): Modification type
+        Return:
+            SaAisErrorT: Return code of the APIs
+        """
+        if object_name is None:
+            rc = eSaAisErrorT.SA_AIS_ERR_INVALID_PARAM
+        else:
+            if not self.accessor:
+                self.accessor = ImmOmAccessor(self.init_version)
+                self.accessor.init()
+
+            # Get the attribute value type by reading the object's class
+            # description
+            rc, obj = self.accessor.get(object_name)
+            if rc == eSaAisErrorT.SA_AIS_OK:
+                class_name = obj.SaImmAttrClassName
+                _, attr_def_list = \
+                    self.imm_om.get_class_description(class_name)
+                value_type = None
+                for attr_def in attr_def_list:
+                    if attr_def.attrName == attr_name:
+                        value_type = attr_def.attrValueType
+                        break
+                if value_type:
+                    rc = self.admin_owner.set_owner(object_name,
+                                                    eSaImmScopeT.SA_IMM_ONE)
+
+            if rc == eSaAisErrorT.SA_AIS_OK:
+                # Make sure the values field is a list
+                if not isinstance(values, list):
+                    values = [values]
+
+                attr_mods = []
+                attr_mod = saImmOm.SaImmAttrModificationT_2()
+                attr_mod.modType = mod_type
+                attr_mod.modAttr = SaImmAttrValuesT_2()
+                attr_mod.modAttr.attrName = attr_name
+                attr_mod.modAttr.attrValueType = value_type
+                attr_mod.modAttr.attrValuesNumber = len(values)
+                attr_mod.modAttr.attrValues = marshal_c_array(value_type,
+                                                              values)
+                attr_mods.append(attr_mod)
+                object_name = SaNameT(object_name)
+                rc = agent.saImmOmCcbObjectModify_2(self.ccb_handle,
+                                                    object_name, attr_mods)
+                if rc != eSaAisErrorT.SA_AIS_OK:
+                    log_err("saImmOmCcbObjectModify_2 FAILED - %s" %
+                            eSaAisErrorT.whatis(rc))
+
+            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 users, so that they 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 modify_value_add(self, object_name, attr_name, values):
         """ Add to the CCB an ADD modification of an existing object
@@ -179,51 +344,13 @@ class Ccb(object):
             object_name (str): Object name
             attr_name (str): Attribute name
             values (list): List of attribute values
+
+        Return:
+            SaAisErrorT: Return code of the APIs
         """
-        assert object_name
-
-        # Make sure the values field is a list
-        if not isinstance(values, list):
-            values = [values]
-
-        # First try to get the attribute value type by reading
-        # the object's class description
-        try:
-            obj = immom.get(object_name)
-        except SafException as err:
-            print("failed: %s" % err)
-            return
-
-        object_name = SaNameT(object_name)
-        object_names = [object_name]
-        class_name = obj.SaImmAttrClassName
-        value_type = None
-        attr_def_list = immom.class_description_get(class_name)
-        for attr_def in attr_def_list:
-            if attr_def.attrName == attr_name:
-                value_type = attr_def.attrValueType
-                break
-
-        if value_type is None:
-            raise SafException(eSaAisErrorT.SA_AIS_ERR_NOT_EXIST,
-                               "attribute '%s' does not exist" % attr_name)
-
-        immom.saImmOmAdminOwnerSet(self.owner_handle, object_names,
-                                   eSaImmScopeT.SA_IMM_ONE)
-        attr_mods = []
-
-        attr_mod = saImmOm.SaImmAttrModificationT_2()
-        attr_mod.modType = \
+        mod_type = \
             saImm.eSaImmAttrModificationTypeT.SA_IMM_ATTR_VALUES_ADD
-        attr_mod.modAttr = SaImmAttrValuesT_2()
-        attr_mod.modAttr.attrName = attr_name
-        attr_mod.modAttr.attrValueType = value_type
-        attr_mod.modAttr.attrValuesNumber = len(values)
-        attr_mod.modAttr.attrValues = marshal_c_array(value_type, values)
-
-        attr_mods.append(attr_mod)
-
-        immom.saImmOmCcbObjectModify_2(self.ccb_handle, object_name, attr_mods)
+        return self._modify(object_name, attr_name, values, mod_type)
 
     def modify_value_replace(self, object_name, attr_name, values):
         """ Add to the CCB an REPLACE modification of an existing object
@@ -232,51 +359,13 @@ class Ccb(object):
             object_name (str): Object name
             attr_name (str): Attribute name
             values (list): List of attribute values
+
+        Return:
+            SaAisErrorT: Return code of the APIs
         """
-        assert object_name
-
-        # Make sure the values field is a list
-        if not isinstance(values, list):
-            values = [values]
-
-        # First try to get the attribute value type by reading
-        # the object's class description
-        try:
-            obj = immom.get(object_name)
-        except SafException as err:
-            print("failed: %s" % err)
-            return
-
-        object_name = SaNameT(object_name)
-        object_names = [object_name]
-        class_name = obj.SaImmAttrClassName
-        value_type = None
-        attr_def_list = immom.class_description_get(class_name)
-        for attr_def in attr_def_list:
-            if attr_def.attrName == attr_name:
-                value_type = attr_def.attrValueType
-                break
-
-        if value_type is None:
-            raise SafException(eSaAisErrorT.SA_AIS_ERR_NOT_EXIST,
-                               "attribute '%s' does not exist" % attr_name)
-
-        immom.saImmOmAdminOwnerSet(self.owner_handle, object_names,
-                                   eSaImmScopeT.SA_IMM_ONE)
-        attr_mods = []
-
-        attr_mod = saImmOm.SaImmAttrModificationT_2()
-        attr_mod.modType = \
+        mod_type = \
             saImm.eSaImmAttrModificationTypeT.SA_IMM_ATTR_VALUES_REPLACE
-        attr_mod.modAttr = SaImmAttrValuesT_2()
-        attr_mod.modAttr.attrName = attr_name
-        attr_mod.modAttr.attrValueType = value_type
-        attr_mod.modAttr.attrValuesNumber = len(values)
-        attr_mod.modAttr.attrValues = marshal_c_array(value_type, values)
-
-        attr_mods.append(attr_mod)
-
-        immom.saImmOmCcbObjectModify_2(self.ccb_handle, object_name, attr_mods)
+        return self._modify(object_name, attr_name, values, mod_type)
 
     def modify_value_delete(self, object_name, attr_name, values):
         """ Add to the CCB an DELETE modification of an existing object
@@ -285,66 +374,44 @@ class Ccb(object):
             object_name (str): Object name
             attr_name (str): Attribute name
             values (list): List of attribute values
+
+        Return:
+            SaAisErrorT: Return code of the APIs
         """
-        assert object_name
-
-        # Make sure the values field is a list
-        if not isinstance(values, list):
-            values = [values]
-
-        # First try to get the attribute value type by reading
-        # the object's class description
-        try:
-            obj = immom.get(object_name)
-        except SafException as err:
-            print("failed: %s" % err)
-            return
-
-        object_name = SaNameT(object_name)
-        object_names = [object_name]
-        class_name = obj.SaImmAttrClassName
-        value_type = None
-        attr_def_list = immom.class_description_get(class_name)
-        for attr_def in attr_def_list:
-            if attr_def.attrName == attr_name:
-                value_type = attr_def.attrValueType
-                break
-
-        if value_type is None:
-            raise SafException(eSaAisErrorT.SA_AIS_ERR_NOT_EXIST,
-                               "attribute '%s' does not exist" % attr_name)
-
-        immom.saImmOmAdminOwnerSet(self.owner_handle, object_names,
-                                   eSaImmScopeT.SA_IMM_ONE)
-        attr_mods = []
-
-        attr_mod = saImmOm.SaImmAttrModificationT_2()
-        attr_mod.modType = \
+        mod_type = \
             saImm.eSaImmAttrModificationTypeT.SA_IMM_ATTR_VALUES_DELETE
-        attr_mod.modAttr = SaImmAttrValuesT_2()
-        attr_mod.modAttr.attrName = attr_name
-        attr_mod.modAttr.attrValueType = value_type
-        attr_mod.modAttr.attrValuesNumber = len(values)
-        attr_mod.modAttr.attrValues = marshal_c_array(value_type, values)
-
-        attr_mods.append(attr_mod)
-
-        immom.saImmOmCcbObjectModify_2(self.ccb_handle, object_name, attr_mods)
+        return self._modify(object_name, attr_name, values, mod_type)
 
     def apply(self):
-        """ Apply the CCB """
-        immom.saImmOmCcbApply(self.ccb_handle)
-
+        """ Apply the CCB
 
-def test():
-    """ A simple function to test the usage of Ccb class """
-    ccb = Ccb()
-    ccb.modify_value_replace("safAmfCluster=myAmfCluster",
-                             "saAmfClusterStartupTimeout",
-                             [10000000000])
-    ccb.apply()
-    del ccb
+        Return:
+            SaAisErrorT: Return code of the APIs
+        """
+        rc = agent.saImmOmCcbApply(self.ccb_handle)
+        if rc != eSaAisErrorT.SA_AIS_OK:
+            log_err("saImmOmCcbApply FAILED - %s" % eSaAisErrorT.whatis(rc))
+
+        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 users, so that they 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_error_strings(self):
+        """ Return the current CCB error strings
+
+        Returns:
+            SaAisErrorT: Return code of the APIs
+            list: List of error strings
+        """
+        c_strings = POINTER(SaStringT)()
 
+        rc = agent.saImmOmCcbGetErrorStrings(self.ccb_handle, c_strings)
 
-if __name__ == '__main__':
-    test()
+        return rc, unmarshalNullArray(c_strings)
diff --git a/python/pyosaf/utils/immom/iterator.py 
b/python/pyosaf/utils/immom/iterator.py
index 75bd004..ea9a4be 100644
--- a/python/pyosaf/utils/immom/iterator.py
+++ b/python/pyosaf/utils/immom/iterator.py
@@ -17,7 +17,6 @@
 ############################################################################
 """ IMM Search iterators """
 from __future__ import print_function
-import sys
 from collections import Iterator
 from ctypes import pointer, c_char_p, cast, c_void_p
 
@@ -26,16 +25,17 @@ from pyosaf.saImm import saImm, eSaImmScopeT, 
eSaImmValueTypeT, \
     unmarshalSaImmValue, SaImmAttrNameT, SaImmSearchParametersT_2, \
     SaImmAttrValuesT_2
 from pyosaf import saImmOm
-from pyosaf.utils import immom
-from pyosaf.utils import SafException
+from pyosaf.utils import log_err, bad_handle_retry
+from pyosaf.utils.immom import agent
 from pyosaf.utils.immom.object import ImmObject
 
 
-class SearchIterator(Iterator):
+class SearchIterator(agent.ImmOmAgentManager, Iterator):
     """ General search iterator """
     def __init__(self, root_name=None, scope=eSaImmScopeT.SA_IMM_SUBTREE,
-                 attribute_names=None, search_param=None):
-        self.search_handle = saImmOm.SaImmSearchHandleT()
+                 attribute_names=None, search_param=None, version=None):
+        super(SearchIterator, self).__init__(version)
+        self.search_handle = None
         if root_name is not None:
             self.root_name = SaNameT(root_name)
         else:
@@ -45,32 +45,28 @@ class SearchIterator(Iterator):
                                 for attr_name in attribute_names] \
             if attribute_names else None
 
-        search_options = saImm.SA_IMM_SEARCH_ONE_ATTR
+        self.search_options = saImm.SA_IMM_SEARCH_ONE_ATTR
         if attribute_names is None:
-            search_options |= saImm.SA_IMM_SEARCH_GET_ALL_ATTR
+            self.search_options |= saImm.SA_IMM_SEARCH_GET_ALL_ATTR
         else:
-            search_options |= saImm.SA_IMM_SEARCH_GET_SOME_ATTR
+            self.search_options |= saImm.SA_IMM_SEARCH_GET_SOME_ATTR
 
         if search_param is None:
-            search_param = SaImmSearchParametersT_2()
+            self.search_param = SaImmSearchParametersT_2()
         else:
-            search_param = search_param
+            self.search_param = search_param
 
-        try:
-            immom.saImmOmSearchInitialize_2(immom.handle, self.root_name,
-                                            self.scope, search_options,
-                                            search_param, self.attribute_names,
-                                            self.search_handle)
-        except SafException as err:
-            if err.value == eSaAisErrorT.SA_AIS_ERR_NOT_EXIST:
-                self.search_handle = None
-                raise err
-            else:
-                raise err
+    def __exit__(self, exit_type, value, traceback):
+        """ Called when Iterator is used in a 'with' statement
+            just before it exits
 
-    def __del__(self):
-        if self.search_handle is not None:
-            immom.saImmOmSearchFinalize(self.search_handle)
+        exit_type, value and traceback are only set if the with statement was
+        left via an exception
+        """
+        if self.search_handle:
+            agent.saImmOmSearchFinalize(self.search_handle)
+        if self.handle:
+            agent.saImmOmFinalize(self.handle)
 
     def __iter__(self):
         return self
@@ -82,14 +78,13 @@ class SearchIterator(Iterator):
     def __next__(self):
         obj_name = SaNameT()
         attributes = pointer(pointer(SaImmAttrValuesT_2()))
-        try:
-            immom.saImmOmSearchNext_2(self.search_handle, obj_name, attributes)
-        except SafException as err:
-            if err.value == eSaAisErrorT.SA_AIS_ERR_NOT_EXIST:
-                raise StopIteration
-            else:
-                raise err
-
+        rc = agent.saImmOmSearchNext_2(self.search_handle, obj_name,
+                                       attributes)
+        if rc != eSaAisErrorT.SA_AIS_OK:
+            if rc != eSaAisErrorT.SA_AIS_ERR_NOT_EXIST:
+                log_err("saImmOmSearchNext_2 FAILED - %s" %
+                        eSaAisErrorT.whatis(rc))
+            raise StopIteration
         attrs = {}
         attr_list = unmarshalNullArray(attributes)
         for attr in attr_list:
@@ -100,6 +95,28 @@ class SearchIterator(Iterator):
                                      for val in attr_range]]
         return ImmObject(obj_name, attrs)
 
+    @bad_handle_retry
+    def init(self):
+        """ Initialize the IMM OM search iterator
+
+        Returns:
+            SaAisErrorT: Return code of the IMM OM APIs
+        """
+        # Cleanup previous resources if any
+        self.finalize()
+        rc = self.initialize()
+
+        self.search_handle = saImmOm.SaImmSearchHandleT()
+        if rc == eSaAisErrorT.SA_AIS_OK:
+            rc = agent.saImmOmSearchInitialize_2(
+                self.handle, self.root_name, self.scope, self.search_options,
+                self.search_param, self.attribute_names, self.search_handle)
+            if rc != eSaAisErrorT.SA_AIS_OK:
+                log_err("saImmOmSearchInitialize_2 FAILED - %s" %
+                        eSaAisErrorT.whatis(rc))
+                self.search_handle = None
+        return rc
+
 
 class InstanceIterator(SearchIterator):
     """ Iterator over instances of a class """
@@ -111,15 +128,3 @@ class InstanceIterator(SearchIterator):
             eSaImmValueTypeT.SA_IMM_ATTR_SASTRINGT
         search_param.searchOneAttr.attrValue = cast(pointer(name), c_void_p)
         SearchIterator.__init__(self, root_name, search_param=search_param)
-
-
-def test():
-    """ A simple function to test the usage of InstanceIterator class """
-    instance_iter = InstanceIterator(sys.argv[1])
-    for dn, attr in instance_iter:
-        print(dn.split("=")[1])
-        print(attr, "\n")
-
-
-if __name__ == '__main__':
-    test()
diff --git a/python/pyosaf/utils/immom/object.py 
b/python/pyosaf/utils/immom/object.py
index 24fd485..0cef920 100644
--- a/python/pyosaf/utils/immom/object.py
+++ b/python/pyosaf/utils/immom/object.py
@@ -16,8 +16,11 @@
 #
 ############################################################################
 """ IMM Object representation """
+from pyosaf.saAis import eSaAisErrorT
 from pyosaf.saImm import saImm
-import pyosaf.utils.immom
+
+from pyosaf.utils import SafException
+from pyosaf.utils.immom.agent import ImmOmAgent
 
 
 class ImmObject(object):
@@ -34,29 +37,40 @@ class ImmObject(object):
         Attributes is a map where attribute name is key, value is a tuple of
         (valueType, values), values is a list.
         class_name and attributes are mutually exclusive.
+
+        Raises:
+            SafException: If the return code of the corresponding API call(s)
+                is not SA_AIS_OK
         """
         self.__dict__["dn"] = dn
-
+        _imm_om = ImmOmAgent()
+        rc = _imm_om.init()
+        if rc != eSaAisErrorT.SA_AIS_OK:
+            raise SafException(rc)
         # mutually exclusive for read vs create
         if class_name is not None:
             assert attributes is None
             self.__dict__["attrs"] = {}
             self.__dict__["class_name"] = class_name
-            self.class_desc[class_name] = \
-                pyosaf.utils.immom.class_description_get(class_name)
+            rc, class_desc = _imm_om.get_class_description(class_name)
+            if rc != eSaAisErrorT.SA_AIS_OK:
+                raise SafException(rc)
+            self.class_desc[class_name] = class_desc
         elif attributes is not None:
             assert class_name is None
             self.__dict__["attrs"] = attributes
             class_name = attributes["SaImmAttrClassName"][1][0]
             self.__dict__["class_name"] = class_name
             if class_name not in self.class_desc:
-                self.class_desc[class_name] = \
-                    pyosaf.utils.immom.class_description_get(class_name)
+                rc, class_desc = _imm_om.get_class_description(class_name)
+                if rc != eSaAisErrorT.SA_AIS_OK:
+                    raise SafException(rc)
+                self.class_desc[class_name] = class_desc
         else:
             raise ValueError("Class and attributes are None")
 
         self.__dict__["rdn_attribute"] = \
-            pyosaf.utils.immom.get_rdn_attribute_for_class(class_name)
+            _imm_om.get_rdn_attribute_for_class(class_name)
 
     def get_value_type(self, attr_name):
         """ Return IMM value type of the named attribute
@@ -108,8 +122,10 @@ class ImmObject(object):
 
     def __setattr__(self, key, value):
         # Correct RDN assignments missing the RDN attribute name in value
-        if key == self.rdn_attribute and value.find(self.rdn_attribute) != 0:
-            value = '%s=%s' % (self.rdn_attribute, value)
+
+        if key == self.rdn_attribute:
+            if str(value).find(self.rdn_attribute) != 0:
+                value = '%s=%s' % (self.rdn_attribute, value)
 
         value_type = self.get_value_type(key)
         if isinstance(value, list):
-- 
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

Reply via email to