--- 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