python/pyosaf/utils/immoi/__init__.py                              |  402 ++++
 python/pyosaf/utils/immoi/implementer.py                           |  893 
++++++++++
 python/samples/immoi/samples/README                                |   82 +
 python/samples/immoi/samples/caps                                  |   42 +
 python/samples/immoi/samples/caps-inheritance-impl                 |   49 +
 python/samples/immoi/samples/classes.xml                           |  187 ++
 python/samples/immoi/samples/imm-listener                          |  111 +
 python/samples/immoi/samples/imm-listener-inheritance-impl         |  158 +
 python/samples/immoi/samples/interface-handler                     |  121 +
 python/samples/immoi/samples/interface-handler-inheritance-version |  125 +
 python/samples/immoi/samples/ping-pong                             |   59 +
 python/samples/immoi/samples/ping-pong-inheritance-impl            |   53 +
 python/samples/immoi/samples/time-reporter                         |   67 +
 python/samples/immoi/samples/time-reporter-inheritance-impl        |   82 +
 python/samples/immoi/samples/tones                                 |   27 +
 python/samples/immoi/samples/tones-inheritance-impl                |   32 +
 16 files changed, 2490 insertions(+), 0 deletions(-)


Add high-level imm oi utils module on top of the direct imm oi Python bindings. 
Also add sample applications demonstrating usage both as subclassing and with 
direct callbacks.

diff --git a/python/pyosaf/utils/immoi/__init__.py 
b/python/pyosaf/utils/immoi/__init__.py
new file mode 100644
--- /dev/null
+++ b/python/pyosaf/utils/immoi/__init__.py
@@ -0,0 +1,402 @@
+############################################################################
+#
+# (C) Copyright 2015 The OpenSAF Foundation
+#
+# 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 OI common utilitites
+'''
+
+import time
+
+from pyosaf.utils import immom
+
+from pyosaf import saAis, saImm, saImmOi
+
+from pyosaf.saAis import SaVersionT, SaNameT, SaSelectionObjectT, \
+    unmarshalSaStringTArray, eSaDispatchFlagsT
+
+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.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
+
+from ctypes import c_char_p, c_void_p, cast, pointer
+
+SELECTION_OBJECT = SaSelectionObjectT()
+HANDLE           = SaImmOiHandleT()
+
+TRYAGAIN_CNT = 60
+
+OPENSAF_IMM_OBJECT = "opensafImm=opensafImm,safApp=safImmService"
+
+class_descriptions = {}
+
+# Decorate the raw saImmOi* functions with retry and raising exceptions
+saImmOiInitialize_2       = decorate(saImmOi.saImmOiInitialize_2)
+saImmOiSelectionObjectGet = decorate(saImmOi.saImmOiSelectionObjectGet)
+saImmOiDispatch           = decorate(saImmOi.saImmOiDispatch)
+saImmOiFinalize           = decorate(saImmOi.saImmOiFinalize)
+saImmOiImplementerSet     = decorate(saImmOi.saImmOiImplementerSet)
+saImmOiImplementerClear   = decorate(saImmOi.saImmOiImplementerClear)
+saImmOiClassImplementerSet = decorate(saImmOi.saImmOiClassImplementerSet)
+saImmOiClassImplementerRelease = 
decorate(saImmOi.saImmOiClassImplementerRelease)
+saImmOiObjectImplementerSet = decorate(saImmOi.saImmOiObjectImplementerSet)
+saImmOiObjectImplementerRelease = 
decorate(saImmOi.saImmOiObjectImplementerRelease)
+saImmOiRtObjectCreate_2   = decorate(saImmOi.saImmOiRtObjectCreate_2)
+saImmOiRtObjectDelete     = decorate(saImmOi.saImmOiRtObjectDelete)
+saImmOiRtObjectUpdate_2   = decorate(saImmOi.saImmOiRtObjectUpdate_2)
+saImmOiAdminOperationResult = decorate(saImmOi.saImmOiAdminOperationResult)
+saImmOiAdminOperationResult_o2 = 
decorate(saImmOi.saImmOiAdminOperationResult_o2)
+saImmOiAugmentCcbInitialize = decorate(saImmOi.saImmOiAugmentCcbInitialize)
+saImmOiCcbSetErrorString  = decorate(saImmOi.saImmOiCcbSetErrorString)
+
+
+def _initialize(callbacks=None):
+    ''' Initializes IMM OI '''
+
+    version = SaVersionT('A', 2, 15)
+
+    saImmOiInitialize_2(HANDLE, callbacks, version)
+
+
+def register_applier(name):
+    ''' Registers an an applier '''
+
+    applier_name = "@" + name
+
+    register_implementer(applier_name)
+
+def register_implementer(name):
+    ''' Registers as an implementer '''
+
+    implementer_name = SaImmOiImplementerNameT(name)
+
+    saImmOiImplementerSet(HANDLE, implementer_name)
+
+
+def get_selection_object():
+    ''' Retrieves the the selection object '''
+
+    global SELECTION_OBJECT
+
+    saImmOiSelectionObjectGet(HANDLE, SELECTION_OBJECT)
+
+
+def implement_class(class_name):
+    ''' Registers the implementer as an OI for the given class '''
+
+    c_class_name = SaImmClassNameT(class_name)
+
+    saImmOiClassImplementerSet(HANDLE, c_class_name)
+
+
+def dispatch(mode=eSaDispatchFlagsT.SA_DISPATCH_ALL):
+    ''' Dispatches all queued callbacks.  
+    '''
+
+    saImmOiDispatch(HANDLE, mode)
+
+
+def create_rt_object(class_name, parent_name, obj):
+    ''' Creates a runtime object '''
+
+    # Marshall parameters
+    c_class_name  = SaImmClassNameT(class_name)
+
+    if parent_name:
+        c_parent_name = SaNameT(parent_name)
+    else:
+        c_parent_name = None
+
+    c_attr_values = []
+    for name, value_pair in obj.attrs.iteritems():
+
+        c_attr_type = value_pair[0]
+        values      = value_pair[1]
+
+        if values == None:
+            values = []
+        elif values == [None]:
+            values = []
+
+        # Make sure all values are in lists
+        if not isinstance(values, list):
+            values = [values]
+
+        # Create the values struct
+        c_attr = SaImmAttrValuesT_2()
+
+        c_attr.attrName         = SaImmAttrNameT(name)
+        c_attr.attrValueType    = c_attr_type
+        c_attr.attrValuesNumber = len(values)
+
+        if len(values) == 0:
+            c_attr.attrValues = None
+        else:
+            c_attr.attrValues = marshal_c_array(c_attr_type, values)
+
+        c_attr_values.append(c_attr)
+
+    # Call the function
+    saImmOiRtObjectCreate_2(HANDLE, c_class_name, c_parent_name, 
+                                    c_attr_values)
+
+
+def delete_rt_object(dn):
+    ''' Deletes a runtime object '''
+
+    # Marshall the parameter
+    c_dn = SaNameT(dn)
+
+    saImmOiRtObjectDelete(HANDLE, c_dn)
+
+
+def update_rt_object(dn, attributes):
+    ''' Updates the given object with the given attribute modifications
+    '''
+
+    # Get the class name for the object
+    class_name = get_class_name_for_dn(dn)
+
+    # Create and marshall attribute modifications
+    attr_mods  = []
+
+    for name, values in attributes.iteritems():
+
+        if values is None:
+            print "WARNING: Received no values for %s in %s" % (name, dn)
+            continue
+
+        if not isinstance(values, list):
+            values = [values]
+
+        attr_type = get_attribute_type(name, class_name)
+
+        c_attr_mod = SaImmAttrModificationT_2()
+        c_attr_mod.modType = 
eSaImmAttrModificationTypeT.SA_IMM_ATTR_VALUES_REPLACE
+        c_attr_mod.modAttr                  = SaImmAttrValuesT_2()
+        c_attr_mod.modAttr.attrName         = SaImmAttrNameT(name)
+        c_attr_mod.modAttr.attrValueType    = attr_type
+        c_attr_mod.modAttr.attrValuesNumber = len(values)
+        c_attr_mod.modAttr.attrValues       = marshal_c_array(attr_type, 
values)
+        attr_mods.append(c_attr_mod)
+
+    # Call the function
+    saImmOiRtObjectUpdate_2(HANDLE, SaNameT(dn), attr_mods)
+
+
+def report_admin_operation_result(invocation_id, result):
+    ''' Reports the result of an administrative operation '''
+
+    saImmOiAdminOperationResult(HANDLE, invocation_id, result)
+
+
+def set_error_string(ccb_id, error_string):
+    ''' Sets the error string. This can only be called from within OI
+        callbacks from a real implementer'''
+
+    c_error_string = saAis.SaStringT(error_string)
+
+    saImmOiCcbSetErrorString(HANDLE, ccb_id, c_error_string)
+
+
+def get_class_category(class_name):
+    ''' Returns the category of the given class '''
+
+    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
+
+
+def get_parent_name_for_dn(dn):
+    ''' returns the dn of the parent of the instance of the given dn '''
+    return ",".join(dn.split(',')[1:])
+
+def get_object_names_for_class(class_name, root_name=None):
+    ''' Returns the instances of the given class, optinally under the given 
root dn
+
+        will not read runtime attributes and is safe to call from OI callbacks
+    '''
+
+    # Set up and marshall the search parameter
+    c_class_name = c_char_p(class_name)
+
+    c_search_param = SaImmSearchParametersT_2()
+    c_search_param.searchOneAttr.attrName = "SaImmAttrClassName"
+    c_search_param.searchOneAttr.attrValueType = 
eSaImmValueTypeT.SA_IMM_ATTR_SASTRINGT
+    c_search_param.searchOneAttr.attrValue = cast(pointer(c_class_name), 
c_void_p)
+
+    # Create the search iterator
+    sit = SearchIterator(_search_param=c_search_param)
+
+    # Return the results
+    return [s.dn for s in sit]
+
+def get_class_name_for_dn(dn):
+    ''' returns the class name for an instance with the given dn '''
+
+    obj = immom.get(dn, ["SaImmAttrClassName"])
+
+    if obj:
+        return obj.SaImmAttrClassName
+    else:
+        return None
+
+def get_object_no_runtime(dn, class_name=None):
+    ''' returns 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.
+    '''
+
+    if not class_name:
+        class_name = get_class_name_for_dn(dn)
+
+    if not class_name:
+        return None
+
+    class_desc = get_class_description(class_name)
+
+    config_attrs = []
+
+    for attr_desc in class_desc:
+        if attr_desc.attrFlags & saImm.saImm.SA_IMM_ATTR_CONFIG:
+            config_attrs.append(attr_desc.attrName)
+
+    obj = immom.get(dn, config_attrs)
+
+    return obj
+
+def cache_class_descriptions(class_names):
+    ''' Explicitly caches the class description of the given class names 
+
+        The get_class_description function uses the cache and will also
+        implicitly cache descriptions
+    '''
+
+    global class_descriptions
+
+    for class_name in class_names:
+        class_descriptions[class_name] = 
immom.class_description_get(class_name)
+
+def get_class_description(class_name):
+    ''' Returns the description of the given class
+
+        Class descriptions are cached. This could cause a problem when
+        attributes are added.
+
+        This is safe to use from OI callbacks
+    '''
+
+    if not class_name in class_descriptions:
+        class_descriptions[class_name] = 
immom.class_description_get(class_name)
+
+    return class_descriptions[class_name]
+
+def get_attribute_type(attribute, class_name):
+    ''' Returns the type of the attribute in the given class 
+
+        This is safe to use from OI callbacks
+    '''
+
+    class_desc = immom.class_description_get(class_name)
+
+    attr_desc = filter(lambda ad: ad.attrName == attribute, class_desc)[0]
+
+    return attr_desc.attrValueType
+
+def get_rdn_attribute_for_class(class_name):
+    ''' Returns the RDN attribute for the given class 
+
+        This is safe to call from OI callbacks
+    '''
+
+    desc = immom.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
+
+def unmarshalLenArray(c_array, length, value_type):
+       ''' Convert c array with a known length to a Python list. '''
+
+       if not c_array:
+            return []
+       ctype = c_array[0].__class__
+       if ctype is str:
+            return unmarshalSaStringTArray(c_array)
+       val_list = []
+        i = 0
+       for ptr in c_array:
+            if i == length:
+                break
+            if not ptr:
+                break
+
+            val = unmarshalSaImmValue(ptr, value_type)
+
+            val_list.append(val)
+
+            i = i + 1
+
+       return val_list
+
+def get_available_classes_in_imm():
+    ''' Returns a list of all available classes in IMM 
+
+        Safe to call from OI callbacks
+    '''
+
+    opensaf_imm = immom.get(OPENSAF_IMM_OBJECT)
+
+    return opensaf_imm.opensafImmClassNames
+
+def create_non_existing_imm_object(class_name, parent_name, attributes):
+    ''' Creates an ImmObject instance for an object that does not yet
+        exist in IMM.
+    '''
+
+    rdn_attribute = get_rdn_attribute_for_class(class_name)
+    rdn_value     = attributes[rdn_attribute][0]
+
+    dn  = '%s,%s' % (rdn_value, parent_name)
+    obj = ImmObject(class_name = class_name, dn=dn)
+
+    for name, values in attributes.iteritems():
+        obj.__setattr__(name, values)
+
+    obj.__setattr__('SaImmAttrClassName', class_name)
+    obj.__setattr__('dn', dn)
+
+    return obj
diff --git a/python/pyosaf/utils/immoi/implementer.py 
b/python/pyosaf/utils/immoi/implementer.py
new file mode 100755
--- /dev/null
+++ b/python/pyosaf/utils/immoi/implementer.py
@@ -0,0 +1,893 @@
+############################################################################
+#
+# (C) Copyright 2015 The OpenSAF Foundation
+#
+# 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
+#
+############################################################################
+'''
+    Class representing an IMM Object Implementer.
+'''
+
+import select
+
+from pyosaf import saImm, saImmOm, saImmOi, saAis
+
+from pyosaf.saAis import eSaAisErrorT
+
+from pyosaf.saImm import eSaImmValueTypeT, eSaImmAttrModificationTypeT, \
+    eSaImmClassCategoryT
+
+from pyosaf.utils import immom, immoi, SafException
+
+from pyosaf.utils.immom.object import ImmObject
+
+from pyosaf.utils.immom.ccb import marshal_c_array
+
+implementer_instance = None
+
+# Cache CCBs
+completed_ccbs = {}
+ccbs           = {}
+
+def _collect_full_transaction(ccb_id):
+    ''' Goes through a completed CCB and summarizes the full transaction as
+        seen by the OI.
+
+        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, class_name)
+
+            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 creates
+        if operation_type == 'CREATE':
+            parent     = operation['parent']
+            class_name = operation['className']
+            attributes = operation['attributes']
+
+            instance = immoi.create_non_existing_imm_object(class_name, 
+                                                            parent, attributes)
+
+            created.append(instance)
+
+            deleted = filter(lambda i: i != dn, deleted)
+
+        # Handle deletes
+        elif operation_type == 'DELETE':
+            dn = operation['dn']
+
+            deleted.append(dn)
+
+            created = filter(lambda i: i.dn != dn, created)
+            updated = filter(lambda i: i.dn != dn, updated)
+
+        # Handle modify operations
+        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 = filter(lambda i: i.dn == dn, 
all_objects_now)
+
+                if len(affected_instances) == 0:
+                    print 'ERROR: Failed to find object %s affected by modify 
operation' % dn
+                else:
+                    affected_instance = affected_instances[0]
+
+                    if not affected_instance 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 = all_objects_now + created
+    instances_after = filter(lambda i: not i.dn in deleted, instances_after)
+
+    out = {'instances_after' : instances_after,
+           'created'         : created,
+           'deleted'         : deleted,
+           'updated'         : updated}
+
+    return out
+
+# Set up callbacks
+def admin_operation(oi_handle, c_invocation_id, c_name, c_operation_id, 
c_params):
+    ''' Callback for administrative operations '''
+
+    # Unmarshal parameters
+    invocation_id = c_invocation_id
+    name          = saImm.unmarshalSaImmValue(c_name, 
+                                              
eSaImmValueTypeT.SA_IMM_ATTR_SANAMET)
+    operation_id  = c_operation_id
+
+    params = []
+
+    for p in saAis.unmarshalNullArray(c_params):
+        paramName   = p.paramName
+        paramType   = p.paramType
+        paramBuffer = p.paramBuffer
+
+        value = saImm.unmarshalSaImmValue(paramBuffer, paramType)
+
+        params.append(value)
+
+    # 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" % \
+            (name, invocation_id, result)
+
+def abort_ccb(oi_handle, ccb_id):
+    ''' Callback for aborted CCBs. 
+
+        Removes the given CCB from the cache 
+    '''
+
+    del ccbs[ccb_id]
+
+def apply_ccb(oi_handle, ccb_id):
+    ''' Callback for apply of CCBs '''
+
+    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, class_name)
+
+            all_instances.append(obj)
+
+    updated = completed_ccbs[ccb_id]['updated']
+    added   = completed_ccbs[ccb_id]['added']
+    removed = 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, added, 
removed)
+
+def attr_update(oi_handle, c_name, c_attr_names):
+    ''' Callback for attribute update calls from IMM '''
+
+    # Unmarshall parameters
+    name       = saImm.unmarshalSaImmValue(c_name, 
+                                     eSaImmValueTypeT.SA_IMM_ATTR_SANAMET)
+    attr_names = saAis.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
+    if implementer_instance.on_runtime_values_get:
+
+        attr_mods = []
+
+        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]
+
+            attribute_type = immoi.get_attribute_type(attr_name, class_name)
+
+            attr_mod = saImm.SaImmAttrModificationT_2()
+            attr_mod.modType = 
eSaImmAttrModificationTypeT.SA_IMM_ATTR_VALUES_REPLACE
+            attr_mod.modAttr = saImm.SaImmAttrValuesT_2()
+            attr_mod.modAttr.attrName = saImm.SaImmAttrNameT(attr_name)
+            attr_mod.modAttr.attrValueType = attribute_type
+            attr_mod.modAttr.attrValuesNumber = len(values)
+            attr_mod.modAttr.attrValues = marshal_c_array(attribute_type, 
values)
+            attr_mods = [attr_mod]
+
+        # Report the updated values for the attributes
+        try:
+            immoi.update_rt_object(name, attr_mods)
+        except SafException as err:
+            return eSaAisErrorT.SA_AIS_ERR_FAILED_OPERATION
+
+    else:
+        return eSaAisErrorT.SA_AIS_ERR_NOT_SUPPORTED
+
+def delete_added(oi_handle, ccb_id, c_name):
+    ''' Callback for object delete '''
+
+    # Unmarshall the parameters
+    name = saImm.unmarshalSaImmValue(c_name, 
+                                     eSaImmValueTypeT.SA_IMM_ATTR_SANAMET)
+
+    # Create a new CCB in the cache if needed
+    if not ccb_id in 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 modify_added(oi_handle, c_ccb_id, c_name, c_attr_modification):
+    ''' Callback for object modify '''
+
+    # Unmarshal the parameters
+    name   = saImm.unmarshalSaImmValue(c_name, 
+                                       eSaImmValueTypeT.SA_IMM_ATTR_SANAMET)
+    ccb_id = c_ccb_id
+
+    attribute_modifications = []
+
+    implementer_objection = None
+
+    for a in saAis.unmarshalNullArray(c_attr_modification):
+        attr_name  = a.modAttr.attrName
+        attr_type  = a.modAttr.attrValueType
+        mod_type   = a.modType
+        nr_values  = a.modAttr.attrValuesNumber
+        attrValues = immoi.unmarshalLenArray(a.modAttr.attrValues, 
+                                             a.modAttr.attrValuesNumber,
+                                             a.modAttr.attrValueType)
+
+        attribute_modifications.append({'attribute'    : attr_name, 
+                                        'type'         : attr_type, 
+                                        'modification' : mod_type, 
+                                        'values'       : attrValues})
+
+        # Tell the implementer about the modification
+        result = implementer_instance.on_modify_added(attr_name, mod_type, 
+                                                      attrValues)
+
+        if result != eSaAisErrorT.SA_AIS_OK:
+            implementer_objected = result
+
+    # Create a new CCB in the cache if needed
+    if not ccb_id in 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
+    else:
+        return eSaAisErrorT.SA_AIS_OK
+
+
+def create_added(oi_handle, c_ccb_id, c_class_name, c_parent, c_attr_values):
+    ''' Callback for object create '''
+
+    # Unmarshal parameters
+    parent     = saImm.unmarshalSaImmValue(c_parent, 
+                                           
eSaImmValueTypeT.SA_IMM_ATTR_SANAMET)
+    class_name = c_class_name
+    ccb_id     = c_ccb_id
+
+    attributes = {}
+
+    for a in saAis.unmarshalNullArray(c_attr_values):
+        attr_name  = a.attrName
+        attr_type  = a.attrValueType
+        nr_values  = a.attrValuesNumber
+
+        attr_values = immoi.unmarshalLenArray(a.attrValues, nr_values, 
+                                              attr_type)
+
+        if len(attr_values) > 0:
+            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:
+        print attribute.attrName
+        if not attribute.attrName in attributes:
+            print "Not in attributes, adding"
+            attributes[attribute.attrName] = None
+
+    print attributes
+
+    # Create a new CCB in the cache if needed
+    if not ccb_id in 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 completed_ccb(oi_handle, ccb_id):
+    ''' Callback for CCB completed
+
+        Validates any configured containments and calls the configured 
+        on_validate function 
+    '''
+
+    # 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(abort_ccb)
+callbacks.saImmOiCcbApplyCallback        = \
+    saImmOi.SaImmOiCcbApplyCallbackT(apply_ccb)
+callbacks.saImmOiCcbCompletedCallback    = \
+    saImmOi.SaImmOiCcbCompletedCallbackT(completed_ccb)
+callbacks.saImmOiCcbObjectCreateCallback = \
+    saImmOi.SaImmOiCcbObjectCreateCallbackT_2(create_added)
+callbacks.saImmOiCcbObjectDeleteCallback = \
+    saImmOi.SaImmOiCcbObjectDeleteCallbackT(delete_added)
+callbacks.saImmOiCcbObjectModifyCallback = \
+    saImmOi.SaImmOiCcbObjectModifyCallbackT_2(modify_added)
+callbacks.saImmOiRtAttrUpdateCallback    = \
+    saImmOi.SaImmOiRtAttrUpdateCallbackT(attr_update)
+callbacks.saImmOiAdminOperationCallback  = \
+    saImmOi.SaImmOiAdminOperationCallbackT_2(admin_operation)
+
+
+def AdminOperation(class_name, op_id):
+    ''' Admin operation decorator, marks and returns the function with the
+        specified operation id and returns the function
+    '''
+
+    def inner_admin_op_decorator(func):
+        ''' Inner decorator which actually sets the admin_op id '''
+        
+        setattr(func, 'AdminOperationOperationId', op_id)
+        setattr(func, 'AdminOperationClassName',   class_name)
+        return func
+
+    return inner_admin_op_decorator
+
+class AdminOperationFunction:
+    ''' Encapsulation of an admin operation and its id '''
+
+    def __init__(self, class_name, operation_id, func):
+        ''' Creates a pair of an operation id and a function '''
+
+        self.operation_id = operation_id
+        self.class_name   = class_name
+        self.func         = func
+
+    def execute(self, name, parameters):
+        ''' Executes the admin operation '''
+
+        return self.func(name, parameters)
+
+    def matches(self, class_name, operation_id):
+        ''' Returns true if this admin operation pair matches the given
+            admin operation id
+        '''
+
+        return operation_id == self.operation_id and \
+               class_name == self.class_name
+
+
+class _ContainmentConstraint:
+    ''' Defines a containment constraint '''
+
+    def __init__(self, parent_class, child_class, lower, upper):
+        ''' 
+            Creates a containment constraint with optional lower and upper 
+            cardinality.
+        '''
+        self.parent_class = parent_class
+        self.child_class  = child_class
+        self.lower        = lower
+        self.upper        = upper
+
+
+class Constraints:
+    ''' Defines constraints for changes to the instances implemented by the OI 
'''
+
+    def __init__(self):
+        ''' Creates an empty Constraints instance '''
+        self.containments = {}
+        self.cardinality  = {}
+
+    def add_allowed_containment(self, parent_class, child_class, lower=None, 
upper=None):
+        ''' Adds a constraint on which type of classes can be created 
+            under the parent 
+        '''
+
+        # Store the allowed parent-child relationship
+        if not parent_class in self.containments:
+            self.containments[parent_class] = []
+
+        self.containments[parent_class] = [child_class]
+
+        # Store the cardinality
+        pair = (parent_class, child_class)
+
+        self.cardinality[pair] = [lower, upper]
+
+    def validate(self, all_instances, updated, created, deleted):
+        ''' Validates the constraints in this Constraints instance '''
+
+        # Validate containments
+        for mo in created:
+
+            parent_name = immoi.get_parent_name_for_dn(mo.dn)
+
+            # Handle the case where there is no parent
+            if not parent_name:
+
+                for parent_class, child_classes in 
self.containments.iteritems():
+                    if mo.SaImmAttrClassName in child_classes:
+                        error_string = "ERROR: Cannot create %s, %s must have 
a parent" % \
+                                       (mo.dn, mo.SaImmAttrClassName)
+                        raise 
SafException(eSaAisErrorT.SA_AIS_ERR_INVALID_PARAM, 
+                                           error_string)
+
+            else:
+
+                parent_class = immoi.get_class_name_for_dn(parent_name)
+
+                child_classes = self.containments[parent_class]
+
+                if not mo.SaImmAttrClassName in child_classes:
+                    error_string = "ERROR: Cannot create %s as a child under 
%s" % \
+                                   (mo.dn, parent_class)
+                    raise SafException(eSaAisErrorT.SA_AIS_ERR_INVALID_PARAM,
+                                       error_string)
+
+        # Validate cardinality of containments
+        count_cardinality = {}
+
+        for mo in all_instances:
+
+            parent_name = immoi.get_parent_name_for_dn(mo.dn)
+
+            if parent_name:
+
+                parent_class = immoi.get_class_name_for_dn(parent_name)
+
+                # Count children of each parent-child relationship
+                pair = (parent_class, mo.SaImmAttrClassName)
+
+                if not pair in count_cardinality:
+                    count_cardinality[pair] = 0
+
+                count_cardinality[pair] += 1
+
+        # Verify the cardinality
+        for counted_pair, number in count_cardinality.iteritems():
+            parent_class = counted_pair[0]
+            child_class  = counted_pair[1]
+
+            # Ignore pairs that have no specified cardinality
+            if not counted_pair in self.cardinality:
+                continue
+
+            lower = self.cardinality[counted_pair][0]
+            upper = self.cardinality[counted_pair][1]
+
+            if lower and number < lower:
+                error_string = "ERROR: Must have at least %s instances of %s 
under %s" % \
+                               (lower, child_class, parent_class)
+                raise SafException(eSaAisErrorT.SA_AIS_ERR_INVALID_PARAM,
+                                   error_string)
+            elif upper and number > upper:
+                error_string = "ERROR: Must have at most %s instances of %s 
under %s" % \
+                               (upper, child_class, parent_class)
+                raise SafException(eSaAisErrorT.SA_AIS_ERR_INVALID_PARAM,
+                                   error_string)
+
+
+class Implementer:
+
+    def __init__(self, class_names=[], name="wrapper", on_create=None, 
+                 on_delete=None, on_modify=None, on_validate=None, 
+                 on_apply=None, on_runtime_values_get=None, 
+                 admin_operations=None, constraints=None):
+        ''' Creates an Implementer instance '''
+
+        self.class_names              = class_names
+        self.name                     = name
+        self.on_create_cb             = on_create
+        self.on_delete_cb             = on_delete
+        self.on_modify_cb             = on_modify
+        self.on_validate_cb           = on_validate
+        self.on_apply_cb              = on_apply
+        self.on_runtime_values_get_cb = on_runtime_values_get
+        self.admin_operations         = admin_operations
+        self.constraints              = constraints
+
+        global implementer_instance
+
+        implementer_instance = self
+
+        # Initialize OI API and register as implementer for the classes
+        immoi.cache_class_descriptions(self.class_names)
+
+        self._register()
+
+    def get_implemented_classes(self):
+        ''' Returns a list of the classes this implementer implements '''
+        return self.class_names
+
+    def implement_class(self, class_name):
+        ''' Adds the the given class_name to the list of classes this
+            implementer implements '''
+        immoi.implement_class(class_name)
+
+        self.class_names.append(class_name)
+
+    def set_constraints(self, constraints):
+        ''' Sets constraints to be verified by the OI '''
+        self.constraints = constraints
+
+    def set_admin_operations(self, admin_operations):
+        ''' Sets the admin operations to be executed by the OI '''
+        self.admin_operations = admin_operations
+
+    def on_modify_added(self, attribute_name, modification_type, values):
+        ''' Called when an object modify operation has been added to an
+            ongoing CCB.
+
+            Will call the on_modify parameter if it has been set. This method
+            can also be overridden by a subclass that wants to handle 
+            on_modify_added'''
+
+        if self.on_modify_cb:
+            try:
+                self.on_modify_cb(attribute_name, modification_type, values)
+            except SafException as err:
+                return err.value
+
+        return eSaAisErrorT.SA_AIS_OK
+
+    def on_delete_added(self, dn):
+        ''' Called when an object delete operation has been added to the
+            active CCB.
+
+            Will call the on_delete parameter if it has been set. This method 
+            can also be overridden by a subclass that wants to handle 
+            on_delete_added
+        '''
+
+        if self.on_delete_cb:
+            try:
+                self.on_delete_cb(dn)
+            except SafException as err:
+                return err.value
+
+        return eSaAisErrorT.SA_AIS_OK
+
+    def on_create_added(self, class_name, parent_name, obj):
+        ''' Called when an object create operation has been performed.
+
+            Will call the on_create parameter if it has been set. This
+            method can also be overridden by a subclass that wants to 
+            handle on_create_added
+        '''
+
+        if self.on_create_cb:
+            try:
+                self.on_create_cb(class_name, parent_name, obj)
+            except SafException as err:
+                return err.value
+
+        return eSaAisErrorT.SA_AIS_OK
+
+
+    def on_apply(self, instances, updated, created, deleted):
+        ''' Calls the registered on_apply callback function if it exists.
+
+            Another way to perform apply is to override this method'''
+        if self.on_apply_cb:
+            try:
+                self.on_apply_cb(instances, updated, created, deleted)
+            except SafException as err:
+                return err.value
+
+        return eSaAisErrorT.SA_AIS_OK
+
+    def on_validate(self, instances, updated, created, deleted):
+        ''' Calls the registered on_validate callback function if it exists.
+
+            Another way to perform validation is to override this method.
+         '''
+
+        if self.on_validate_cb:
+            self.on_validate_cb(instances, updated, created, deleted)
+
+    def _validate(self, ccb_id, instances, updated, created, deleted):
+        ''' Performs any configured validation code '''
+
+        try:
+
+            # Validate constraints on the containments (if configured)
+            self.__validate_constraints(instances, updated, created, 
+                                                 deleted)
+
+            # Let the user code validate the CCB (if configured)
+            self.on_validate(instances, updated, created, deleted)
+        except SafException as err:
+            immoi.set_error_string(ccb_id, err.msg)
+            return err.value
+        except:
+            return eSaAisErrorT.SA_AIS_ERR_FAILED_OPERATION
+
+        return eSaAisErrorT.SA_AIS_OK
+
+
+    def enter_dispatch_loop(self):
+        ''' Activates the OI and starts an infinite dispatch loop
+        '''
+        self.__start_dispatch_loop()
+
+    def admin_operation(self, operation_id, object_name, parameters):
+        ''' Executes the specified admin operation and returns the result
+
+            Returns eSaAisErrorT.SA_AIS_ERR_NOT_SUPPORTED if no corresponding
+            operation is registered
+        '''
+
+        # Get the class name
+        class_name = immoi.get_class_name_for_dn(object_name)
+
+        # Find and execute a matching admin operation
+        if self.admin_operations:
+            for admin_operation in self.admin_operations:
+                if admin_operation.matches(class_name, operation_id):
+                    try:
+                        admin_operation.execute(object_name, parameters)
+                        return eSaAisErrorT.SA_AIS_OK
+                    except SafException as err:
+                        print "ERROR: Admin operation %s caused exception %s" 
%\
+                            (operation_id, err)
+                        return err.value
+
+        # Scan for AdminOperation-decorated functions in subclasses
+        for member_name in dir(self):
+
+            func = getattr(self, member_name)
+
+            tmp_id         = getattr(func, 'AdminOperationOperationId', None)
+            tmp_class_name = getattr(func, 'AdminOperationClassName', None)
+
+            if tmp_id == operation_id and tmp_class_name == class_name:
+                try:
+                    func(object_name, parameters)
+                    return eSaAisErrorT.SA_AIS_OK
+                except SafException as err:
+                    print "ERROR: Admin operation %s caused exception %s" % \
+                        (operation_id, err)
+                    return err.value
+
+        # Report that the operation is not supported
+        return eSaAisErrorT.SA_AIS_ERR_NOT_SUPPORTED
+
+    def _register(self):
+        ''' Initializes IMM OI and registers as an OI for the configured 
+            classes 
+        '''
+
+        # Initialize the OI API
+        immoi._initialize(callbacks)
+
+        # Ensure that all classes are configuration classes
+        runtime_classes = filter(lambda c: immoi.get_class_category(c) == \
+                                 eSaImmClassCategoryT.SA_IMM_CLASS_RUNTIME, 
self.class_names)
+        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()
+
+        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)
+
+            else:
+                print "WARNING: %s is missing in IMM. Not becoming 
implementer." % class_name
+
+    def get_selection_object(self):
+        ''' Returns the selection object '''
+        return immoi.SELECTION_OBJECT.value
+
+    def update_runtime_attributes(self, dn, attributes):
+        ''' Updates the given runtime attributes for the specified DN '''
+
+        # Report the updates
+        try:
+            immoi.update_rt_object(dn, attributes)
+        except SafException as err:
+            print "ERROR: Failed to update runtime attributes of %s: %s" % \
+                (dn, err)
+
+    def create(self, obj):
+
+        # 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)
+
+    def delete(self, dn):
+        ''' Deletes a runtime object with the given DN '''
+
+        immoi.delete_rt_object(dn)
+
+    def __start_dispatch_loop(self):
+        ''' Starts an infinite dispatch loop '''
+
+        inputs  = [immoi.SELECTION_OBJECT.value]
+        outputs = []
+
+        # Handle updates
+        while inputs:
+
+            readable, writable, exceptional = select.select(inputs, outputs, 
+                                                            inputs)
+
+            immoi.dispatch()
+
+    def __validate_constraints(self, all_instances, updated, created, 
+                              deleted):
+        ''' Validates configured constraints '''
+        if self.constraints:
+            return self.constraints.validate(all_instances, updated, 
+                                             created, deleted)
+        else:
+            return eSaAisErrorT.SA_AIS_OK
+
+
+class Applier(Implementer):
+    ''' Class representing an applier '''
+
+    def __init__(self, class_names, name="wrapper", on_create=None, 
+                 on_delete=None, on_modify=None, on_apply=None):
+        ''' Creates an Applier instance and runs the Implementer superclass'
+            __init__ method
+        '''
+
+        # Initialize the base class
+        Implementer.__init__(self, class_names=class_names, name=name, 
+                             on_create=on_create, on_delete=on_delete,
+                             on_modify=on_modify, on_apply=on_apply)
+        
+    def _validate(self, instances, updated, created, deleted):
+        ''' Empty validate handler as appliers cannot validate '''
+        return eSaAisErrorT.SA_AIS_OK
+
+    def _register(self):
+        ''' Initializes IMM OI and registers as an applier for the configured 
+            classes 
+        '''
+
+        # Initialize the OI API
+        immoi._initialize(callbacks)
+
+        # Ensure that all classes are configuration classes
+        runtime_classes = filter(lambda c: 
+                                 immoi.get_class_category(c) == \
+                                 eSaImmClassCategoryT.SA_IMM_CLASS_RUNTIME, 
self.class_names)
+        if runtime_classes:
+            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 objects
+        immoi.get_selection_object()
+
+        # 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)
+
+            else:
+                print "WARNING: %s is missing in IMM. Not becoming applier." % 
\
+                    class_name
diff --git a/python/samples/immoi/samples/README 
b/python/samples/immoi/samples/README
new file mode 100644
--- /dev/null
+++ b/python/samples/immoi/samples/README
@@ -0,0 +1,82 @@
+Samples
+=======
+
+The samples use the classes defined in the classes.xml file. Before running 
them, load it:
+
+$ immcfg -f classes.xml
+
+Each sample OI is implemented in two versions, one using straight callbacks 
and one using a class that subclasses Implementer or Applier. The latter is 
named <oi-name>-inheritance-impl
+
+Imm Listener
+------------
+
+The Imm Lister OI demonstrates how to build an applier. It listens to changes 
to SampleClass1 and simply prints them on stdout. It's an applier which means 
that it does not validate any of the changes and it can be run in parallel with 
a proper OI.
+
+Use like this:
+
+$ imm-listener SampleClass1
+
+or, to listen for changes to all configuration classes:
+
+$ imm-listener --all
+
+
+Tones
+-----
+
+The Tones OI demonstrates validation of containment relations. It implements 
classes for the tones Do, Re, Mi, Fa, So, La, Ti and only allows creation of 
each tone under the lower tone, except for Do which can be created without a 
parent.
+
+Use like this:
+
+$ tones &
+
+$ immcfg -c Do doId=1                 (allowed)
+
+$ immcfg -c Re reId=1,doId=1          (allowed)
+
+$ immcfg -c Do doId=1,reId=1,doId=1   (not allowed)
+
+$ immcfg -c Mi miId=1                 (not allowed)
+
+
+Caps
+----
+
+The Caps OI demonstrates validation. It validates that any new CapsSample 
objects and any updated CapsSample objects follow these rules:
+
+  - The upperCaps attribute consists only of uppercase characters
+  - The lowerCaps attribute consists only of lowercase characters
+
+Use like this:
+
+$ caps &
+
+$ immcfg -c CapsSample capsId=<RDN>
+
+$ immcfg -a upperCaps=ABC capsId=<RDN>  (allowed)
+
+$ immcfg -a lowerCaps=Abc capsId=<RDN>  (not allowed)
+
+
+Time Reporter
+-------------
+
+The Time Reporter OI demonstrates update of runtime attributes from an OI and 
inclusion of the OI logic into a select loop owned by the application. It 
creates a runtime object timeId=1 of the class TimeSample and updates its 
hours, minutes and seconds attributes once every second.
+
+Use like this:
+
+$ time-reporter
+
+
+Ping Pong
+---------
+
+The Ping Pong OI demonstrates administrative operations. It creates an object 
pingPongId=1 and responds on admin operations 0 and 1.
+
+Use like this:
+
+$ ping-pong &
+
+$ immadm -o [0|1] pingPongId=1 <parameters..>
+
+
diff --git a/python/samples/immoi/samples/caps 
b/python/samples/immoi/samples/caps
new file mode 100755
--- /dev/null
+++ b/python/samples/immoi/samples/caps
@@ -0,0 +1,42 @@
+#!/usr/bin/env python
+
+import itertools
+
+from pyosaf.saAis import eSaAisErrorT
+
+from pyosaf.utils import SafException
+
+from pyosaf.utils.immoi.implementer import Implementer
+
+caps_class_name = "CapsSample"
+
+def handle_validate(all_instances, updated, created, deleted):
+
+    # Verify that upper and lower caps are correct in created objects
+    # and updated objects
+    for mo in itertools.chain(created, updated):
+
+        # Verify upper caps
+        if mo.upperCaps and not mo.upperCaps.isupper():
+            err_str = "ERROR: %s.upperCaps must be upper case (was %s)" % \
+                (mo.dn, mo.upperCaps)
+            raise SafException(eSaAisErrorT.SA_AIS_ERR_INVALID_PARAM, err_str)
+
+        # Verify lower caps
+        if mo.lowerCaps and not mo.lowerCaps.islower():
+            err_str = "ERROR: %s.lowerCaps must be lower case (was %s)" % \
+                      (mo.dn, mo.lowerCaps)
+            raise SafException(eSaAisErrorT.SA_AIS_ERR_INVALID_PARAM, err_str)
+
+
+if __name__ == "__main__":
+
+    # Create the implementer
+    classes = [caps_class_name]
+
+    caps_implementer = Implementer(on_validate=handle_validate, 
+                                   class_names=classes,
+                                   name="CapsImplementer")
+
+    # Start the infinite dispatch loop
+    caps_implementer.enter_dispatch_loop()
diff --git a/python/samples/immoi/samples/caps-inheritance-impl 
b/python/samples/immoi/samples/caps-inheritance-impl
new file mode 100755
--- /dev/null
+++ b/python/samples/immoi/samples/caps-inheritance-impl
@@ -0,0 +1,49 @@
+#!/usr/bin/env python
+
+import itertools
+
+from pyosaf.saAis import eSaAisErrorT
+
+from pyosaf.utils import SafException
+
+from pyosaf.utils.immoi.implementer import Implementer
+
+caps_class_name = "CapsSample"
+
+class Caps(Implementer):
+    ''' The Caps implementer implements CapsSample and makes sure that
+        the upperCaps and lowerCaps consist only of uppercase characters
+        and lowercase characters respectively.'''
+
+    def __init__(self):
+        ''' Constructs the Caps instance '''
+        Implementer.__init__(self, class_names=[caps_class_name],
+                             name="CapsImplementer")
+
+    def on_validate(self, all_instances, to_update, to_create, to_delete):
+        ''' Validates that the rules for upperCaps and lowerCaps are kept '''
+
+        # Verify that upper and lower caps are correct in created objects
+        # and updated objects
+        for mo in itertools.chain(to_create, to_update):
+
+            # Verify upper caps
+            if mo.upperCaps and not mo.upperCaps.isupper():
+                err_str = "ERROR: %s.upperCaps must be upper case (was %s)" % \
+                    (mo.dn, mo.upperCaps)
+                raise SafException(eSaAisErrorT.SA_AIS_ERR_INVALID_PARAM, 
err_str)
+
+            # Verify lower caps
+            if mo.lowerCaps and not mo.lowerCaps.islower():
+                err_str = "ERROR: %s.lowerCaps must be lower case (was %s)" % \
+                          (mo.dn, mo.lowerCaps)
+                raise SafException(eSaAisErrorT.SA_AIS_ERR_INVALID_PARAM, 
err_str)
+
+
+if __name__ == "__main__":
+
+    # Create the implementer
+    caps_implementer = Caps()
+
+    # Start the infinite dispatch loop
+    caps_implementer.enter_dispatch_loop()
diff --git a/python/samples/immoi/samples/classes.xml 
b/python/samples/immoi/samples/classes.xml
new file mode 100644
--- /dev/null
+++ b/python/samples/immoi/samples/classes.xml
@@ -0,0 +1,187 @@
+<?xml version="1.0"?>
+<imm:IMM-contents xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:noNamespaceSchemaLocation="SAI-AIS-IMM-XSD-A.02.13.xsd" 
xmlns:xs="http://www.w3.org/2001/XMLSchema"; 
xmlns:imm="http://www.saforum.org/IMMSchema";>
+  <class name="SampleClass1">
+    <category>SA_CONFIG</category>
+    <rdn>
+      <name>sampleClassId</name>
+      <type>SA_STRING_T</type>
+      <category>SA_CONFIG</category>
+      <flag>SA_INITIALIZED</flag>
+      <flag>SA_NOTIFY</flag>
+    </rdn>
+    <attr>
+      <name>attribute1</name>
+      <type>SA_STRING_T</type>
+      <category>SA_CONFIG</category>
+      <flag>SA_WRITABLE</flag>
+      <flag>SA_NOTIFY</flag>
+    </attr>
+    <attr>
+      <name>attribute2</name>
+      <type>SA_STRING_T</type>
+      <category>SA_CONFIG</category>
+      <flag>SA_WRITABLE</flag>
+      <flag>SA_NOTIFY</flag>
+    </attr>
+  </class>
+
+  <!-- Tones OI -->
+  <class name="Do">
+    <category>SA_CONFIG</category>
+    <rdn>
+      <name>doId</name>
+      <type>SA_STRING_T</type>
+      <category>SA_CONFIG</category>
+      <flag>SA_INITIALIZED</flag>
+      <flag>SA_NOTIFY</flag>
+    </rdn>
+  </class>
+  <class name="Re">
+    <category>SA_CONFIG</category>
+    <rdn>
+      <name>reId</name>
+      <type>SA_STRING_T</type>
+      <category>SA_CONFIG</category>
+      <flag>SA_INITIALIZED</flag>
+      <flag>SA_NOTIFY</flag>
+    </rdn>
+  </class>
+  <class name="Mi">
+    <category>SA_CONFIG</category>
+    <rdn>
+      <name>miId</name>
+      <type>SA_STRING_T</type>
+      <category>SA_CONFIG</category>
+      <flag>SA_INITIALIZED</flag>
+      <flag>SA_NOTIFY</flag>
+    </rdn>
+  </class>
+  <class name="Fa">
+    <category>SA_CONFIG</category>
+    <rdn>
+      <name>faId</name>
+      <type>SA_STRING_T</type>
+      <category>SA_CONFIG</category>
+      <flag>SA_INITIALIZED</flag>
+      <flag>SA_NOTIFY</flag>
+    </rdn>
+  </class>
+  <class name="So">
+    <category>SA_CONFIG</category>
+    <rdn>
+      <name>soId</name>
+      <type>SA_STRING_T</type>
+      <category>SA_CONFIG</category>
+      <flag>SA_INITIALIZED</flag>
+      <flag>SA_NOTIFY</flag>
+    </rdn>
+  </class>
+  <class name="La">
+    <category>SA_CONFIG</category>
+    <rdn>
+      <name>laId</name>
+      <type>SA_STRING_T</type>
+      <category>SA_CONFIG</category>
+      <flag>SA_INITIALIZED</flag>
+      <flag>SA_NOTIFY</flag>
+    </rdn>
+  </class>
+  <class name="Ti">
+    <category>SA_CONFIG</category>
+    <rdn>
+      <name>tiId</name>
+      <type>SA_STRING_T</type>
+      <category>SA_CONFIG</category>
+      <flag>SA_INITIALIZED</flag>
+      <flag>SA_NOTIFY</flag>
+    </rdn>
+  </class>
+
+  <!-- Ping-pong OI -->
+  <class name="PingPong">
+    <category>SA_CONFIG</category>
+    <rdn>
+      <name>pingPongId</name>
+      <type>SA_STRING_T</type>
+      <category>SA_CONFIG</category>
+      <flag>SA_INITIALIZED</flag>
+      <flag>SA_NOTIFY</flag>
+    </rdn>
+  </class>
+
+  <!-- Caps OI -->
+  <class name="CapsSample">
+    <category>SA_CONFIG</category>
+    <rdn>
+      <name>capsId</name>
+      <type>SA_STRING_T</type>
+      <category>SA_CONFIG</category>
+      <flag>SA_INITIALIZED</flag>
+      <flag>SA_NOTIFY</flag>
+    </rdn>
+    <attr>
+      <name>lowerCaps</name>
+      <type>SA_STRING_T</type>
+      <category>SA_CONFIG</category>
+      <flag>SA_WRITABLE</flag>
+      <flag>SA_NOTIFY</flag>
+    </attr>
+    <attr>
+      <name>upperCaps</name>
+      <type>SA_STRING_T</type>
+      <category>SA_CONFIG</category>
+      <flag>SA_WRITABLE</flag>
+      <flag>SA_NOTIFY</flag>
+    </attr>
+  </class>
+  <class name="InterfaceRO01">
+    <category>SA_RUNTIME</category>
+    <rdn>
+      <name>interfaceId</name>
+      <type>SA_STRING_T</type>
+      <category>SA_RUNTIME</category>
+      <flag>SA_CACHED</flag>
+    </rdn>
+    <attr>
+      <name>ipv4Addresses</name>
+      <type>SA_STRING_T</type>
+      <category>SA_RUNTIME</category>
+      <flag>SA_CACHED</flag>
+      <flag>SA_MULTI_VALUE</flag>
+    </attr>
+    <attr>
+      <name>ipv6Addresses</name>
+      <type>SA_STRING_T</type>
+      <category>SA_RUNTIME</category>
+      <flag>SA_CACHED</flag>
+      <flag>SA_MULTI_VALUE</flag>
+    </attr>
+  </class>
+  <class name="TimeSample">
+    <category>SA_RUNTIME</category>
+    <rdn>
+      <name>timeId</name>
+      <type>SA_STRING_T</type>
+      <category>SA_RUNTIME</category>
+      <flag>SA_CACHED</flag>
+    </rdn>
+    <attr>
+      <name>hours</name>
+      <type>SA_UINT32_T</type>
+      <category>SA_RUNTIME</category>
+      <flag>SA_CACHED</flag>
+    </attr>
+    <attr>
+      <name>minutes</name>
+      <type>SA_UINT32_T</type>
+      <category>SA_RUNTIME</category>
+      <flag>SA_CACHED</flag>
+    </attr>
+    <attr>
+      <name>seconds</name>
+      <type>SA_UINT32_T</type>
+      <category>SA_RUNTIME</category>
+      <flag>SA_CACHED</flag>
+    </attr>
+  </class>
+</imm:IMM-contents>
diff --git a/python/samples/immoi/samples/imm-listener 
b/python/samples/immoi/samples/imm-listener
new file mode 100755
--- /dev/null
+++ b/python/samples/immoi/samples/imm-listener
@@ -0,0 +1,111 @@
+#!/usr/bin/env python
+
+import argparse
+import sys
+
+from pyosaf.saImm import eSaImmClassCategoryT
+
+from pyosaf.utils.immoi import get_available_classes_in_imm, get_class_category
+from pyosaf.utils.immoi.implementer import Applier
+
+
+def is_config_class(class_name):
+
+    category = get_class_category(class_name)
+
+    return category == eSaImmClassCategoryT.SA_IMM_CLASS_CONFIG
+
+
+def parse_class_names():
+
+    return args.classes
+
+def print_mo(mo):
+    ''' Prints an MO instance '''
+
+    print "    %s" % mo.dn
+
+    # Print attributes
+    for name, type_value_pair in mo.attrs.iteritems():
+        value = type_value_pair[1]
+
+        if len(value) == 1:
+            value = value[0]
+
+        print "        - %s: %s" % (name, value)
+
+def handle_apply(all_instances, updated, created, to_delete):
+
+    print
+    print '-' * 10 + " Configuration changed " + '-' * 10
+
+    # Handle creates
+    print "  Created objects"
+    for mo in created:
+        print_mo(mo)
+
+    # Handle deletes
+    print "  Deleted objects"
+    for mo in to_delete:
+        print "    %s" % mo
+
+    # Handle updates
+    print "  Updated objects"
+    for mo in updated:
+        print_mo(mo)
+        
+if __name__ == "__main__":
+
+    # Parse command line arguments
+    parser = argparse.ArgumentParser(
+        description='Listens to changes to objects of the given classes')
+    parser.add_argument('classes', type=str, nargs='*', 
+                        help='space-separated list of class names')
+    parser.add_argument('--all', help='listens to changes to all classes',
+                        dest='all', action='store_const', const=all, 
+                        default=False)
+
+    args = parser.parse_args()
+
+    classes = args.classes
+
+    listen_to_all = args.all
+
+    # Exit if no classes are passed
+    if not listen_to_all and (not classes or classes == []):
+        print "ERROR: Must specify classes to listen to or '--all'"
+        sys.exit(1)
+
+    # Sort out which classes to listen to
+    all_available_classes = get_available_classes_in_imm()
+    classes_to_listen_to = []
+
+    if listen_to_all:
+        print "INFO: Listening to changes to all configuration classes"
+
+        classes_to_listen_to = [c for c in all_available_classes if 
is_config_class(c)]
+
+    else:
+
+        # Validate classes
+        for class_name in classes:
+
+            if not class_name in all_available_classes:
+                print "WARNING: Ignoring %s. It does not exist in IMM" % 
class_name
+
+                continue
+
+            elif not is_config_class(class_name):
+                print "WARNING: Ignoring runtime class %s" % class_name
+
+                continue
+
+            classes_to_listen_to.append(class_name)
+
+    print "INFO: Listening for changes in %s" % ", ".join(classes_to_listen_to)
+
+    echo_applier = Applier(on_apply=handle_apply, 
+                           class_names=classes_to_listen_to,
+                           name="ImmListener")
+
+    echo_applier.enter_dispatch_loop()
diff --git a/python/samples/immoi/samples/imm-listener-inheritance-impl 
b/python/samples/immoi/samples/imm-listener-inheritance-impl
new file mode 100755
--- /dev/null
+++ b/python/samples/immoi/samples/imm-listener-inheritance-impl
@@ -0,0 +1,158 @@
+#!/usr/bin/env python
+
+import argparse
+import sys
+
+from pyosaf.saAis import eSaAisErrorT
+
+from pyosaf.saImm import eSaImmClassCategoryT
+
+from pyosaf.utils.immoi import get_available_classes_in_imm, get_class_category
+from pyosaf.utils.immoi.implementer import Applier
+
+
+class ImmListener(Applier):
+    ''' This class listens to modifications to IMM and prints
+        them on STDOUT. 
+
+        It extends the Applier class and overrides the apply function.
+
+    '''
+
+    def __init__(self, class_names=[]):
+        ''' Creates a listener applier for the given class names '''
+        Applier.__init__(self, class_names, name='ImmListener')
+
+    def on_apply(self, instances, updated, created, deleted):
+        ''' Overrides the apply method in Applier to print
+            any modifications to configuration data in IMM
+        '''
+
+        print
+        print '-' * 10 + " Configuration changed " + '-' * 10
+
+        # Handle creates
+        print "  Created objects"
+        for mo in created:
+            self.print_mo(mo)
+
+        # Handle deletes
+        print "  Deleted objects"
+        for mo in deleted:
+            print "    %s" % mo
+
+        # Handle updates
+        print "  Updated objects"
+        for mo in updated:
+            self.print_mo(mo)
+
+    def on_create_added(self, class_name, parent, obj):
+        ''' Overrides on_create in Applier. This method is called each
+            time a create operation is added to a CCB.
+        '''
+
+        print
+        print
+        print '-' * 10  + " Create added to CCB " + '-' * 10
+        print
+        print "Class: %s" % class_name
+        print "Parent: %s" % parent
+        print
+        self.print_mo(obj)
+        print
+
+        return eSaAisErrorT.SA_AIS_OK
+
+    def on_delete_added(self, dn):
+        ''' Overrides on_delete in Applier. This method is called each
+            time a delete operations is added to a CCB.
+        '''
+
+        print
+        print
+        print '-' * 10 + " Delete added to CCB " + '-' * 10
+        print
+        print "    %s" % dn
+
+        return eSaAisErrorT.SA_AIS_OK
+
+    def print_mo(self, mo):
+        ''' Prints an MO instance '''
+
+        print "    %s" % mo.dn
+
+        # Print attributes
+        for name, type_value_pair in mo.attrs.iteritems():
+            value = type_value_pair[1]
+
+            if len(value) == 1:
+                value = value[0]
+
+            print "        - %s: %s" % (name, value)
+
+
+def is_config_class(class_name):
+    ''' Returns True if the given class is a configuration class,
+        otherwise False
+    '''
+
+    category = get_class_category(class_name)
+
+    return category == eSaImmClassCategoryT.SA_IMM_CLASS_CONFIG
+
+
+if __name__ == "__main__":
+
+    # Parse command line arguments
+    parser = argparse.ArgumentParser(
+        description='Listens to changes to objects of the given classes')
+    parser.add_argument('classes', type=str, nargs='*', 
+                        help='space-separated list of class names')
+    parser.add_argument('--all', help='listens to changes to all classes',
+                        dest='all', action='store_const', const=all, 
+                        default=False)
+
+    args = parser.parse_args()
+
+    classes = args.classes
+
+    listen_to_all = args.all
+
+    # Exit if no classes are passed
+    if not listen_to_all and (not classes or classes == []):
+        print "ERROR: Must specify classes to listen to or '--all'"
+        sys.exit(1)
+
+    # Sort out which classes to listen to
+    all_available_classes = get_available_classes_in_imm()
+    classes_to_listen_to = []
+
+    if listen_to_all:
+        print "INFO: Listening to changes to all configuration classes"
+
+        classes_to_listen_to = [c for c in all_available_classes \
+                                if is_config_class(c)]
+
+    else:
+
+        # Validate classes
+        for class_name in classes:
+
+            if not class_name in all_available_classes:
+                print "WARNING: Ignoring %s. It does not exist in IMM" % 
class_name
+
+                continue
+
+            elif not is_config_class(class_name):
+                print "WARNING: Ignoring runtime class %s" % class_name
+
+                continue
+
+            classes_to_listen_to.append(class_name)
+
+    print "INFO: Listening for changes in %s" % ", ".join(classes_to_listen_to)
+
+    imm_listener = ImmListener(classes_to_listen_to)
+
+    imm_listener.enter_dispatch_loop()
+
diff --git a/python/samples/immoi/samples/interface-handler 
b/python/samples/immoi/samples/interface-handler
new file mode 100755
--- /dev/null
+++ b/python/samples/immoi/samples/interface-handler
@@ -0,0 +1,121 @@
+#!/usr/bin/env python
+
+import argparse
+import sys
+import netifaces
+import select
+
+from pyosaf.saAis import eSaAisErrorT
+
+from pyosaf.utils import immom, immoi
+
+from pyosaf.utils.immom.object import ImmObject
+from pyosaf.utils.immom.iterator import InstanceIterator
+
+
+from pyosaf.utils.immoi import get_object_no_runtime
+from pyosaf.utils.immoi.implementer import Implementer
+
+interface_class_name = "InterfaceRO01"
+timeout = 1
+
+def get_interface_name_from_dn(dn):
+
+    return dn.split(',')[0].split('=')[1]
+
+def create_rt_object_for_interface(implementer, interface):
+
+    mo = ImmObject(class_name=interface_class_name, dn='interfaceId=%s' % 
interface)
+
+    # Set the RDN with the right interface name
+    mo.interfaceId = 'interfaceId=%s' % interface
+
+    # Look up the IPv4 and IPv6 addresses
+    addresses = netifaces.ifaddresses(interface)
+
+    mo.ipv4Addresses = None
+    mo.ipv6Addresses = None
+
+    if netifaces.AF_INET in addresses:
+        ipv4 = [a['addr'] for a in addresses[netifaces.AF_INET]]
+        mo.ipv4Addresses = ipv4
+    else:
+        mo.ipv4Addresses = "dummy"
+
+    if netifaces.AF_INET6 in addresses:
+        ipv6 = [a['addr'] for a in addresses[netifaces.AF_INET6]]
+        mo.ipv6Addresses = ipv6
+    else:
+        mo.ipv6Addresses = "dummy"
+
+    ##
+    ## This exposes a potential fault in IMM.
+    ##
+    ## Cached attributes need to have their values set when 
+    ## the object is created. If there is an interface that lacks
+    ## an IPv4 or IPv6 address, this will be set to None and 
+    ## converted to an empty list in the implementer code. IMM
+    ## does not allow this and responds with SA_AIS_ERR_INVALID_PARAM
+    ##
+
+    implementer.create(mo)
+
+def select_loop(implementer):
+
+    # Get selection object for the implementer
+    selection_object = implementer.get_selection_object()
+
+    # Wait for next IO event or N seconds
+    inputs  = [selection_object]
+    outputs = []
+
+    while inputs:
+
+        readable, writable, exceptional = \
+        select.select(inputs, outputs, inputs, timeout)
+
+        if selection_object in readable:
+            implementer.dispatch()
+        else:
+
+            interfaces = netifaces.interfaces()
+
+            # Add objects for new interfaces
+            for interface in interfaces:
+                if not immoi.get_object_no_runtime('interfaceId=%s' % 
interface,
+                                                   
class_name=interface_class_name):
+                    create_rt_object_for_interface(implementer, interface)
+
+            # Go through existing objects
+            for mo in InstanceIterator('InterfaceRO'):
+                interface_name = get_interface_name_from_dn(mo.dn)
+
+                # Remove objects for deleted interfaces
+                if not mo.dn in interfaces:
+                    implementer.delete(mo.dn)
+
+                # Update interfaces
+                addresses = netifaces.ifaddresses(interface_name)
+
+                ipv4 = None
+                ipv6 = None
+
+                if netifaces.AF_INET in addresses:
+                    ipv4 = [a['addr'] for a in addresses[netifaces.AF_INET]]
+
+                if netifaces.AF_INET6 in addresses:
+                    ipv6 = [a['addr'] for a in addresses[netifaces.AF_INET6]]
+
+                attributes = {'ipv4Addresses' : ipv4,
+                              'ipv6Addresses' : ipv6}
+
+                implementer.update_runtime_attributes(mo.dn, attributes)
+
+
+if __name__ == "__main__":
+
+    # Create the implementer instance
+    interface_implementer = Implementer(name="InterfaceImplementer")
+
+    # Enter an infinite select loop
+    select_loop(interface_implementer)
diff --git a/python/samples/immoi/samples/interface-handler-inheritance-version 
b/python/samples/immoi/samples/interface-handler-inheritance-version
new file mode 100755
--- /dev/null
+++ b/python/samples/immoi/samples/interface-handler-inheritance-version
@@ -0,0 +1,125 @@
+#!/usr/bin/env python
+
+import argparse
+import sys
+import netifaces
+import select
+
+from pyosaf.saAis import eSaAisErrorT
+
+from pyosaf.utils import immom, immoi
+
+from pyosaf.utils.immom.object import ImmObject
+from pyosaf.utils.immom.iterator import InstanceIterator
+
+
+from pyosaf.utils.immoi import get_object_no_runtime
+from pyosaf.utils.immoi.implementer import Implementer
+
+interface_class_name = "InterfaceRO01"
+timeout = 1
+
+class InterfaceImplementer(Implementer):
+    
+
+    def __init__(self, timeout=1):
+        Implementer.__init__(self, name="InterfaceImplementer")
+
+        self.timeout = timeout
+
+    def get_interface_name_from_dn(self, dn):
+
+        return dn.split(',')[0].split('=')[1]
+
+    def create_rt_object_for_interface(self, interface):
+
+        mo = ImmObject(class_name=interface_class_name, dn='interfaceId=%s' % 
interface)
+
+        # Set the RDN with the right interface name
+        mo.interfaceId = 'interfaceId=%s' % interface
+
+        # Look up the IPv4 and IPv6 addresses
+        addresses = netifaces.ifaddresses(interface)
+
+        if netifaces.AF_INET in addresses:
+            ipv4 = [a['addr'] for a in addresses[netifaces.AF_INET]]
+            mo.ipv4Addresses = ipv4
+        else:
+            mo.ipv4Addresses = "dummy"
+
+        if netifaces.AF_INET6 in addresses:
+            ipv6 = [a['addr'] for a in addresses[netifaces.AF_INET6]]
+            mo.ipv6Addresses = ipv6
+        else:
+            mo.ipv6Addresses = "dummy"
+
+        ##
+        ## This exposes a potential fault in IMM.
+        ##
+        ## Cached attributes need to have their values set when 
+        ## the object is created. If there is an interface that lacks
+        ## an IPv4 or IPv6 address, this will be set to None and 
+        ## converted to an empty list in the implementer code. IMM
+        ## does not allow this and responds with SA_AIS_ERR_INVALID_PARAM
+        ##
+        ## Work around this for now by setting dummy values
+        ##
+
+        self.create(mo)
+
+    def enter_dispatch_loop(self):
+
+        # Wait for next IO event or N seconds
+        inputs  = [self.get_selection_object()]
+        outputs = []
+
+        while inputs:
+
+            readable, writable, exceptional = \
+            select.select(inputs, outputs, inputs, timeout)
+
+            if self.get_selection_object() in readable:
+                self.dispatch()
+            else:
+
+                interfaces = netifaces.interfaces()
+
+                # Add objects for new interfaces
+                for interface in interfaces:
+                    if not immoi.get_object_no_runtime('interfaceId=%s' % 
interface,
+                                                       
class_name=interface_class_name):
+                        self.create_rt_object_for_interface(interface)
+
+                # Go through existing objects
+                for mo in InstanceIterator('InterfaceRO'):
+                    interface_name = self.get_interface_name_from_dn(mo.dn)
+
+                    # Remove objects for deleted interfaces
+                    if not mo.dn in interfaces:
+                        self.delete(mo.dn)
+
+                    # Update interfaces
+                    addresses = netifaces.ifaddresses(interface_name)
+
+                    ipv4 = None
+                    ipv6 = None
+
+                    if netifaces.AF_INET in addresses:
+                        ipv4 = [a['addr'] for a in 
addresses[netifaces.AF_INET]]
+
+                    if netifaces.AF_INET6 in addresses:
+                        ipv6 = [a['addr'] for a in 
addresses[netifaces.AF_INET6]]
+
+                    attributes = {'ipv4Addresses' : ipv4,
+                                  'ipv6Addresses' : ipv6}
+
+                    self.update_runtime_attributes(mo.dn, attributes)
+
+
+if __name__ == "__main__":
+
+    # Create the implementer
+    interface_implementer = InterfaceImplementer()
+
+    # Enter an infinite select loop
+    interface_implementer.enter_dispatch_loop()
diff --git a/python/samples/immoi/samples/ping-pong 
b/python/samples/immoi/samples/ping-pong
new file mode 100755
--- /dev/null
+++ b/python/samples/immoi/samples/ping-pong
@@ -0,0 +1,59 @@
+#!/usr/bin/env python
+
+import select
+
+from pyosaf.saAis import eSaAisErrorT
+
+from pyosaf.utils import immom
+from pyosaf.utils.immoi.implementer import Implementer, AdminOperationFunction
+from pyosaf.utils.immom.object import ImmObject
+from pyosaf.utils.immom.ccb import Ccb
+
+class_name = "PingPong"
+dn         = "pingPongId=1"
+
+
+def ping(dn, arguments):
+    print
+    print
+    print "Pong!"
+    print
+
+    print "INFO: I am %s, received arguments %s" % (dn, arguments)
+
+    return eSaAisErrorT.SA_AIS_OK
+
+
+def pong(dn, arguments):
+    print 
+    print
+    print "Ping!"
+    print
+
+    print "INFO: I am %s, received arguments %s" % (dn, arguments)
+
+    return eSaAisErrorT.SA_AIS_OK
+
+
+if __name__ == '__main__':
+
+    # Create the ping-pong instance if it doesn't exist
+    if not immom.get(dn):
+
+        with Ccb(flags=None) as ccb:
+            obj = ImmObject(class_name=class_name, dn=dn)
+
+            obj.pingPongId  = "pingPongId=1"
+
+            ccb.create(obj)
+
+    # Create the implementer instance
+    operations = [AdminOperationFunction(class_name, 0, ping),
+                  AdminOperationFunction(class_name, 1, pong)]
+
+    implementer = Implementer(class_names=[class_name])
+
+    implementer.set_admin_operations(operations)
+
+    # Start dispatch and time update loop
+    implementer.enter_dispatch_loop()
diff --git a/python/samples/immoi/samples/ping-pong-inheritance-impl 
b/python/samples/immoi/samples/ping-pong-inheritance-impl
new file mode 100755
--- /dev/null
+++ b/python/samples/immoi/samples/ping-pong-inheritance-impl
@@ -0,0 +1,53 @@
+#!/usr/bin/env python
+
+import select
+
+from pyosaf.saAis import eSaAisErrorT
+
+from pyosaf.utils import immom
+from pyosaf.utils.immoi.implementer import Implementer, AdminOperation
+from pyosaf.utils.immom.object import ImmObject
+from pyosaf.utils.immom.ccb import Ccb
+
+class_name = "PingPong"
+dn         = "pingPongId=1"
+
+class PingPong(Implementer):
+
+    def __init__(self):
+        Implementer.__init__(self, class_names=[class_name])
+
+    @AdminOperation(class_name, 0)
+    def ping(self, dn, arguments):
+        print
+        print
+        print "Pong!"
+        print
+        print "INFO: I am %s, received arguments %s" % (dn, arguments)
+
+    @AdminOperation(class_name, 1)
+    def pong(self, dn, arguments):
+        print 
+        print
+        print "Ping!"
+        print
+        print "INFO: I am %s, received arguments %s" % (dn, arguments)
+
+
+if __name__ == '__main__':
+
+    # Create the ping-pong instance if it doesn't exist
+    if not immom.get(dn):
+
+        with Ccb(flags=None) as ccb:
+            obj = ImmObject(class_name=class_name, dn=dn)
+
+            obj.pingPongId  = "pingPongId=1"
+
+            ccb.create(obj)
+
+    # Create the implementer instance
+    implementer = PingPong()
+
+    # Start dispatch and time update loop
+    implementer.enter_dispatch_loop()
diff --git a/python/samples/immoi/samples/time-reporter 
b/python/samples/immoi/samples/time-reporter
new file mode 100755
--- /dev/null
+++ b/python/samples/immoi/samples/time-reporter
@@ -0,0 +1,67 @@
+#!/usr/bin/env python
+
+import select
+import datetime
+
+from pyosaf.utils import immom, immoi
+from pyosaf.utils.immoi.implementer import Implementer
+from pyosaf.utils.immom.object import ImmObject
+
+class_name = "TimeSample"
+dn         = "timeId=1"
+timeout    = 1
+
+def get_time():
+    now = datetime.datetime.now()
+
+    return (now.hour, now.minute, now.second)
+
+def select_loop(implementer):
+
+    # Get selection object for the implementer
+    selection_object = implementer.get_selection_object()
+
+    # Wait for next OI event or one second timeout
+    inputs  = [selection_object]
+    outputs = []
+
+    while inputs:
+
+        readable, writable, exceptional = \
+        select.select(inputs, outputs, inputs, timeout)
+
+        if selection_object in readable:
+            implementer.dispatch()
+        else:
+
+            # Update the time
+            (hours, minutes, seconds) = get_time()
+
+            attributes = {'hours'   : hours,
+                          'minutes' : minutes,
+                          'seconds' : seconds}
+
+            implementer.update_runtime_attributes(dn, attributes)
+
+if __name__ == '__main__':
+
+    # Create the implementer instance
+    time_implementer = Implementer(name="TimeReporter")
+
+    # Get the current time
+    (hours, minutes, seconds) = get_time()
+
+    # Create the time instance if it doesn't exist
+    if not immoi.get_object_no_runtime(dn):
+
+        obj = ImmObject(class_name=class_name, dn=dn)
+
+        obj.hours   = hours
+        obj.minutes = minutes
+        obj.seconds = seconds
+        obj.timeId  = "timeId=1"
+
+        time_implementer.create(obj)
+
+    # Start dispatch and time update loop
+    select_loop(time_implementer)
diff --git a/python/samples/immoi/samples/time-reporter-inheritance-impl 
b/python/samples/immoi/samples/time-reporter-inheritance-impl
new file mode 100755
--- /dev/null
+++ b/python/samples/immoi/samples/time-reporter-inheritance-impl
@@ -0,0 +1,82 @@
+#!/usr/bin/env python
+
+import select
+import datetime
+
+from pyosaf.utils import immom, immoi
+from pyosaf.utils.immoi.implementer import Implementer
+from pyosaf.utils.immom.object import ImmObject
+
+class_name = "TimeSample"
+dn         = "timeId=1"
+timeout    = 1
+
+class TimeReporter(Implementer):
+    ''' This implementer creates a runtime object and updates it with
+        the current time according to the configured timeout (in seconds)'''
+
+    def __init__(self, timeout=1, time_id=1):
+        ''' Initializes this time reporter instance '''
+
+        Implementer.__init__(self, name="TimeReporter")
+
+        self.timeout = timeout
+        self.time_id = time_id
+
+    def create_time_instance(self):
+
+        # Create the time instance if it doesn't exist
+        dn = "timeId=%s" % self.time_id
+
+        if not immoi.get_object_no_runtime(dn):
+
+            now = datetime.datetime.now()
+
+            obj = ImmObject(class_name=class_name, dn=dn)
+
+            obj.hours   = now.hour
+            obj.minutes = now.minute
+            obj.seconds = now.second
+            obj.timeId  = "timeId=%" % self.time_id
+
+            time_implementer.create(obj)
+
+    def enter_dispatch_loop(self):
+        ''' 
+            Loops continuously and dispatches callbacks or updates the 
+            current time
+        '''
+
+        # Get selection object for the implementer
+        selection_object = self.get_selection_object()
+
+        # Wait for next OI event or one second timeout
+        inputs  = [selection_object]
+        outputs = []
+
+        while inputs:
+
+            readable, writable, exceptional = \
+            select.select(inputs, outputs, inputs, self.timeout)
+
+            if selection_object in readable:
+                self.dispatch()
+            else:
+
+                # Update the time
+                now = datetime.datetime.now()
+
+                attributes = {'hours'   : now.hour,
+                              'minutes' : now.minute,
+                              'seconds' : now.second}
+
+                self.update_runtime_attributes(dn, attributes)
+
+if __name__ == '__main__':
+
+    # Create the implementer instance
+    time_implementer = TimeReporter(time_id=1)
+
+
+    # Start dispatch and time update loop
+    time_implementer.enter_dispatch_loop()
diff --git a/python/samples/immoi/samples/tones 
b/python/samples/immoi/samples/tones
new file mode 100755
--- /dev/null
+++ b/python/samples/immoi/samples/tones
@@ -0,0 +1,27 @@
+#!/usr/bin/env python
+
+from pyosaf.utils.immoi import implementer
+
+tone_class_names = ["Do", "Re", "Mi", "Fa", "So", "La", "Ti"]
+
+        
+if __name__ == "__main__":
+
+    # Set up containment constraints
+    constraints = implementer.Constraints()
+
+    constraints.add_allowed_containment('Do', 'Re', upper=1)
+    constraints.add_allowed_containment('Re', 'Mi', upper=1)
+    constraints.add_allowed_containment('Mi', 'Fa', upper=1)
+    constraints.add_allowed_containment('Fa', 'So', upper=1)
+    constraints.add_allowed_containment('So', 'La', upper=1)
+    constraints.add_allowed_containment('La', 'Ti', upper=1)
+
+    # Create the implementer
+    tones_implementer = implementer.Implementer(class_names=tone_class_names,
+                                                name="TonesImplementer")
+
+    tones_implementer.set_constraints(constraints)
+
+    # Start the infinite dispatch loop
+    tones_implementer.enter_dispatch_loop()
diff --git a/python/samples/immoi/samples/tones-inheritance-impl 
b/python/samples/immoi/samples/tones-inheritance-impl
new file mode 100755
--- /dev/null
+++ b/python/samples/immoi/samples/tones-inheritance-impl
@@ -0,0 +1,32 @@
+#!/usr/bin/env python
+
+from pyosaf.utils.immoi.implementer import Implementer, Constraints
+
+class Tones(Implementer):
+
+    def __init__(self):
+        tone_class_names = ["Do", "Re", "Mi", "Fa", "So", "La", "Ti"]
+
+        Implementer.__init__(self, class_names=tone_class_names,
+                             name="TonesImplementer")
+
+        # Set up containment constraints
+        constraints = Constraints()
+
+        constraints.add_allowed_containment('Do', 'Re', upper=1)
+        constraints.add_allowed_containment('Re', 'Mi', upper=1)
+        constraints.add_allowed_containment('Mi', 'Fa', upper=1)
+        constraints.add_allowed_containment('Fa', 'So', upper=1)
+        constraints.add_allowed_containment('So', 'La', upper=1)
+        constraints.add_allowed_containment('La', 'Ti', upper=1)
+
+        self.set_constraints(constraints)
+
+        
+if __name__ == "__main__":
+
+    # Create the implementer
+    tones_implementer = Tones()
+
+    # Start the infinite dispatch loop
+    tones_implementer.enter_dispatch_loop()

------------------------------------------------------------------------------
_______________________________________________
Opensaf-devel mailing list
Opensaf-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/opensaf-devel

Reply via email to