A test program, ccbhdl_test, is added to the demo directory that tests
combined CCBs, data ownership and reusage of created objects escpecially
the CCB handling object
* Fixed bugs found during testing. Fixed some review comments.
* Still some TODOs. These are more like comments about possible changes
in the future. Will not be removed for now
* LLDTEST Tags still remains. Will be removed before pushing
* All review commits will be concatenated into one commit before pushing
---
src/smf/Makefile.am | 37 +-
.../smfd/imm_modify_config/add_operation_to_ccb.cc | 5 -
src/smf/smfd/imm_modify_config/attribute.cc | 1 -
src/smf/smfd/imm_modify_config/attribute.h | 2 -
src/smf/smfd/imm_modify_config/immccb.cc | 48 +--
src/smf/smfd/imm_modify_config/immccb.h | 51 +--
src/smf/smfd/imm_modify_demo/Makefile | 2 +-
src/smf/smfd/imm_modify_demo/ccbhdl_test.cc | 377 +++++++++++++++++++++
src/smf/smfd/imm_om_api/om_admin_owner_clear.h | 11 +
src/smf/smfd/imm_om_api/om_admin_owner_handle.h | 8 +
src/smf/smfd/imm_om_api/om_admin_owner_set.h | 6 +
src/smf/smfd/imm_om_api/om_ccb_handle.cc | 3 +-
src/smf/smfd/imm_om_api/om_ccb_handle.h | 7 +
src/smf/smfd/imm_om_api/om_ccb_object_create.h | 5 +
src/smf/smfd/imm_om_api/om_ccb_object_delete.h | 5 +
src/smf/smfd/imm_om_api/om_ccb_object_modify.h | 5 +
src/smf/smfd/imm_om_api/om_handle.cc | 8 +-
src/smf/smfd/imm_om_api/om_handle.h | 13 +-
18 files changed, 518 insertions(+), 76 deletions(-)
create mode 100644 src/smf/smfd/imm_modify_demo/ccbhdl_test.cc
diff --git a/src/smf/Makefile.am b/src/smf/Makefile.am
index 673387db6..016da30c8 100644
--- a/src/smf/Makefile.am
+++ b/src/smf/Makefile.am
@@ -258,7 +258,7 @@ endif
# Demos and test
if ENABLE_TESTS
-bin_PROGRAMS += bin/ccbdemo_create bin/ccbdemo_delete bin/ccbdemo_modify
+bin_PROGRAMS += bin/ccbdemo_create bin/ccbdemo_delete bin/ccbdemo_modify
bin/ccbhdl_test
dist_pkgimmxml_svc_DATA += \
src/smf/smfd/imm_modify_demo/democlass.xml
@@ -368,4 +368,39 @@ bin_ccbdemo_modify_LDADD = \
lib/libSaImmOm.la \
lib/libopensaf_core.la
+# CCBHDL Test
+# -----------
+bin_ccbhdl_test_CFLAGS = $(AM_CFLAGS) -Wformat=1
+
+bin_ccbhdl_test_CPPFLAGS = \
+ -DSA_EXTENDED_NAME_SOURCE \
+ $(AM_CPPFLAGS)
+
+bin_ccbhdl_test_SOURCES = \
+ src/smf/smfd/imm_modify_demo/ccbhdl_test.cc
+
+# IMM configuration modifier
+bin_ccbhdl_test_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 += \
+ src/smf/smfd/imm_om_api/common/common.cc \
+ src/smf/smfd/imm_om_api/common/imm_attribute.cc \
+ src/smf/smfd/imm_om_api/om_admin_owner_clear.cc \
+ src/smf/smfd/imm_om_api/om_admin_owner_handle.cc \
+ src/smf/smfd/imm_om_api/om_admin_owner_set.cc \
+ src/smf/smfd/imm_om_api/om_ccb_handle.cc \
+ src/smf/smfd/imm_om_api/om_ccb_object_create.cc \
+ src/smf/smfd/imm_om_api/om_ccb_object_delete.cc \
+ src/smf/smfd/imm_om_api/om_ccb_object_modify.cc \
+ src/smf/smfd/imm_om_api/om_handle.cc
+
+bin_ccbhdl_test_LDADD = \
+ lib/libosaf_common.la \
+ lib/libSaImmOm.la \
+ lib/libopensaf_core.la
+
endif
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 6948aa89c..6658182ff 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
@@ -18,11 +18,6 @@
#include "add_operation_to_ccb.h"
-#if 1 // TODO(Lennart) Remove
-#include <stdio.h>
-#include <iostream>
-#endif
-
#include <limits.h>
#include <string>
diff --git a/src/smf/smfd/imm_modify_config/attribute.cc
b/src/smf/smfd/imm_modify_config/attribute.cc
index 72d5e8828..4b9f63817 100644
--- a/src/smf/smfd/imm_modify_config/attribute.cc
+++ b/src/smf/smfd/imm_modify_config/attribute.cc
@@ -44,7 +44,6 @@ using namespace modelmodify;
// - A numeric IMM type, SaImmValueTypeT
// out: - A filled in numeric vector
// return false on error. Type of error is logged in syslog
-// TODO(Lennart) Make static
template<typename T>
static bool StringToNumericValue(const std::string& str_value,
T& num_value, const SaImmValueTypeT imm_type) {
diff --git a/src/smf/smfd/imm_modify_config/attribute.h
b/src/smf/smfd/imm_modify_config/attribute.h
index 7cb0e316c..61db1d8c8 100644
--- a/src/smf/smfd/imm_modify_config/attribute.h
+++ b/src/smf/smfd/imm_modify_config/attribute.h
@@ -32,8 +32,6 @@
#ifndef ATTRVAL_HDL_H
#define ATTRVAL_HDL_H
-// TODO(Lennart) Place within namespace modelmodify?
-
// Convert std::string to SaNameT or SaAnyT.
// Shall be used with AttributeDescriptor object where SaNametToString or
// SaAnytToString has been used to convert the data to std::string
diff --git a/src/smf/smfd/imm_modify_config/immccb.cc
b/src/smf/smfd/imm_modify_config/immccb.cc
index e52d6b9e6..968306499 100644
--- a/src/smf/smfd/imm_modify_config/immccb.cc
+++ b/src/smf/smfd/imm_modify_config/immccb.cc
@@ -66,20 +66,23 @@ ModelModification::ModelModification()
imm_ccb_handle_(nullptr),
imm_ao_handle_(nullptr),
imm_ao_owner_set_(nullptr),
- error_info_{"No error", SA_AIS_OK},
modification_timeout_(120000),
ccb_flags_(0) {
+ TRACE_ENTER2("LLDTEST");
// Create a unique admin owner name for this ObjectModification object
instance_number_ = next_instance_number_++;
admin_owner_name_ = "SmfObjectModification" +
std::to_string(instance_number_);
+ TRACE_LEAVE2("LLDTEST");
}
ModelModification::~ModelModification() {
// If an OM handle exists, cleanup the CCB handling by finalizing the handle
// Note that releaseOwnershipFinalize must be set when creating an OM admin
// owner handle so that admin ownership is released
+ TRACE_ENTER2("LLDTEST");
FinalizeHandles();
+ TRACE_LEAVE2("LLDTEST");
}
bool ModelModification::DoModelModification(CcbDescriptor modifications) {
@@ -88,10 +91,6 @@ bool ModelModification::DoModelModification(CcbDescriptor
modifications) {
int recovery_info = kNotSet;
base::Timer modification_time(modification_timeout_);
- // Creating a CCB is a sequence of calls to different IMM APIs. Each API call
- // may fail. But in some cases, based on the API return code, it is possible
- // to recover by redoing the sequence from start. To prevent indefinite
- // re-tries the recovery loop is timed out
while (modification_time.is_timeout() == false) {
// If CCB creation fail invalid handles shall be finalized
// Note: This function is safe to call also if there are no handles
@@ -220,17 +219,19 @@ int ModelModification::CreateObjectManager() {
RetryTiming.interval = base::kFourtyMilliseconds;
RetryTiming.timeout = 60000;
imm_om_handle_->ChangeDefaultRetryControl(RetryTiming);
+ } else {
+ TRACE("LLDTEST %s: OM handle object exists", __FUNCTION__);
}
bool return_state = imm_om_handle_->InitializeHandle();
if (return_state == false) {
// No recovery is possible
- LOG_NO("LLDTEST %s: OM-handle, InitializeHandle(), Fail", __FUNCTION__);
+ LOG_NO("LLDTEST %s: OM-handle, RestoreHandle(), Fail", __FUNCTION__);
recovery_info = kFail;
} else {
recovery_info = kContinue;
}
- TRACE_LEAVE2("LLDTEST");
+ TRACE_LEAVE2("LLDTEST, %s", RecoveryTxt(recovery_info));
return recovery_info;
}
@@ -248,12 +249,15 @@ int ModelModification::CreateAdminOwner() {
TRACE_ENTER2("LLDTEST");
int recovery_info = kNotSet;
if (imm_ao_handle_ == nullptr) {
- SaImmHandleT om_handle = imm_om_handle_->GetHandle();
imm_ao_handle_ = std::unique_ptr<immom::ImmOmAdminOwnerHandle>
- (new immom::ImmOmAdminOwnerHandle(om_handle, admin_owner_name_));
+ (new immom::ImmOmAdminOwnerHandle(0, admin_owner_name_));
+ } else {
+ TRACE("LLDTEST %s: OM handle object exists", __FUNCTION__);
}
- bool rc = imm_ao_handle_->InitializeHandle();
+ SaImmHandleT om_handle = imm_om_handle_->GetHandle();
+ imm_ao_handle_->ReleaseOwnershipOnFinalize();
+ bool rc = imm_ao_handle_->InitializeHandle(om_handle);
if (rc == false) {
SaAisErrorT ais_rc = imm_ao_handle_->ais_error();
if (ais_rc == SA_AIS_ERR_BAD_HANDLE) {
@@ -266,14 +270,16 @@ int ModelModification::CreateAdminOwner() {
recovery_info = kFail;
}
} else {
+ SaImmAdminOwnerHandleT ao_handle = imm_ao_handle_->GetHandle();
if (imm_ao_owner_set_ == nullptr) {
- SaImmAdminOwnerHandleT ao_handle = imm_ao_handle_->GetHandle();
imm_ao_owner_set_ = std::unique_ptr<immom::ImmOmAdminOwnerSet>
(new immom::ImmOmAdminOwnerSet(ao_handle));
+ } else {
+ imm_ao_owner_set_->UpdateHandle(ao_handle);
}
recovery_info = kContinue;
}
- TRACE_LEAVE2("LLDTEST");
+ TRACE_LEAVE2("LLDTEST, %s", RecoveryTxt(recovery_info));
return recovery_info;
}
@@ -285,12 +291,12 @@ int ModelModification::CreateCcb() {
TRACE_ENTER2("LLDTEST");
int recovery_info = kNotSet;
if (imm_ccb_handle_ == nullptr) {
- SaImmAdminOwnerHandleT ao_handle = imm_ao_handle_->GetHandle();
imm_ccb_handle_ = std::unique_ptr<immom::ImmOmCcbHandle>
- (new immom::ImmOmCcbHandle(ao_handle, ccb_flags_));
+ (new immom::ImmOmCcbHandle(0, ccb_flags_));
}
- bool rc = imm_ccb_handle_->InitializeHandle();
+ SaImmAdminOwnerHandleT ao_handle = imm_ao_handle_->GetHandle();
+ bool rc = imm_ccb_handle_->InitializeHandle(ao_handle);
if (rc == false) {
SaAisErrorT ais_rc = imm_ccb_handle_->ais_error();
if (ais_rc == SA_AIS_ERR_BAD_HANDLE) {
@@ -352,8 +358,12 @@ int
ModelModification::AdminOwnerSet(std::vector<std::string>& objects,
return kFail;
}
+ // The ImmOmAdminOwnerSet exists and may have been used earlier so clean the
+ // object list before adding new objects
+ imm_ao_owner_set_->ClearObjectNames();
// Set objects to become admin owner of
for (auto& object : objects) {
+ TRACE("LLDTEST* %s: Set AO for object = '%s'", __FUNCTION__,
object.c_str());
if (object.empty()) {
// Do not add empty strings (IMM root)
continue;
@@ -412,15 +422,13 @@ int
ModelModification::AdminOwnerSet(std::vector<std::string>& objects,
// has an admin owner if the same admin owner name is used, meaning that
// this is not needed to check
// Recovery: BAD HANDLE; Restart CCB handling
-// FAILED OPERATION; Restart CCB handling
+// FAILED OPERATION; Restart CCB handling if resource abort
// Return: Recovery information
int ModelModification::AddCreates(std::vector<CreateDescriptor>&
create_descriptors) {
TRACE_ENTER2("LLDTEST");
int recovery_info = kNotSet;
- // TODO(Lennart) The code below for setting admin owner is the same (almost)
- // for all configuration operations (redundant). Fix
// Do this for all create descriptors
for (auto& create_descriptor : create_descriptors) {
if (create_descriptor.parent_name.empty() == false) {
@@ -475,7 +483,7 @@ int ModelModification::AddCreate(CreateDescriptor&
create_descriptor) {
// Add delete requests for all objects to be deleted
// Set admin ownership for objects to be deleted with scope SA_IMM_SUBTREE.
// Add all delete operations to the CCB
-// Note1: We have to be owner of the object(s) and the sub tree
+// Note1: We have to be owner of the object(s) and the sub tree(s)
// Note2: It is not a problem to set admin ownership of an object that already
// has an admin owner if the same admin owner name is used, meaning that
// this is not needed to check
@@ -550,7 +558,7 @@ int ModelModification::AddDelete(DeleteDescriptor&
delete_descriptor) {
// has an admin owner if the same admin owner name is used, meaning that
// this is not needed to check
// Recovery: BAD HANDLE; Restart CCB handling
-// FAILED OPERATION; Restart CCB handling
+// FAILED OPERATION; Restart CCB handling if resource abort
// Return: Recovery information
int ModelModification::AddModifies(std::vector<ModifyDescriptor>&
modify_descriptors) {
diff --git a/src/smf/smfd/imm_modify_config/immccb.h
b/src/smf/smfd/imm_modify_config/immccb.h
index 6fc71eee4..744306a6a 100644
--- a/src/smf/smfd/imm_modify_config/immccb.h
+++ b/src/smf/smfd/imm_modify_config/immccb.h
@@ -70,7 +70,8 @@
* RETRY handling is done in three levels:
* 1. For each IMM API. This is mainly try again loops
* 2. For sequences of APIs. The main reason is if the OM handle has to be
- * recreated (SA_AIS_ERR_BAD_HANDLE)
+ * recreated (SA_AIS_ERR_BAD_HANDLE) however there are some other
+ * recoverable problems as well
* 3. For failing to apply a CCB if the reason is not un-recoverable e.g.
* a validation error
*
@@ -81,8 +82,6 @@
* does not have to log any detailed information.
* Example of user logging:
* LOG_NO("%s: DoModification() Fail", __FUNCTION__);
- * TODO(Lennart) More detailed information about a fail is saved and can be
- * requested.
*
* Possible reasons for a fail:
* * An IMM API has returned an unrecoverable error. This includes try again
@@ -91,15 +90,12 @@
* * Other unrecoverable error from an Object Implementer
* * All CCB apply retries has failed
* * A general timeout for the whole modify sequence has happened before
- * the CCB was applied
+ * the CCB was applied successfully
*/
-// TODO(Lennart) Recovery is not possible if CcbApply returns FAILED OPERATION
-// since this error is returned if an OI validation fails. Note that if the
-// SA_IMM_CCB_REGISTERED_OI flag is set no OI verification will be done
-// This is also the case for Create and Modify
-// Investigate!
-
+// Functions for converting some SAF sepcific types to string. For numeric
+// values the STL to_string can be used
+// -----------------------------------------------------------------------
// Convert SaNameT to a std::string to be used with an AttributeDescriptor
// Note: Both types may contain data that cannot be read as text string but
// their data can still be contained in a std::string object
@@ -124,6 +120,8 @@ namespace modelmodify {
// * Multiple values of the given value_type. This is allowed only if the
// attribute is defined as a multi value attribute
// Used with IMM object creation and value modifications
+// Note: Use <object name>.values_as_strings.clear() before adding any values
+// to remove any previous values if the object is reused
//
struct AttributeDescriptor {
std::string attribute_name;
@@ -169,7 +167,7 @@ struct AttributeModifyDescriptor {
// Possible modifications are:
// * Create an IMM object
// * Delete an IMM object
-// * Modify the value(s) of one attribute in an IMM object
+// * Modify the value(s) of one or more attribute(s) in an IMM object
// Note: Several modifications can be added to one CCB.
//
// Some general notes:
@@ -207,6 +205,7 @@ struct DeleteDescriptor {
};
// ModifyDescriptor: Modify value(s) of one or several attributes in one object
+// Note: Use only one ModifyDescriptor per object in the same CCB
//
struct ModifyDescriptor {
// Full DN of the object to be modified
@@ -241,15 +240,6 @@ struct CcbDescriptor {
}
};
-// TODO(Lennart) Is this really needed? If not remove
-// ErrorInfo: Used as out-parameter to give information about an operation
-struct ErrorInfo {
- // Name of the failed function / method
- std::string function_name;
- // Holds the ais_error code of the last called IMM API
- SaAisErrorT ais_error;
-};
-
// Internal recovery information
// -----------------------------
// An operation may Fail and no recovery is possible but it may also fail
@@ -272,6 +262,7 @@ const uint64_t kExistTimeout = 30000; // 30 sec
const uint64_t kExistWait = 2000; // 2 sec
// Recovery code to text
+// Used with log and trace
inline static const char* RecoveryTxt(int recovery_info) {
switch (recovery_info) {
case modelmodify::kContinue:
@@ -300,9 +291,6 @@ inline static const char* RecoveryTxt(int recovery_info) {
// Note2: All IMM resources needed for the operation will be allocated
// internally when needed and released before DoModification returns.
//
-// TODO(Lennart) We may also need:
-// * Method to set some timeout for the operation(s) (other settings?)
-//
// Example: Create an IMM object Obj1 based on IMM class Class1. The rdn
// attribute name is obj1Name and the object name (rdn) shall be
// obj1=1. Parent is safApp=safSmfService. No other attributes will be
@@ -310,7 +298,7 @@ inline static const char* RecoveryTxt(int recovery_info) {
//
// // 1. Fill in an attribute descriptor for the object name:
// modelmodify::AttributeDescriptor object_name("obj1Name");
-// object_name.AddValue<SaStringT>("obj1=1");
+// object_name.AddValue("obj1=1");
//
// // 2. Fill in a create descriptor:
// modelmodify::CreateDescriptor create_obj1;
@@ -332,7 +320,7 @@ inline static const char* RecoveryTxt(int recovery_info) {
class ModelModification {
public:
ModelModification();
- ~ModelModification(); // TODO(Lennart) Finalize the Object Manager
+ ~ModelModification();
// Set CCB Flags
// If flag SA_IMM_CCB_REGISTERED_OI is set then an Object Implementer must
@@ -345,9 +333,6 @@ class ModelModification {
// a validation error
bool DoModelModification(CcbDescriptor modifications);
- // Get more information if DoModification return Fail
- ErrorInfo GetErrorInfo(void) { return error_info_; }
-
private:
void FinalizeHandles(void);
int CreateHandles(void);
@@ -378,7 +363,7 @@ class ModelModification {
// Instance number for this class used to create a unique applier name
// This is needed if instances are used in several threads
- // By using a unique name we can avoid taking admin ownership form some other
+ // By using a unique name we can avoid taking admin ownership from some other
// thread that has an ongoing modification
// At construction time the static instance number is copied to the local
// instance number and the global instance number is incremented for next
@@ -390,8 +375,6 @@ class ModelModification {
CcbDescriptor ccb_descriptor_;
// A list of all objects to become admin owner of
std::vector<std::string> admin_owner_objects_;
- // Store information about each step in the IMM API sequence
- ErrorInfo error_info_;
// Main timeout for DoModification()
uint64_t modification_timeout_;
SaImmCcbFlagsT ccb_flags_;
@@ -401,12 +384,6 @@ class ModelModification {
} // namespace modelmodify
-// TODO(Lennart) For now functions are added here for converting SaNameT and
-// SaAnyT to std::string. May be inline methods in the
-// AttributeDescriptor structure?
-//
-// Maybe part of modelmodify namespace?
-
static inline std::string SaNametToString(SaNameT* name_value) {
std::string out_string;
if (osaf_is_extended_name_empty(name_value)) {
diff --git a/src/smf/smfd/imm_modify_demo/Makefile
b/src/smf/smfd/imm_modify_demo/Makefile
index 9492d3272..8dc274034 100644
--- a/src/smf/smfd/imm_modify_demo/Makefile
+++ b/src/smf/smfd/imm_modify_demo/Makefile
@@ -15,5 +15,5 @@
#
all:
- $(MAKE) -C ../../../.. bin/ccbdemo_create bin/ccbdemo_delete
bin/ccbdemo_modify
+ $(MAKE) -C ../../../.. bin/ccbdemo_create bin/ccbdemo_delete
bin/ccbdemo_modify bin/ccbhdl_test
diff --git a/src/smf/smfd/imm_modify_demo/ccbhdl_test.cc
b/src/smf/smfd/imm_modify_demo/ccbhdl_test.cc
new file mode 100644
index 000000000..fca3759db
--- /dev/null
+++ b/src/smf/smfd/imm_modify_demo/ccbhdl_test.cc
@@ -0,0 +1,377 @@
+/* -*- OpenSAF -*-
+ *
+ * (C) Copyright 2008 The OpenSAF Foundation
+ * Copyright Ericsson AB 2017 - 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 "osaf/configmake.h"
+
+#include "smf/smfd/imm_modify_config/immccb.h"
+
+#include "smf/smfd/imm_om_api/common/common.h"
+#include "smf/smfd/imm_om_api/om_admin_owner_clear.h"
+#include "smf/smfd/imm_om_api/om_admin_owner_handle.h"
+#include "smf/smfd/imm_om_api/om_admin_owner_set.h"
+#include "smf/smfd/imm_om_api/om_ccb_handle.h"
+#include "smf/smfd/imm_om_api/om_ccb_object_create.h"
+#include "smf/smfd/imm_om_api/om_ccb_object_delete.h"
+#include "smf/smfd/imm_om_api/om_ccb_object_modify.h"
+#include "smf/smfd/imm_om_api/om_handle.h"
+
+using namespace std;
+
+// Before this program can be used the class defined in democlass.xml has to be
+// installed.
+
+// Create one object. The whole procedure is done here including applying the
+// CCB
+// DN = Test1=1,safApp=safSmfService
+// Several attributes are set
+void CreateOneObject(void) {
+
+ // Create an object description
+ // ============================
+ modelmodify::AttributeDescriptor attribute;
+ modelmodify::CreateDescriptor imm_object;
+ std::string object_name = "Test1=1";
+
+ // Define the object
+ // -----------------
+ imm_object.class_name = "ImmTestValuesConfig";
+ imm_object.parent_name = "safApp=safSmfService";
+ // Set IMM Object name
+ attribute.attribute_name = "immTestValuesCfg";
+ attribute.value_type = SA_IMM_ATTR_SASTRINGT;
+ attribute.AddValue(object_name);
+ imm_object.AddAttribute(attribute);
+
+ // Set some attributes
+ // -------------------
+
+ // SA_UINT32_T multi-value attribute
+ attribute.attribute_name = "SaUint32TValues";
+ attribute.value_type = SA_IMM_ATTR_SAUINT32T;
+ // We are reusing so remove "old" values and add new ones
+ attribute.values_as_strings.clear();
+ attribute.AddValue(std::to_string(1));
+ attribute.AddValue(std::to_string(2));
+ // Add the attribute to the object
+ imm_object.AddAttribute(attribute);
+
+ // SA_INT32_T multi-value attribute
+ attribute.attribute_name = "SaInt32TValues";
+ attribute.value_type = SA_IMM_ATTR_SAINT32T;
+ attribute.values_as_strings.clear();
+ attribute.AddValue(std::to_string(3));
+ attribute.AddValue(std::to_string(4));
+ // Add the attribute to the object
+ imm_object.AddAttribute(attribute);
+
+ // SA_NAME_T multi-value attribute
+ SaNameT a_name;
+ attribute.attribute_name = "SaNameTValues";
+ attribute.value_type = SA_IMM_ATTR_SANAMET;
+ attribute.values_as_strings.clear();
+ // Add two short names
+ osaf_extended_name_lend("a_name1", &a_name);
+ attribute.AddValue(SaNametToString(&a_name));
+ osaf_extended_name_lend("a_name2", &a_name);
+ attribute.AddValue(SaNametToString(&a_name));
+ // Add a long name and a third short name
+ char long_name[300];
+ for (size_t i = 0; i < 299; i++) {
+ long_name[i] = 'a';
+ }
+ long_name[299] = '\0';
+ osaf_extended_name_lend(long_name, &a_name);
+ attribute.AddValue(SaNametToString(&a_name));
+ osaf_extended_name_lend("a_name3", &a_name);
+ attribute.AddValue(SaNametToString(&a_name));
+ imm_object.AddAttribute(attribute);
+
+ // SA_UINT32_T single-value attribute
+ attribute.attribute_name = "SaUint32TValue";
+ attribute.value_type = SA_IMM_ATTR_SAUINT32T;
+ attribute.values_as_strings.clear();
+ // Note: Single value attribute so only one value can be added
+ attribute.AddValue(std::to_string(10));
+ imm_object.AddAttribute(attribute);
+
+ // Create a CCB descriptor and add the description of the object to create
+ // =======================================================================
+ modelmodify::CcbDescriptor CcbDescriptor;
+
+ CcbDescriptor.AddCreate(imm_object);
+
+ // Create a modification handler and use it to create the object as defined
+ // above
+ // ========================================================================
+
+ modelmodify::ModelModification ModelModifier;
+
+ // Set CCB flag to inform IMM that there is no Object Implementer
+ ModelModifier.SetCcbFlags(0);
+
+ // Create the IMM object
+ if (ModelModifier.DoModelModification(CcbDescriptor) == false) {
+ cout << "Creation of '" << object_name << "' FAIL" << endl;
+ } else {
+ cout << "Creation of '" << object_name << "' SUCCESS" << endl;
+ }
+}
+
+// Remove all modifications from a CCB descriptor so it can be reused
+void CcbDescriptorCleaner(modelmodify::CcbDescriptor& ccb_descriptor) {
+ ccb_descriptor.create_descriptors.clear();
+ ccb_descriptor.delete_descriptors.clear();
+ ccb_descriptor.modify_descriptors.clear();
+}
+
+// Fill in a create descriptor.
+// The created object will be given the name in 'object_name'
+// Class name and parent is filled in here
+// Values set are:
+// SaInt32TValues: 1, 2, 3
+// SaUint64TValues: 100, 200
+// SaTimeTValues: 10000
+void SetupObjectCreate1(std::string object_name,
+ modelmodify::CreateDescriptor& create_desc) {
+ modelmodify::AttributeDescriptor attribute;
+ // Prepare the create descriptor
+ create_desc.class_name = "ImmTestValuesConfig";
+ create_desc.parent_name = "safApp=safSmfService";
+ create_desc.attributes.clear();
+
+ // Set object name
+ attribute.attribute_name = "immTestValuesCfg";
+ attribute.value_type = SA_IMM_ATTR_SASTRINGT;
+ attribute.AddValue(object_name);
+ create_desc.AddAttribute(attribute);
+
+ // Set SaInt32TValues
+ attribute.values_as_strings.clear();
+ attribute.attribute_name = "SaInt32TValues";
+ attribute.value_type = SA_IMM_ATTR_SAINT32T;
+ attribute.AddValue(std::to_string(1));
+ attribute.AddValue(std::to_string(2));
+ attribute.AddValue(std::to_string(3));
+ create_desc.AddAttribute(attribute);
+
+ // Set SaUint64TValues
+ attribute.values_as_strings.clear();
+ attribute.attribute_name = "SaUint64TValues";
+ attribute.value_type = SA_IMM_ATTR_SAUINT64T;
+ attribute.AddValue(std::to_string(100));
+ attribute.AddValue(std::to_string(200));
+ create_desc.AddAttribute(attribute);
+
+ // Set SaTimeTValues
+ attribute.values_as_strings.clear();
+ attribute.attribute_name = "SaTimeTValues";
+ attribute.value_type = SA_IMM_ATTR_SATIMET;
+ attribute.AddValue(std::to_string(10000));
+ create_desc.AddAttribute(attribute);
+}
+
+void WaitForUserAction(std::string message) {
+ cout << message << endl;
+ cout << endl << "Press Enter to continue" << endl << endl;
+ getchar();
+}
+
+int main() {
+ cout << "ccbhdl_test" << endl;
+ cout << "IMM class used for test: ImmTestValuesConfig" << endl;
+
+#if 0 // Enable trace
+ unsigned int category_mask = 0xffffffff;
+ const char* logPath = PKGLOGDIR "/osafccbdemo1";
+ if (logtrace_init("ccbdemo1", logPath, category_mask) == -1) {
+ syslog(LOG_ERR, "osafntfimcnd logtrace_init FAILED");
+ /* We allow to execute anyway. */
+ cout << "logtrace_init() Fail" << endl;
+ } else {
+ //cout << "logtrace enabled" << endl;
+ }
+#endif
+
+ // Prepare/enable extended name
+ // ----------------------------
+ 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
+
+ // ==================== TEST START
===========================================
+
+ // Create a first object
+ // ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
+ // This is done in a separate function where also the CCB is applied
+ cout << "Creating: Test1=1,safApp=safSmfService" << endl;
+ CreateOneObject();
+ cout << endl;
+
+ // Wait for the user to verify in another node and press Enter in this node
+ WaitForUserAction("Verify creation of Test1=1,safApp=safSmfService");
+
+ // Create a modifier to be used here in main() and set CCB flag to handle
+ // that there is no Object Implementer for objects handled here
+ // ----------------------------------------------------------------------
+ modelmodify::ModelModification modifier;
+ modifier.SetCcbFlags(0);
+ // A CCB descriptor for the modifier
+ modelmodify::CcbDescriptor ccb_descriptor;
+
+ // We also need some model modification descriptors
+ modelmodify::CreateDescriptor create_descriptor;
+ modelmodify::ModifyDescriptor modify_descriptor;
+ modelmodify::DeleteDescriptor delete_descriptor;
+ // an attribute modify descriptor
+ modelmodify::AttributeDescriptor attribute_descriptor;
+ // and an attribute modify descriptor
+ modelmodify::AttributeModifyDescriptor attribute_modify_descriptor;
+
+ // Create and apply a CCB that creates two more objects
+ // ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
+
+ SetupObjectCreate1("Test1=2", create_descriptor);
+ ccb_descriptor.AddCreate(create_descriptor);
+ SetupObjectCreate1("Test1=3", create_descriptor);
+ ccb_descriptor.AddCreate(create_descriptor);
+ 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 << endl;
+
+ WaitForUserAction("Verify creation of Test1=2 and Test1=3 objects "
+ "(,safApp=safSmfService)");
+
+ // Modify the model using a new CCB with mixed modifications
+ // ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
+
+ // Create one new object "Test1=4"
+ // Delete "Test1=2" and "Test1=3" parent "Test1=2,safApp=safSmfService"
+ // Modify "Test1=1":
+ // Add 4 to SaUint32TValues
+ // Delete "a_name1" from SaNameTValues
+ // Replace SaInt32TValues 3 and 4 with 5, 6, 7
+ // All is done in the same CCB
+
+ // We are reusing the CCB descriptor so:
+ // Clean the CCB descriptor from the previous object creation
+ CcbDescriptorCleaner(ccb_descriptor);
+
+ // Add a new object to the CBB. The new object is created within a function
+ // that uses an attribute descriptor that is local to this function. This
will
+ // test that data is correctly handed over to the create descriptor
+ SetupObjectCreate1("Test1=4", create_descriptor);
+ ccb_descriptor.AddCreate(create_descriptor);
+
+ // Add two objects deletions
+ delete_descriptor.object_name = "Test1=2,safApp=safSmfService";
+ ccb_descriptor.AddDelete(delete_descriptor);
+ delete_descriptor.object_name = "Test1=3,safApp=safSmfService";
+ ccb_descriptor.AddDelete(delete_descriptor);
+
+ // Setup the modifications for the "test1=1" object:
+ modify_descriptor.object_name = "Test1=1,safApp=safSmfService";
+ // Add a value to SaUint32TValues
+ // Clean attribute descriptor from any possible previous usage
+ attribute_descriptor.values_as_strings.clear();
+ attribute_descriptor.attribute_name = "SaUint32TValues";
+ attribute_descriptor.value_type = SA_IMM_ATTR_SAUINT32T;
+ attribute_descriptor.AddValue(std::to_string(4));
+ attribute_modify_descriptor.modification_type = SA_IMM_ATTR_VALUES_ADD;
+ attribute_modify_descriptor.attribute_descriptor = attribute_descriptor;
+ modify_descriptor.AddAttributeModification(attribute_modify_descriptor);
+ // Delete a value from SaNameTValues
+ attribute_descriptor.values_as_strings.clear();
+ SaNameT a_name; // Use a real SaNameT
+ osaf_extended_name_lend("a_name1", &a_name);
+ attribute_descriptor.attribute_name = "SaNameTValues";
+ attribute_descriptor.value_type = SA_IMM_ATTR_SANAMET;
+ attribute_descriptor.AddValue(SaNametToString(&a_name));
+ attribute_modify_descriptor.modification_type = SA_IMM_ATTR_VALUES_DELETE;
+ attribute_modify_descriptor.attribute_descriptor = attribute_descriptor;
+ modify_descriptor.AddAttributeModification(attribute_modify_descriptor);
+ // Replace the values in SaInt32TValues
+ attribute_descriptor.attribute_name = "SaInt32TValues";
+ attribute_descriptor.value_type = SA_IMM_ATTR_SAINT32T;
+ attribute_descriptor.values_as_strings.clear();
+ attribute_descriptor.AddValue(std::to_string(5));
+ attribute_descriptor.AddValue(std::to_string(6));
+ attribute_descriptor.AddValue(std::to_string(7));
+ attribute_modify_descriptor.modification_type = SA_IMM_ATTR_VALUES_REPLACE;
+ attribute_modify_descriptor.attribute_descriptor = attribute_descriptor;
+ modify_descriptor.AddAttributeModification(attribute_modify_descriptor);
+
+ // Add the modifications to the CCB
+ ccb_descriptor.AddModify(modify_descriptor);
+
+ cout << "Modify the model using a mixed CCB START" << endl;
+
+ // The modifier object is used here for the second time
+ if (modifier.DoModelModification(ccb_descriptor) == false) {
+ cout << "Modify the model using a mixed CCB, FAIL" << endl;
+ } else {
+ cout << "Modify the model using a mixed CCB, SUCCESS" << endl;
+ }
+
+ cout << "Test sequence DONE" << endl << endl;
+
+ cout << "Verify that:" << endl;
+ cout << " - The Test1=2 and Test1=3 objects are deleted" << endl;
+ cout << " - A new Test1=4 object is created" << endl;
+ cout << " - Test1=1 is modified:" << endl;
+ cout << " SaUint32TValues: Added '4'" << endl;
+ cout << " SaNameTValues: Deleted 'a_name1'" << endl;
+ cout << " SaInt32TValues: Values '3, 4' "
+ "replaced by '5, 6, 7'" << endl << endl;
+
+ WaitForUserAction("After pressing Enter the remaining objects "
+ "will be deleted");
+
+ cout << "Cleanup by deleting the remaining Test1=1 and Test1=4 objects"
+ << endl;
+
+ CcbDescriptorCleaner(ccb_descriptor);
+ delete_descriptor.object_name = "Test1=1,safApp=safSmfService";
+ ccb_descriptor.AddDelete(delete_descriptor);
+ delete_descriptor.object_name = "Test1=4,safApp=safSmfService";
+ ccb_descriptor.AddDelete(delete_descriptor);
+
+ // The modifier object is used here for the third time
+ if (modifier.DoModelModification(ccb_descriptor) == false) {
+ cout << "Delete Test1=1 and Test1=4, FAIL" << endl;
+ } else {
+ cout << "Delete Test1=1 and Test1=4, SUCCESS" << endl;
+ }
+
+ return 0;
+}
\ No newline at end of file
diff --git a/src/smf/smfd/imm_om_api/om_admin_owner_clear.h
b/src/smf/smfd/imm_om_api/om_admin_owner_clear.h
index 89bb8bf47..b2d2ba4a7 100644
--- a/src/smf/smfd/imm_om_api/om_admin_owner_clear.h
+++ b/src/smf/smfd/imm_om_api/om_admin_owner_clear.h
@@ -55,6 +55,17 @@ class ImmOmAdminOwnerClear : public ImmBase {
~ImmOmAdminOwnerClear();
+ // Set a new OM handle. Needed when recovering from BAD_HANDLE
+ void UpdateHandle(const SaImmHandleT& om_handle) {
+ om_handle_ = om_handle;
+ }
+
+ // Set new objects to clear
+ void SetObjectList(const std::vector<std::string>& list_of_object_names) {
+ object_names_.clear();
+ object_names_ = list_of_object_names;
+ }
+
// Clear admin owner of object names on given scope.
// Use ais_error() to get returned AIS code.
bool ClearAdminOwner(SaImmScopeT scope = SA_IMM_SUBTREE);
diff --git a/src/smf/smfd/imm_om_api/om_admin_owner_handle.h
b/src/smf/smfd/imm_om_api/om_admin_owner_handle.h
index 93080a8e7..515c1f23c 100644
--- a/src/smf/smfd/imm_om_api/om_admin_owner_handle.h
+++ b/src/smf/smfd/imm_om_api/om_admin_owner_handle.h
@@ -54,6 +54,7 @@ class ImmOmAdminOwnerHandle : public ImmBase {
const std::string& admin_owner_name);
~ImmOmAdminOwnerHandle();
+
// Use before InitializeHandle() if admin ownership shall be kept when handle
// is finalized
ImmOmAdminOwnerHandle& KeepOwnershipOnFinalize() {
@@ -73,6 +74,13 @@ class ImmOmAdminOwnerHandle : public ImmBase {
// or finalized, false otherwise.
// Use ais_error() to get AIS return code.
bool InitializeHandle();
+ // Initialize with a new OM handle. Needed for recovery
+ bool InitializeHandle(const SaImmHandleT& om_handle) {
+ FinalizeHandle();
+ om_handle_ = om_handle;
+ admin_owner_handle_ = 0;
+ return InitializeHandle();
+ }
bool FinalizeHandle();
// Get the IMM Admin Owner handle and implicitly do initializing if the
handle
diff --git a/src/smf/smfd/imm_om_api/om_admin_owner_set.h
b/src/smf/smfd/imm_om_api/om_admin_owner_set.h
index 762954530..9638c63e6 100644
--- a/src/smf/smfd/imm_om_api/om_admin_owner_set.h
+++ b/src/smf/smfd/imm_om_api/om_admin_owner_set.h
@@ -62,6 +62,12 @@ class ImmOmAdminOwnerSet : public ImmBase {
object_names_.push_back(object_name);
}
+ // Set a new OM handle. Needed when recovering from BAD_HANDLE
+ void UpdateHandle(const SaImmAdminOwnerHandleT& admin_owner_handle) {
+ admin_owner_handle_ = admin_owner_handle;
+ is_set_admin_owner_invoked_ = false;
+ }
+
// Clear the list of object names
// Note: this will not release the admin owner
void ClearObjectNames(void) {
diff --git a/src/smf/smfd/imm_om_api/om_ccb_handle.cc
b/src/smf/smfd/imm_om_api/om_ccb_handle.cc
index 0916b45c3..9f6098f13 100644
--- a/src/smf/smfd/imm_om_api/om_ccb_handle.cc
+++ b/src/smf/smfd/imm_om_api/om_ccb_handle.cc
@@ -35,7 +35,7 @@ ImmOmCcbHandle::ImmOmCcbHandle(
: ImmOmCcbHandle{admin_owner_handle, SA_IMM_CCB_REGISTERED_OI} {}
ImmOmCcbHandle::~ImmOmCcbHandle() {
- TRACE_ENTER();
+ TRACE_ENTER2("LLDTESTOOO");
FinalizeHandle();
}
@@ -69,6 +69,7 @@ bool ImmOmCcbHandle::FinalizeHandle() {
base::Timer wtime(retry_ctrl_.timeout);
while (wtime.is_timeout() == false) {
ais_error_ = saImmOmCcbFinalize(ccb_handle_);
+ TRACE("LLDTESTOOO %s: saImmOmCcbFinalize() = %s", __FUNCTION__,
saf_error(ais_error_));
if (ais_error_ == SA_AIS_ERR_TRY_AGAIN) {
base::Sleep(retry_ctrl_.interval);
continue;
diff --git a/src/smf/smfd/imm_om_api/om_ccb_handle.h
b/src/smf/smfd/imm_om_api/om_ccb_handle.h
index be335d9e7..72ba05cbf 100644
--- a/src/smf/smfd/imm_om_api/om_ccb_handle.h
+++ b/src/smf/smfd/imm_om_api/om_ccb_handle.h
@@ -61,6 +61,13 @@ class ImmOmCcbHandle : public ImmBase {
// Returns false on error.
// Use ais_error() to get AIS return code.
bool InitializeHandle();
+ // Initialize with a new AO handle. Needed for recovery
+ bool InitializeHandle(const SaImmAdminOwnerHandleT& admin_owner_handle) {
+ FinalizeHandle();
+ admin_owner_handle_ = admin_owner_handle;
+ ccb_handle_ = 0;
+ return InitializeHandle();
+ }
bool FinalizeHandle();
bool ApplyCcb();
diff --git a/src/smf/smfd/imm_om_api/om_ccb_object_create.h
b/src/smf/smfd/imm_om_api/om_ccb_object_create.h
index 656b9bfcb..ac799a415 100644
--- a/src/smf/smfd/imm_om_api/om_ccb_object_create.h
+++ b/src/smf/smfd/imm_om_api/om_ccb_object_create.h
@@ -89,6 +89,11 @@ class ImmOmCcbObjectCreate : public ImmBase {
explicit ImmOmCcbObjectCreate(const SaImmCcbHandleT& ccb_handle);
~ImmOmCcbObjectCreate();
+ // Set a new CCB handle. Needed when recovering from BAD_HANDLE
+ void UpdateHandle(const SaImmCcbHandleT& ccb_handle) {
+ ccb_handle_ = ccb_handle;
+ }
+
// If this method is not called the object will be created as a top level
// object
ImmOmCcbObjectCreate& SetParentName(const std::string& parent_object);
diff --git a/src/smf/smfd/imm_om_api/om_ccb_object_delete.h
b/src/smf/smfd/imm_om_api/om_ccb_object_delete.h
index 9474e063d..93fc9858f 100644
--- a/src/smf/smfd/imm_om_api/om_ccb_object_delete.h
+++ b/src/smf/smfd/imm_om_api/om_ccb_object_delete.h
@@ -59,6 +59,11 @@ class ImmOmCcbObjectDelete : public ImmBase {
explicit ImmOmCcbObjectDelete(const SaImmCcbHandleT& ccb_handle);
~ImmOmCcbObjectDelete();
+ // Set a new CCB handle. Needed when recovering from BAD_HANDLE
+ void UpdateHandle(const SaImmCcbHandleT& ccb_handle) {
+ ccb_handle_ = ccb_handle;
+ }
+
// Returns false if failing
// Use ais_error() to get returned AIS code.
bool AddObjectDeleteToCcb(const std::string& object_name);
diff --git a/src/smf/smfd/imm_om_api/om_ccb_object_modify.h
b/src/smf/smfd/imm_om_api/om_ccb_object_modify.h
index fd94a084b..e46806b81 100644
--- a/src/smf/smfd/imm_om_api/om_ccb_object_modify.h
+++ b/src/smf/smfd/imm_om_api/om_ccb_object_modify.h
@@ -67,6 +67,11 @@ class ImmOmCcbObjectModify : public ImmBase {
const std::string& object_name);
~ImmOmCcbObjectModify();
+ // Set a new CCB handle. Needed when recovering from BAD_HANDLE
+ void UpdateHandle(const SaImmCcbHandleT& ccb_handle) {
+ ccb_handle_ = ccb_handle;
+ }
+
// Replace all existing values with a new set of values
// T must be AIS data types or std::string.
// NOTE: must explicitly give typename T if passing NULL value.
diff --git a/src/smf/smfd/imm_om_api/om_handle.cc
b/src/smf/smfd/imm_om_api/om_handle.cc
index 58a3e8cb3..c942e0fb3 100644
--- a/src/smf/smfd/imm_om_api/om_handle.cc
+++ b/src/smf/smfd/imm_om_api/om_handle.cc
@@ -40,7 +40,7 @@ ImmOmHandle::~ImmOmHandle() {
}
// Initialize @om_handle_ from IMM
-bool ImmOmHandle::InitializeHandle() {
+bool ImmOmHandle::OmInitialize() {
TRACE_ENTER();
SaVersionT version = version_;
ais_error_ = SA_AIS_OK;
@@ -84,7 +84,7 @@ bool ImmOmHandle::FinalizeHandle() {
SaImmHandleT ImmOmHandle::GetHandle() {
// Implicitly initialize handle
- if (om_handle_ == 0) InitializeHandle();
+ if (om_handle_ == 0) OmInitialize();
return om_handle_;
}
@@ -135,6 +135,9 @@ bool ImmOmHandle::DispatchBlocking() {
return Dispatch(SA_DISPATCH_BLOCKING);
}
+// TODO(Lennart) Removed like this. We may want this back if this becomes a
+// library again in the future
+#if 0
// To enable tracing early when the library is loaded.
__attribute__((constructor))
static void logtrace_init_constructor(void) {
@@ -147,5 +150,6 @@ static void logtrace_init_constructor(void) {
}
}
}
+#endif
} // namespace immom
diff --git a/src/smf/smfd/imm_om_api/om_handle.h
b/src/smf/smfd/imm_om_api/om_handle.h
index 6c71fa14a..8b4eee2ab 100644
--- a/src/smf/smfd/imm_om_api/om_handle.h
+++ b/src/smf/smfd/imm_om_api/om_handle.h
@@ -55,12 +55,14 @@ class ImmOmHandle : public ImmBase {
const SaImmCallbacksT* callbacks = nullptr);
~ImmOmHandle();
- void SetDummy(int i) { dummy_ = 1; }
- int GetDummy(void) { return dummy_; }
-
- // Return true if IMM OM handle is successfully initialized/finalized.
+// Return true if IMM OM handle is successfully initialized/finalized.
// Use ais_error() to get AIS return code.
- bool InitializeHandle();
+ bool InitializeHandle() {
+ FinalizeHandle();
+ om_handle_ = 0;
+ return OmInitialize();
+ }
+ bool OmInitialize();
bool FinalizeHandle();
// Fetch the operating system handle associated with the handle OM handle.
@@ -93,7 +95,6 @@ class ImmOmHandle : public ImmBase {
SaVersionT version_;
SaVersionT supported_version_;
SaImmHandleT om_handle_;
- int dummy_;
DELETE_COPY_AND_MOVE_OPERATORS(ImmOmHandle);
};
--
2.15.1
------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
Opensaf-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/opensaf-devel