SMF imm operation lists may contain duplicate create operations.
Fix, rollback data shall not be stored for duplicate operations and
duplicate operations shall not be added to the CCB.
Fix, crash in SmfUtil::doImmoperation if the CCB fails
---
 opensaf.spec.in                               |   3 +-
 src/smf/Makefile.am                           |  42 ++-
 src/smf/smfd/SmfRollback.h                    |  11 -
 src/smf/smfd/SmfUtils.cc                      |  73 ++++-
 src/smf/smfd/SmfUtils_ObjExist.cc             | 297 ++++++++++++++++++
 src/smf/smfd/SmfUtils_ObjExist.h              |  67 ++++
 .../imm_modify_config/add_operation_to_ccb.cc |   7 +-
 src/smf/smfd/imm_modify_config/attribute.cc   |   6 +
 src/smf/smfd/imm_modify_config/immccb.h       |  53 +++-
 src/smf/smfd/imm_modify_demo/Makefile         |   3 +-
 src/smf/smfd/imm_modify_demo/README           |  10 +-
 src/smf/smfd/imm_modify_demo/common.cc        |   4 +-
 src/smf/smfd/imm_modify_demo/common.h         |   3 +
 .../{ccbhdl_test.cc => test_ccbhdl.cc}        |  45 ++-
 src/smf/smfd/imm_modify_demo/test_objexist.cc | 127 ++++++++
 15 files changed, 681 insertions(+), 70 deletions(-)
 create mode 100644 src/smf/smfd/SmfUtils_ObjExist.cc
 create mode 100644 src/smf/smfd/SmfUtils_ObjExist.h
 rename src/smf/smfd/imm_modify_demo/{ccbhdl_test.cc => test_ccbhdl.cc} (95%)
 create mode 100644 src/smf/smfd/imm_modify_demo/test_objexist.cc

diff --git a/opensaf.spec.in b/opensaf.spec.in
index 3d7ac28a9..5ae2070d4 100644
--- a/opensaf.spec.in
+++ b/opensaf.spec.in
@@ -1538,7 +1538,8 @@ fi
 %{_bindir}/ccbdemo_create
 %{_bindir}/ccbdemo_delete
 %{_bindir}/ccbdemo_modify
-%{_bindir}/ccbhdl_test
+%{_bindir}/test_ccbhdl
+%{_bindir}/test_objexist
 %endif
 %if %is_ais_ckpt
 %{_bindir}/ckpttest
diff --git a/src/smf/Makefile.am b/src/smf/Makefile.am
index 3502f1546..5eed2e683 100644
--- a/src/smf/Makefile.am
+++ b/src/smf/Makefile.am
@@ -106,6 +106,7 @@ noinst_HEADERS += \
        src/smf/smfd/SmfUpgradeProcedure.h \
        src/smf/smfd/SmfUpgradeStep.h \
        src/smf/smfd/SmfUtils.h \
+       src/smf/smfd/SmfUtils_ObjExist.h \
        src/smf/smfd/smfd.h \
        src/smf/smfd/smfd_cb.h \
        src/smf/smfd/smfd_evt.h \
@@ -198,6 +199,7 @@ bin_osafsmfd_SOURCES = \
        src/smf/smfd/SmfUpgradeProcedure.cc \
        src/smf/smfd/SmfUpgradeStep.cc \
        src/smf/smfd/SmfUtils.cc \
+       src/smf/smfd/SmfUtils_ObjExist.cc \
        src/smf/smfd/smfd_amf.cc \
        src/smf/smfd/smfd_campaign_oi.cc \
        src/smf/smfd/smfd_evt.c \
@@ -261,7 +263,12 @@ endif
 # Demos and test
 if ENABLE_TESTS
 
-bin_PROGRAMS += bin/ccbdemo_create bin/ccbdemo_delete bin/ccbdemo_modify 
bin/ccbhdl_test
+bin_PROGRAMS += \
+       bin/ccbdemo_create \
+       bin/ccbdemo_delete \
+       bin/ccbdemo_modify \
+       bin/test_ccbhdl \
+       bin/test_objexist
 
 dist_pkgimmxml_svc_DATA += \
        src/smf/smfd/imm_modify_demo/democlass.xml
@@ -377,26 +384,26 @@ bin_ccbdemo_modify_LDADD = \
        lib/libSaImmOm.la \
        lib/libopensaf_core.la
 
-# CCBHDL Test
+# Test CCBHDL
 # -----------
-bin_ccbhdl_test_CFLAGS = $(AM_CFLAGS) -Wformat=1
+bin_test_ccbhdl_CFLAGS = $(AM_CFLAGS) -Wformat=1
 
-bin_ccbhdl_test_CPPFLAGS = \
+bin_test_ccbhdl_CPPFLAGS = \
        -DSA_EXTENDED_NAME_SOURCE \
        $(AM_CPPFLAGS)
 
-bin_ccbhdl_test_SOURCES = \
+bin_test_ccbhdl_SOURCES = \
        src/smf/smfd/imm_modify_demo/common.cc \
-       src/smf/smfd/imm_modify_demo/ccbhdl_test.cc
+       src/smf/smfd/imm_modify_demo/test_ccbhdl.cc
 
 # IMM configuration modifier
-bin_ccbhdl_test_SOURCES += \
+bin_test_ccbhdl_SOURCES += \
        src/smf/smfd/imm_modify_config/attribute.cc \
        src/smf/smfd/imm_modify_config/add_operation_to_ccb.cc \
        src/smf/smfd/imm_modify_config/immccb.cc
 
 # IMM OM C++ APIs
-bin_ccbhdl_test_SOURCES += \
+bin_test_ccbhdl_SOURCES += \
        src/smf/smfd/imm_om_ccapi/common/common.cc \
        src/smf/smfd/imm_om_ccapi/common/imm_attribute.cc \
        src/smf/smfd/imm_om_ccapi/om_admin_owner_clear.cc \
@@ -408,7 +415,24 @@ bin_ccbhdl_test_SOURCES += \
        src/smf/smfd/imm_om_ccapi/om_ccb_object_modify.cc \
        src/smf/smfd/imm_om_ccapi/om_handle.cc
 
-bin_ccbhdl_test_LDADD = \
+bin_test_ccbhdl_LDADD = \
+       lib/libosaf_common.la \
+       lib/libSaImmOm.la \
+       lib/libopensaf_core.la
+
+# Test OBJEXIST
+# -------------
+bin_test_objexist_CFLAGS = $(AM_CFLAGS) -Wformat=1
+
+bin_test_objexist_CPPFLAGS = \
+       -DSA_EXTENDED_NAME_SOURCE \
+       $(AM_CPPFLAGS)
+
+bin_test_objexist_SOURCES = \
+       src/smf/smfd/SmfUtils_ObjExist.cc \
+       src/smf/smfd/imm_modify_demo/test_objexist.cc
+
+bin_test_objexist_LDADD = \
        lib/libosaf_common.la \
        lib/libSaImmOm.la \
        lib/libopensaf_core.la
diff --git a/src/smf/smfd/SmfRollback.h b/src/smf/smfd/SmfRollback.h
index fbbe2cf85..bd4dd7ff6 100644
--- a/src/smf/smfd/SmfRollback.h
+++ b/src/smf/smfd/SmfRollback.h
@@ -213,17 +213,6 @@ class SmfRollbackCcb {
   ///
   void addCcbData(SmfRollbackData* i_data);
 
-  // Clear the list of SmfRollbackData
-  // Note: The list consists of pointers to SmfRollbackData objects. This
-  // clear method will delete all SmfRollbackData objects and after that
-  // the list will be cleared.
-  void clearCcbData() {
-    for (auto& data : m_rollbackData) {
-      delete data;
-    }
-    m_rollbackData.clear();
-  }
-
  protected:
   std::string m_dn; /* dn to where this rollback CCB should be stored/read */
   std::list<SmfRollbackData*> m_rollbackData; /* Rollback data for this CCB */
diff --git a/src/smf/smfd/SmfUtils.cc b/src/smf/smfd/SmfUtils.cc
index 8815faa63..77d424a65 100644
--- a/src/smf/smfd/SmfUtils.cc
+++ b/src/smf/smfd/SmfUtils.cc
@@ -30,9 +30,11 @@
 #include "ais/include/saAis.h"
 #include "ais/include/saAmf.h"
 #include "ais/include/saSmf.h"
+#include "ais/include/saImmOm.h"
 #include "base/saf_error.h"
 #include "base/osaf_extended_name.h"
 #include "base/osaf_time.h"
+#include "base/time.h"
 
 #include "osaf/immutil/immutil.h"
 
@@ -41,6 +43,7 @@
 #include "smf/smfd/smfd_smfnd.h"
 #include "smf/smfd/SmfImmOperation.h"
 #include "smf/smfd/SmfRollback.h"
+#include "SmfUtils_ObjExist.h"
 
 /* ========================================================================
  *   DEFINITIONS
@@ -640,8 +643,17 @@ done:
 // -----------------
 // doImmOperations()
 // -----------------
-
-// Help function for doImmOperations
+// Note: This method will use two IMM Om. The timeout time for each is set to
+// two minutes. This may seem as we may have to wait for up to 4 minutes or
+// longer in some situations but this is not the case.
+// The only reason for a long wait is when creating an OM handle when IMM
+// synchronization is going on. Once the synchronization is done the rest of
+// the OM handling is going fast. However, we don't know when a synchronization
+// is started so a long timeout is needed in all places where an OM handle is
+// requested.
+
+// Help functions for doImmOperations
+// ----------------------------------
 // Check if the class name begin with "SaAmf" and ends with "Type"
 static bool IsAmfTypeClass(const modelmodify::CreateDescriptor&
                             object_to_check) {
@@ -669,6 +681,10 @@ SaAisErrorT SmfImmUtils::doImmOperations(
   modelmodify::ModelModification modifier;
   // Note: ccb flags are default set to 0 (do not require OI)
 
+  // Note: This object should be created once
+  CheckObjectExist object_exist_check;
+  CheckObjectExist::ReturnCode obj_exist_rc = 
CheckObjectExist::ReturnCode::kOk;
+
   for (auto& imm_operation : i_immOperationList) {
     // Create rollback object for each operation in CCB there if requested
     SmfRollbackData *rollbackData = NULL;
@@ -696,15 +712,53 @@ SaAisErrorT SmfImmUtils::doImmOperations(
       // If the name of the object starts with "SaAmf" and ends with "Type"
       // then ignore if the object already exist
       // (set ignore_ais_err_exist = true in imm_modifications)
-      // Note: Is done here since the connection between SmfImmOperation and
+      // Note1: Is done here since the connection between SmfImmOperation and
       // this util function is not strong enough (can be used separately) also
-      // the criteria for ignoring exist is defined here
+      // the criteria for ignoring exist is defined here.
+      // Note2: We must still check if the object actually exist here since the
+      // rollback data that is always created must be deleted if the create
+      // is not done. Otherwise rollback will fail. The way of doing this is
+      // to use an IMM access operation. Handling the CCB is done as a
+      // transaction and it is not possible to see in this loop if an object
+      // creation will be ignored or not.
+      //
+
+      TRACE("%s: Create operation", __FUNCTION__);
+
       modelmodify::CreateDescriptor create_descriptor =
           imm_operation->GetCreateDescriptor();
+
+      // If the object to be created already exists:
+      // - Do not add the create descriptor to the CCB descriptor
+      // - Delete the rollbackData that was created by
+      //   imm_operation->Execute(rollbackData)
       if (IsAmfTypeClass(create_descriptor)) {
-        create_descriptor.ignore_ais_err_exist = true;
+        TRACE("%s: IsAmfTypeClass() true, class = %s", __FUNCTION__,
+              create_descriptor.class_name.c_str());
+
+        obj_exist_rc = object_exist_check.IsExisting(create_descriptor);
+        if (obj_exist_rc == CheckObjectExist::ReturnCode::kOk) {
+          // The object in the create descriptor already exist in the IMM model
+          // The create descriptor shall not be added to the ccb descriptor and
+          // any corresponding rollback data shall be deleted
+          TRACE("%s Object already in IMM model", __FUNCTION__);
+          if (rollbackData != NULL) {
+            delete rollbackData;
+            rollbackData = NULL;
+          }
+        } else if (imm_modifications.AddCreate(create_descriptor) == false) {
+          // The create descriptor is a duplicate and is not added to the CCB
+          // Any corresponding rollbackData shall be deleted
+          TRACE("%s Duplicate Create operation found", __FUNCTION__);
+          if (rollbackData != NULL) {
+            delete rollbackData;
+            rollbackData = NULL;
+          }
+        }
+      } else {
+        // Is not an Amf type class so always add the create descriptor
+        imm_modifications.AddCreate(create_descriptor);
       }
-      imm_modifications.AddCreate(create_descriptor);
     } else if (imm_operation->GetOperationType() == SmfImmOperation::Delete) {
       // Never Fail if an object to delete does not exist
       imm_modifications.AddDelete(imm_operation->GetDeleteDescriptor());
@@ -733,14 +787,11 @@ SaAisErrorT SmfImmUtils::doImmOperations(
         // Operation failed for other reason than AIS error return from IMM
         result = SA_AIS_ERR_FAILED_OPERATION;
       }
+      LOG_NO("%s: DoModelModification() Fail, %s in '%s'", __FUNCTION__,
+             saf_error(result), error_info.api_name.c_str());
     }
   }
 
-  if (result != SA_AIS_OK) {
-    // Clear the rollback ccb list if we fail
-    io_rollbackCcb->clearCcbData();
-  }
-
   return result;
 }
 
diff --git a/src/smf/smfd/SmfUtils_ObjExist.cc 
b/src/smf/smfd/SmfUtils_ObjExist.cc
new file mode 100644
index 000000000..dc9be0323
--- /dev/null
+++ b/src/smf/smfd/SmfUtils_ObjExist.cc
@@ -0,0 +1,297 @@
+/*      -*- OpenSAF  -*-
+ *
+ * Copyright Ericsson AB 2018 - 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 AB
+ *
+ */
+
+#include "smf/smfd/SmfUtils_ObjExist.h"
+
+#include <string.h>
+#include <iostream>
+
+#include "ais/include/saAis.h"
+#include "ais/include/saImmOm.h"
+#include "base/saf_error.h"
+#include "base/osaf_extended_name.h"
+#include "base/time.h"
+
+#include "smf/smfd/imm_modify_config/immccb.h"
+
+  // Check if the object exist. If fail use recovery possibilities
+  // The Object DN is created from class and parent name
+  CheckObjectExist::ReturnCode CheckObjectExist::IsExisting(
+                          modelmodify::CreateDescriptor& create_descriptor) {
+    // Allow for two max time retries
+    base::Timer check_timer(120000);  // 2 minutes
+    ReturnCode rc = ReturnCode::kOk;
+
+    while (check_timer.is_timeout() != true) {
+      if (is_initialized_ != true) {
+        // Only initialize if not already initialized
+        if (OmInitialize() != ReturnCode::kOk) {
+          LOG_NO("%s: OmInitialize(), Fail", __FUNCTION__);
+          rc = ReturnCode::kFail;
+          break;
+        }
+
+        ReturnCode acci_rc = AccesorInitialize();
+        if (acci_rc == ReturnCode::kRestartOm) {
+          is_initialized_ = false;
+          // No delay needed here
+          continue;
+        } else if (acci_rc == kFail) {
+          LOG_NO("%s: AccesorInitialize(), Fail", __FUNCTION__);
+          rc = ReturnCode::kFail;
+          break;
+        }
+      }
+
+      ReturnCode getrdn_rc = GetObjectRdn(create_descriptor);
+      if (getrdn_rc == ReturnCode::kRestartOm) {
+        is_initialized_ = false;
+        // No delay needed here
+        continue;
+      } else if (getrdn_rc == kFail) {
+        LOG_NO("%s: GetObjectRdn(), Fail", __FUNCTION__);
+        is_initialized_ = false;
+        rc = ReturnCode::kFail;
+        break;
+      }
+
+      std::string object_dn = object_rdn_;
+      if (create_descriptor.parent_name.empty() == false) {
+        object_dn += "," + create_descriptor.parent_name;
+      }
+
+      ReturnCode aget_rc = AccessorGet(object_dn);
+      if (aget_rc == ReturnCode::kRestartOm) {
+        is_initialized_ = false;
+        // No delay needed here
+        continue;
+      }
+      if (aget_rc == ReturnCode::kFail) {
+        LOG_NO("%s: AccessorGet(), Fail", __FUNCTION__);
+        is_initialized_ = false;
+        rc = ReturnCode::kFail;
+        break;
+      }
+
+      // If we get here we did not fail and have a valid result
+      rc = aget_rc;
+      break;
+    }  // check_timer.is_timeout()
+    if ((check_timer.is_timeout() == true)
+        && (rc != ReturnCode::kOk)
+        && (rc != ReturnCode::kNotExist)) {
+      LOG_NO("%s: IsExisting Timeout", __FUNCTION__);
+      is_initialized_ = false;
+      rc = ReturnCode::kFail;
+    }
+
+    if ((rc == ReturnCode::kOk) || (rc == ReturnCode::kNotExist)) {
+      is_initialized_ = true;
+    }
+
+    return rc;
+  }
+
+
+  // Update om_handle_
+  CheckObjectExist::ReturnCode CheckObjectExist::OmInitialize() {
+    // Long timeout. We may have to wait for IMM sync
+    base::Timer init_timer(60000);  // 1 minute
+    SaAisErrorT ais_rc = SA_AIS_OK;
+    ReturnCode rc = ReturnCode::kOk;
+
+    if (om_handle_ != 0) {
+      saImmOmFinalize(om_handle_);
+      om_handle_ = 0;
+    }
+
+    while (init_timer.is_timeout() != true) {
+      SaVersionT tmp_version = kImmVersion;
+      ais_rc = saImmOmInitialize(&om_handle_, NULL, &tmp_version);
+      if (ais_rc == SA_AIS_ERR_TRY_AGAIN) {
+        base::Sleep(base::kOneHundredMilliseconds);
+        continue;
+      } else if (ais_rc != SA_AIS_OK) {
+        rc = ReturnCode::kFail;
+        LOG_NO("%s: saImmOmInitialize Fail, %s", __FUNCTION__,
+               saf_error(ais_rc));
+        break;
+      }
+      rc = ReturnCode::kOk;
+      break;
+    }
+    if (init_timer.is_timeout() && ais_rc != SA_AIS_OK) {
+      LOG_NO("%s: saImmOmInitialize Timeout, %s", __FUNCTION__,
+             saf_error(ais_rc));
+      rc = ReturnCode::kFail;
+    }
+
+    return rc;
+  }
+
+  // Update accessor_handle_
+  CheckObjectExist::ReturnCode CheckObjectExist::AccesorInitialize() {
+    base::Timer init_timer(10000);  // 10 seconds
+    SaAisErrorT ais_rc = SA_AIS_OK;
+    ReturnCode rc = ReturnCode::kOk;
+
+    while (init_timer.is_timeout() != true) {
+      ais_rc = saImmOmAccessorInitialize(om_handle_, &accessor_handle_);
+      if (ais_rc == SA_AIS_ERR_TRY_AGAIN) {
+        base::Sleep(base::kOneHundredMilliseconds);
+        continue;
+      } else if (ais_rc == SA_AIS_ERR_BAD_HANDLE) {
+        rc = ReturnCode::kRestartOm;
+        break;
+      } else if (ais_rc != SA_AIS_OK) {
+        LOG_NO("%s: saImmOmAccessorInitialize Fail, %s", __FUNCTION__,
+               saf_error(ais_rc));
+        rc = ReturnCode::kFail;
+        break;
+      }
+      rc = ReturnCode::kOk;
+      break;
+    }
+    if (init_timer.is_timeout() && ais_rc != SA_AIS_OK) {
+      LOG_NO("%s: saImmOmAccessorInitialize Timeout, %s", __FUNCTION__,
+             saf_error(ais_rc));
+      rc = ReturnCode::kFail;
+    }
+
+    return rc;
+  }
+
+  // Check object existence
+  CheckObjectExist::ReturnCode CheckObjectExist::AccessorGet(std::string
+                                                             object_dn) {
+    ReturnCode rc = ReturnCode::kOk;
+    SaAisErrorT ais_rc = SA_AIS_OK;
+    SaImmAttrValuesT_2 **fetched_attributes;  // Dummy, not used
+    SaNameT object_dn_as_sanamet;
+    osaf_extended_name_lend(object_dn.c_str(), &object_dn_as_sanamet);
+
+    base::Timer get_timer(10000);  // 10 seconds
+    while (get_timer.is_timeout() == false) {
+      ais_rc = saImmOmAccessorGet_2(accessor_handle_,
+                                    &object_dn_as_sanamet,
+                                    NULL,
+                                    &fetched_attributes);
+      if (ais_rc == SA_AIS_ERR_TRY_AGAIN) {
+        base::Sleep(base::kOneHundredMilliseconds);
+        continue;
+      }
+      if (ais_rc == SA_AIS_ERR_BAD_HANDLE) {
+        rc = ReturnCode::kRestartOm;
+        break;
+      }
+      if (ais_rc == SA_AIS_ERR_NOT_EXIST) {
+        rc = ReturnCode::kNotExist;
+        break;
+      }
+      if (ais_rc == SA_AIS_OK) {
+        rc = ReturnCode::kOk;
+        break;
+      }
+
+      // All other ais error codes is a fail
+      LOG_NO("%s: saImmOmAccessorGet_2 Fail, %s", __FUNCTION__,
+             saf_error(ais_rc));
+      rc = ReturnCode::kFail;
+      break;
+    }
+    if (get_timer.is_timeout() &&
+        (ais_rc != SA_AIS_OK) &&
+        (ais_rc != SA_AIS_ERR_NOT_EXIST)) {
+      // Timeout fail only if we failed to get a result
+      LOG_NO("%s: saImmOmAccessorGet_2 Timeout, %s", __FUNCTION__,
+             saf_error(ais_rc));
+      rc = ReturnCode::kFail;
+    }
+
+    return rc;
+  }
+
+  // Update object_rdn_
+  // Fail if IMM error or no RDN could be found
+  CheckObjectExist::ReturnCode CheckObjectExist::GetObjectRdn(
+                          modelmodify::CreateDescriptor& create_descriptor ) {
+    SaAisErrorT ais_rc = SA_AIS_OK;
+    ReturnCode rc = ReturnCode::kOk;
+
+    // Get the class descriptor
+    SaImmAttrDefinitionT_2** attr_definitions;
+    SaImmClassCategoryT class_category = SA_IMM_CLASS_CONFIG;
+    SaImmClassNameT class_name = const_cast<char *>
+                                 (create_descriptor.class_name.c_str());
+
+    base::Timer get_timer(10000);  // 10 seconds
+    while (get_timer.is_timeout() == false) {
+      ais_rc = saImmOmClassDescriptionGet_2(om_handle_,
+                                            class_name,
+                                            &class_category,
+                                            &attr_definitions);
+      if (ais_rc == SA_AIS_ERR_TRY_AGAIN) {
+        base::Sleep(base::kOneHundredMilliseconds);
+        continue;
+      } else if (ais_rc == SA_AIS_ERR_BAD_HANDLE) {
+        rc = ReturnCode::kRestartOm;
+        break;
+      } else if (ais_rc != SA_AIS_OK) {
+        LOG_NO("%s: saImmOmClassDescriptionGet_2 Fail, %s", __FUNCTION__,
+               saf_error(ais_rc));
+        rc = ReturnCode::kFail;
+        break;
+      }
+      rc = ReturnCode::kOk;
+      break;
+    }
+    if (get_timer.is_timeout() && ais_rc != SA_AIS_OK) {
+      LOG_NO("%s: saImmOmClassDescriptionGet_2 Timeout, %s", __FUNCTION__,
+             saf_error(ais_rc));
+      rc = ReturnCode::kFail;
+    }
+
+    if (rc == ReturnCode::kOk) {
+      // Find name of RDN attribute
+      std::string rdn_attr;
+      for (int i = 0; attr_definitions[i] != nullptr; i++) {
+        SaImmAttrFlagsT flags = attr_definitions[i]->attrFlags;
+        if ((flags & SA_IMM_ATTR_RDN) == SA_IMM_ATTR_RDN) {
+          rdn_attr = reinterpret_cast<char *>(attr_definitions[i]->attrName);
+          break;
+        }
+      }
+
+      // Find attribute value of RDN attribute (object RDN)
+      for (auto& attribute : create_descriptor.attributes) {
+        if (attribute.attribute_name == rdn_attr) {
+          if (attribute.values_as_strings.empty()) {
+            object_rdn_.clear();
+            rc = ReturnCode::kFail;
+          } else {
+            object_rdn_ = attribute.values_as_strings[0];
+          }
+          break;
+        }
+      }
+    } else {
+      // Failed. No valid object RDN is found
+      object_rdn_.clear();
+    }
+
+    return rc;
+  }
diff --git a/src/smf/smfd/SmfUtils_ObjExist.h b/src/smf/smfd/SmfUtils_ObjExist.h
new file mode 100644
index 000000000..cbcd5f5d5
--- /dev/null
+++ b/src/smf/smfd/SmfUtils_ObjExist.h
@@ -0,0 +1,67 @@
+/*      -*- OpenSAF  -*-
+ *
+ * Copyright Ericsson AB 2018 - 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 AB
+ *
+ */
+
+#ifndef SMFUTILS_OBJEXIST_H
+#define SMFUTILS_OBJEXIST_H
+
+#include <string>
+
+#include "ais/include/saAis.h"
+#include "ais/include/saImmOm.h"
+
+#include "smf/smfd/imm_modify_config/immccb.h"
+
+// Check if an IMM object exists.
+// Return: kOk if object exist, kNotExist if not exist and kFail if failing to
+//         get the information
+// Note1: An IMM access operation is used. An IMM access operation is fast and
+//        done locally on the node.
+// Note2: To make many checks (in a loop) more efficient the handles are
+//        created once.
+class CheckObjectExist {
+ public:
+  enum ReturnCode { kOk, kRestartOm, kFail, kNotExist };
+
+  CheckObjectExist()
+    : is_initialized_(false), om_handle_(0), accessor_handle_(0) {}
+  ~CheckObjectExist() { saImmOmFinalize(om_handle_); }
+
+  // Check if the object exist. If fail use recovery possibilities
+  // The Object DN is created from class and parent name
+  ReturnCode IsExisting(modelmodify::CreateDescriptor& create_descriptor);
+
+ private:
+  // Update om_handle_
+  ReturnCode OmInitialize();
+
+  // Update accessor_handle_
+  ReturnCode AccesorInitialize();
+
+  // Check object existence
+  ReturnCode AccessorGet(std::string object_dn);
+
+  // Update object_rdn_
+  ReturnCode GetObjectRdn(modelmodify::CreateDescriptor& create_descriptor );
+
+  std::string object_rdn_;
+  bool is_initialized_;
+  SaImmHandleT om_handle_;
+  SaImmAccessorHandleT accessor_handle_;
+  const SaVersionT kImmVersion = {'A', 2, 17};
+};
+
+#endif /* SMFUTILS_OBJEXIST_H */
diff --git a/src/smf/smfd/imm_modify_config/add_operation_to_ccb.cc 
b/src/smf/smfd/imm_modify_config/add_operation_to_ccb.cc
index 803556561..44373cd17 100644
--- a/src/smf/smfd/imm_modify_config/add_operation_to_ccb.cc
+++ b/src/smf/smfd/imm_modify_config/add_operation_to_ccb.cc
@@ -94,7 +94,7 @@ int AddCreateToCcb(const SaImmCcbHandleT& ccb_handle,
   if(attributes.AddAttributesForObjectCreate(create_descriptor) == false) {
     LOG_NO("%s: SetAttributeValues() Fail", __FUNCTION__);
     recovery_info = kFail;
-    api_name_ = "";
+    api_name_.clear();
     ais_error_ = SA_AIS_OK;
   }
 
@@ -124,9 +124,10 @@ int AddCreateToCcb(const SaImmCcbHandleT& ccb_handle,
           recovery_info = kContinue;
         } else {
           recovery_info = kFail;
-          api_name_ = "saImmOmCcbObjectCreate_2";
-          ais_error_ = ais_rc;
         }
+        // Note: This information is always needed also if we do not fail
+        api_name_ = "saImmOmCcbObjectCreate_2";
+        ais_error_ = ais_rc;
       } else {
         // Unrecoverable Fail
         LOG_NO("%s: ObjectCreateCcbAdd(), %s, kFail",
diff --git a/src/smf/smfd/imm_modify_config/attribute.cc 
b/src/smf/smfd/imm_modify_config/attribute.cc
index 2f7fc0561..932911012 100644
--- a/src/smf/smfd/imm_modify_config/attribute.cc
+++ b/src/smf/smfd/imm_modify_config/attribute.cc
@@ -104,10 +104,16 @@ static bool StringToNumericValue(const std::string& 
str_value,
 
 bool AttributeHandler::AddAttributesForObjectCreate(const CreateDescriptor&
                                      create_descriptor) {
+  TRACE_ENTER();
   bool rc = true;
   for (auto& attribute_descriptor : create_descriptor.attributes) {
     rc = AddAttribute(attribute_descriptor, Request::kCreate);
+    if (rc == false) {
+      LOG_NO("%s: AddAttribute() Fail", __FUNCTION__);
+      break;
+    }
   }
+
   return rc;
 }
 
diff --git a/src/smf/smfd/imm_modify_config/immccb.h 
b/src/smf/smfd/imm_modify_config/immccb.h
index 583ea59fd..9fd36765e 100644
--- a/src/smf/smfd/imm_modify_config/immccb.h
+++ b/src/smf/smfd/imm_modify_config/immccb.h
@@ -133,12 +133,24 @@ static inline SaImmAttrModificationTypeT 
StringToImmAttrModType(const
 struct AttributeDescriptor {
   std::string attribute_name;
   SaImmValueTypeT value_type;
+  std::vector<std::string> values_as_strings;
 
   void AddValue(std::string value_as_string) {
     values_as_strings.push_back(value_as_string);
+    // To make it possible to compare
+    std::sort(values_as_strings.begin(), values_as_strings.end());
   }
 
-  std::vector<std::string> values_as_strings;
+  bool operator==(const AttributeDescriptor& as) const {
+    return ((as.attribute_name == attribute_name) &&
+            (as.value_type == value_type) &&
+            (as.values_as_strings == values_as_strings));
+  }
+
+  // Compares attribute name only
+  bool operator<(const AttributeDescriptor& as) const {
+    return attribute_name < as.attribute_name;
+  }
 };
 
 // AttributeModifyDescriptor: Describes one attribute to modify
@@ -207,6 +219,19 @@ struct CreateDescriptor {
   std::vector<AttributeDescriptor> attributes;
   void AddAttribute(AttributeDescriptor one_attribute) {
     attributes.push_back(one_attribute);
+    // To make it possible to compare (sorted on attribute name)
+    std::sort(attributes.begin(), attributes.end());
+  }
+
+  // Compare all variables. All equal is true.
+  // Note: The vectors are compared using vector operator ==. This means that
+  // not only the content must be the same but also the order.
+  // TBD() could be handled by sorting
+  bool operator==(const CreateDescriptor& cd) {
+    return ((cd.ignore_ais_err_exist == ignore_ais_err_exist) &&
+            (cd.class_name == class_name) &&
+            (cd.parent_name == parent_name) &&
+            (cd.attributes == attributes));
   }
 };
 
@@ -248,14 +273,28 @@ struct CcbDescriptor {
   std::vector<ModifyDescriptor> modify_descriptors;
 
   // Use these methods to add the modifications
-  void AddCreate(CreateDescriptor one_create_descriptor) {
-    create_descriptors.push_back(one_create_descriptor);
+
+  // Duplicate descriptors cannot be added. If a duplicate
+  // create descriptor is given as input it will not be added.
+  // Returns false if duplicate
+  bool AddCreate(CreateDescriptor new_create_descriptor) {
+    bool is_added = true;
+    for (auto& stored_create_descriptor : create_descriptors) {
+      if (stored_create_descriptor == new_create_descriptor) {
+        is_added = false;
+        break;
+      }
+    }
+    if (is_added == true)
+      create_descriptors.push_back(new_create_descriptor);
+    return is_added;
   }
-  void AddDelete(DeleteDescriptor one_delete_descriptor) {
-    delete_descriptors.push_back(one_delete_descriptor);
+
+  void AddDelete(DeleteDescriptor new_delete_descriptor) {
+    delete_descriptors.push_back(new_delete_descriptor);
   }
-  void AddModify(ModifyDescriptor one_modify_descriptor) {
-    modify_descriptors.push_back(one_modify_descriptor);
+  void AddModify(ModifyDescriptor new_modify_descriptor) {
+    modify_descriptors.push_back(new_modify_descriptor);
   }
 };
 
diff --git a/src/smf/smfd/imm_modify_demo/Makefile 
b/src/smf/smfd/imm_modify_demo/Makefile
index 8dc274034..8fc621c03 100644
--- a/src/smf/smfd/imm_modify_demo/Makefile
+++ b/src/smf/smfd/imm_modify_demo/Makefile
@@ -15,5 +15,6 @@
 #
 
 all:
-       $(MAKE) -C ../../../.. bin/ccbdemo_create bin/ccbdemo_delete 
bin/ccbdemo_modify bin/ccbhdl_test
+       $(MAKE) -C ../../../.. bin/ccbdemo_create bin/ccbdemo_delete \
+       bin/ccbdemo_modify bin/test_ccbhdl bin/test_objexist
 
diff --git a/src/smf/smfd/imm_modify_demo/README 
b/src/smf/smfd/imm_modify_demo/README
index f202472e6..a575cd2d1 100644
--- a/src/smf/smfd/imm_modify_demo/README
+++ b/src/smf/smfd/imm_modify_demo/README
@@ -53,7 +53,8 @@ with immlist and immfind to check the results
 
 Running the test program:
 -------------------------
-$ ccbhdl_test
+Test the immccb handler:
+$ test_ccbhdl
 
 The test program is interactive. It runs a sequence prints out what has been
 done and suggests what the user should verify and stops execution. Execution
@@ -66,3 +67,10 @@ The test program executes a number of CCB operations on 
order to test:
   different scopes e.g.
   - Create a modifier object and use it once and let it go out of scope
   - Create a modifier object that is reused to create and apply several CCBs
+
+Test SmfUtils_ObjExist:
+
+$ test_objexist
+
+This test is dependent on that democlass.xml is installed. This will be done
+if --enable-tests and --enable-immxml is used with the ./configure script
diff --git a/src/smf/smfd/imm_modify_demo/common.cc 
b/src/smf/smfd/imm_modify_demo/common.cc
index 4b0a678b5..7c3ab8e91 100644
--- a/src/smf/smfd/imm_modify_demo/common.cc
+++ b/src/smf/smfd/imm_modify_demo/common.cc
@@ -31,7 +31,7 @@
 
 using namespace std;
 
-static bool ExecuteCommand(const char* command) {
+bool ExecuteCommand(const char* command) {
   bool function_rc = true;
 
   int rc = system(command);
@@ -41,7 +41,7 @@ static bool ExecuteCommand(const char* command) {
   } else {
     rc = WEXITSTATUS(rc);
     if (rc != 0) {
-      cout << "Command '%s', FAIL" << endl;
+      cout << "Command '" << command << "' ,FAIL" << endl;
       function_rc = false;
     }
   }
diff --git a/src/smf/smfd/imm_modify_demo/common.h 
b/src/smf/smfd/imm_modify_demo/common.h
index 69649c3e0..4493dd7fe 100644
--- a/src/smf/smfd/imm_modify_demo/common.h
+++ b/src/smf/smfd/imm_modify_demo/common.h
@@ -22,6 +22,9 @@
 #ifndef COMMON_H
 #define COMMON_H
 
+// Execute a shell command using system
+bool ExecuteCommand(const char* command);
+
 // Enable long DN handling in IMM by setting longDnsAllowed=1 in IMM
 // configuration object
 bool EnableImmLongDn(void);
diff --git a/src/smf/smfd/imm_modify_demo/ccbhdl_test.cc 
b/src/smf/smfd/imm_modify_demo/test_ccbhdl.cc
similarity index 95%
rename from src/smf/smfd/imm_modify_demo/ccbhdl_test.cc
rename to src/smf/smfd/imm_modify_demo/test_ccbhdl.cc
index b8ddef9bf..35ccf4ddc 100644
--- a/src/smf/smfd/imm_modify_demo/ccbhdl_test.cc
+++ b/src/smf/smfd/imm_modify_demo/test_ccbhdl.cc
@@ -205,7 +205,7 @@ void WaitForUserAction(const std::string& message) {
 }
 
 int main() {
-  cout << "ccbhdl_test" << endl;
+  cout << "test_ccbhdl" << endl;
   cout << "IMM class used for test: ImmTestValuesConfig" << endl;
 
 #if 0 //  Enable trace
@@ -224,16 +224,19 @@ int main() {
   // An IMM demo class is used. Is installed here
   // --------------------------------------------
   if (InstallDemoClass() == false) return -1;
+#endif
 
+#if 1
   // Prepare/enable extended name
   // ----------------------------
   if (EnableImmLongDn() == false) return -1;
-#endif
+
   setenv("SA_ENABLE_EXTENDED_NAMES", "1", 1);
   osaf_extended_name_init();
   // Note: Long DN must be configured in IMM configuration object before
   // running ccbdemo_...
   // DN for that object is opensafImm=opensafImm,safApp=safImmService
+#endif
 
   // ==================== TEST START 
===========================================
 
@@ -274,7 +277,7 @@ int main() {
   if (modifier.DoModelModification(ccb_descriptor) == false) {
     cout << "Create two more objects in the same CCB, FAIL" << endl;
   } else {
-    cout << "Create two more objects using one CCB, SUCCESS" << endl;
+    cout << "Create two more objects in the same CCB, SUCCESS" << endl;
   }
   cout << endl;
 
@@ -470,9 +473,7 @@ int main() {
   create_descriptor.AddAttribute(attribute_descriptor);
 
   // Reuse the ccb_descriptor and add the object creation
-  ccb_descriptor.create_descriptors.clear();
-  ccb_descriptor.delete_descriptors.clear();
-  ccb_descriptor.modify_descriptors.clear();
+  CcbDescriptorCleaner(ccb_descriptor);
   ccb_descriptor.AddCreate(create_descriptor);
 
   // Do the modification
@@ -497,9 +498,10 @@ int main() {
     cout << "AIS error code: " << saf_error(error_info.ais_error) << endl;
     cout << "This is an internal error so there shall be no valid AIS error "
             "code or AIS API-name" << endl;
-
+    cout << "If 'Failng API:' is empty then test is SUCCESS" << endl;
   } else {
     cout << "Creation of '" << object_name << "' SUCCESS" << endl;
+    cout << "Test case FAIL" << endl;
   }
 
   // cout << endl;
@@ -537,9 +539,7 @@ int main() {
   create_descriptor.AddAttribute(attribute_descriptor);
 
   // Reuse the ccb_descriptor and add the object creation
-  ccb_descriptor.create_descriptors.clear();
-  ccb_descriptor.delete_descriptors.clear();
-  ccb_descriptor.modify_descriptors.clear();
+  CcbDescriptorCleaner(ccb_descriptor);
   ccb_descriptor.AddCreate(create_descriptor);
 
   // Do the modification
@@ -555,8 +555,7 @@ int main() {
     modifier.GetErrorInformation(error_info);
     cout << "Failing API: '" << error_info.api_name << "' " << endl;
     cout << "AIS error code: " << saf_error(error_info.ais_error) << endl;
-    cout << "This is an internal error so there shall be no valid AIS error "
-            "code or AIS API-name" << endl << endl;
+    cout << endl;
 
     cout << "First object shall be created with no FAIL" << endl;
     cout << "Test FAIL" << endl;
@@ -575,12 +574,10 @@ int main() {
     modifier.GetErrorInformation(error_info);
     cout << "Failing API: '" << error_info.api_name << "' " << endl;
     cout << "AIS error code: " << saf_error(error_info.ais_error) << endl;
-    cout << "This is not an internal error so there shall be a valid AIS error 
"
-            "code and AIS API-name" << endl;
 
-    cout << "Creation of second object shall FAIL. Test SUCCESS" << endl;
+    cout << "Second creation of same object shall FAIL. Test SUCCESS" << endl;
   } else {
-    cout << "Creation of object did not fail. Test FAIL" << endl;
+    cout << "Second creation of same object did not FAIL. Test FAIL" << endl;
   }
   cout << endl;
 
@@ -599,22 +596,22 @@ int main() {
     modifier.GetErrorInformation(error_info);
     cout << "Failing API: '" << error_info.api_name << "' " << endl;
     cout << "AIS error code: " << saf_error(error_info.ais_error) << endl;
-    cout << "This is not an internal error so there shall be a valid AIS error 
"
-            "code and AIS API-name" << endl;
 
-    cout << "Creation of third object failed. Test FAIL" << endl;
+    cout << "Third creation of same object failed. Test FAIL" << endl;
   } else {
-    cout << "Creation of object did not fail. Test SUCCESS" << endl;
+    cout << "Third creation of same object did not fail. Test SUCCESS" << endl;
   }
   cout << endl;
 
-  // Try deleting an object that does not exist; ignore_ais_err_not_exist = 
true
+  // Try deleting an object that does not exist;
+  // ignore_ais_err_not_exist = true
+  // Shall SUCCEED but no object is deleted
   std::string object_name_not_exist = "Test12=100";
   cout << "Deleting object that does not exist. Flag ignore_ais_err_not_exist "
       "= true (default)" << endl;
   delete_descriptor.object_name = object_name_not_exist + "," +
                                   create_descriptor.parent_name;
-  ccb_descriptor.create_descriptors.clear();
+  CcbDescriptorCleaner(ccb_descriptor);
   ccb_descriptor.AddDelete(delete_descriptor);
   if (modifier.DoModelModification(ccb_descriptor) == false) {
     cout << "Deleting object '" << delete_descriptor.object_name << "' FAIL"
@@ -628,7 +625,7 @@ int main() {
   delete_descriptor.object_name = object_name + "," +
                                   create_descriptor.parent_name;
   delete_descriptor.ignore_ais_err_not_exist = true;
-  ccb_descriptor.create_descriptors.clear();
+  CcbDescriptorCleaner(ccb_descriptor);
   ccb_descriptor.AddDelete(delete_descriptor);
   if (modifier.DoModelModification(ccb_descriptor) == false) {
     cout << "Deleting object '" << delete_descriptor.object_name << "' FAIL"
@@ -642,7 +639,7 @@ int main() {
   delete_descriptor.object_name = object_name_not_exist + "," +
                                   create_descriptor.parent_name;
   delete_descriptor.ignore_ais_err_not_exist = false;
-  ccb_descriptor.create_descriptors.clear();
+  CcbDescriptorCleaner(ccb_descriptor);
   ccb_descriptor.AddDelete(delete_descriptor);
   if (modifier.DoModelModification(ccb_descriptor) == false) {
     cout << "Deleting object '" << delete_descriptor.object_name << "' FAIL"
diff --git a/src/smf/smfd/imm_modify_demo/test_objexist.cc 
b/src/smf/smfd/imm_modify_demo/test_objexist.cc
new file mode 100644
index 000000000..3fb23e3c2
--- /dev/null
+++ b/src/smf/smfd/imm_modify_demo/test_objexist.cc
@@ -0,0 +1,127 @@
+/*      -*- OpenSAF  -*-
+ *
+ * Copyright Ericsson AB 2018 - 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 AB
+ *
+ */
+
+#include <limits.h>
+
+#include <string>
+#include <vector>
+#include <memory>
+#include <utility>
+#include <iostream>
+
+#include "ais/include/saImm.h"
+#include "ais/include/saAis.h"
+#include "base/osaf_extended_name.h"
+
+#include "smf/smfd/SmfUtils_ObjExist.h"
+
+#include "smf/smfd/imm_modify_config/immccb.h"
+
+using namespace std;
+
+int main() {
+  CheckObjectExist e_check;
+  CheckObjectExist::ReturnCode exist_rc;
+
+  printf("Testing");
+
+  cout << "Find an object test:" << endl;
+  modelmodify::CreateDescriptor existing_object;
+  modelmodify::AttributeDescriptor log_config;
+  log_config.attribute_name = "logConfig";
+  log_config.value_type = SA_IMM_ATTR_SASTRINGT;
+  log_config.AddValue("logConfig=1");
+  existing_object.class_name = "OpenSafLogConfig";
+  existing_object.parent_name = "safApp=safLogService";
+  existing_object.AddAttribute(log_config);
+
+  std::string object_dn = "logConfig=1,safApp=safLogService";
+  cout << "Object to check '" << object_dn << "'" << endl;
+  exist_rc = e_check.IsExisting(existing_object);
+  if (exist_rc == e_check.kFail) {
+    cout << "Check failed" << endl;
+  } else if (exist_rc == e_check.kOk) {
+    cout << "Object exist" << endl;
+  } else if (exist_rc == e_check.kNotExist) {
+    cout << "Object does not exist" << endl;
+  } else {
+    cout << "Fail, unknown return code" << endl;
+  }
+  cout << endl;
+
+
+  object_dn = "Kalle";
+  cout << "Object to check '" << object_dn << "'" << endl;
+  log_config.values_as_strings.clear();
+  log_config.AddValue("Kalle");
+  existing_object.attributes.clear();
+  existing_object.AddAttribute(log_config);
+  exist_rc = e_check.IsExisting(existing_object);
+  if (exist_rc == e_check.kFail) {
+    cout << "Check failed" << endl;
+  } else if (exist_rc == e_check.kOk) {
+    cout << "Object exist" << endl;
+  } else if (exist_rc == e_check.kNotExist) {
+    cout << "Object does not exist" << endl;
+  } else {
+    cout << "Fail, unknown return code" << endl;
+  }
+  cout << endl;
+
+
+  object_dn = "logConfig=1";
+  cout << "Object to check '" << object_dn << "'" << endl;
+  log_config.values_as_strings.clear();
+  log_config.AddValue("logConfig=1");
+  existing_object.attributes.clear();
+  existing_object.parent_name.clear();
+  existing_object.AddAttribute(log_config);
+  exist_rc = e_check.IsExisting(existing_object);
+  if (exist_rc == e_check.kFail) {
+    cout << "Check failed" << endl;
+  } else if (exist_rc == e_check.kOk) {
+    cout << "Object exist" << endl;
+  } else if (exist_rc == e_check.kNotExist) {
+    cout << "Object does not exist" << endl;
+  } else {
+    cout << "Fail, unknown return code" << endl;
+  }
+  cout << endl;
+
+
+  object_dn = "";
+  cout << "Object to check '" << object_dn << "'" << endl;
+  log_config.values_as_strings.clear();
+  //log_config.AddValue("logConfig=1");
+  existing_object.attributes.clear();
+  existing_object.parent_name.clear();
+  existing_object.AddAttribute(log_config);
+  exist_rc = e_check.IsExisting(existing_object);
+  if (exist_rc == e_check.kFail) {
+    cout << "Check failed" << endl;
+  } else if (exist_rc == e_check.kOk) {
+    cout << "Object exist" << endl;
+  } else if (exist_rc == e_check.kNotExist) {
+    cout << "Object does not exist" << endl;
+  } else {
+    cout << "Fail, unknown return code" << endl;
+  }
+  cout << endl;
+
+
+  return 0;
+}
-- 
2.17.0


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

Reply via email to