When IMM handle is initialized with lower version than A.2.14, after ccbApply, 
ccbAbort should return ERR_VERSION, but SA_AIS_OK is returned.
When CCB is not applied, ccbAbort works fine and returns ERR_VERSION.
The rest of the code is reviewed and tested, and it's ok.

NACK until the version is fixed.

Best regards,
Zoran

-----Original Message-----
From: Anders Björnerstedt 
Sent: den 4 april 2014 14:53
To: reddy.neelaka...@oracle.com; Zoran Milinkovic
Cc: opensaf-devel@lists.sourceforge.net
Subject: [PATCH 2 of 3] IMM: Support for saImmOmCcbValidate API [#798]

 osaf/libs/agents/saf/imma/imma_cb.h                        |    1 +
 osaf/libs/agents/saf/imma/imma_oi_api.c                    |    2 +-
 osaf/libs/agents/saf/imma/imma_om_api.c                    |  166 ++++++++++++-
 osaf/libs/common/immsv/immsv_evt.c                         |    4 +
 osaf/libs/common/immsv/include/immsv_evt.h                 |    1 +
 osaf/libs/saf/include/saImmOm_A_2_14.h                     |    3 +
 osaf/services/saf/immsv/immnd/ImmModel.cc                  |  167 ++++++++++--
 osaf/services/saf/immsv/immnd/ImmModel.hh                  |    3 +-
 osaf/services/saf/immsv/immnd/immnd_evt.c                  |   73 ++++-
 osaf/services/saf/immsv/immnd/immnd_init.h                 |    4 +-
 tests/immsv/implementer/test_SaImmOiCcb.c                  |  120 ++++++++-
 tests/immsv/implementer/test_saImmOiAugmentCcbInitialize.c |   38 ++-
 tests/immsv/management/test_saImmOmCcbApply.c              |   89 ++++++
 tests/immsv/management/test_saImmOmCcbInitialize.c         |    4 +
 tests/unit_test_fw/src/utest.c                             |    2 +-
 15 files changed, 607 insertions(+), 70 deletions(-)


See ticket or osaf/services/saf/immsv/README for details.

diff --git a/osaf/libs/agents/saf/imma/imma_cb.h 
b/osaf/libs/agents/saf/imma/imma_cb.h
--- a/osaf/libs/agents/saf/imma/imma_cb.h
+++ b/osaf/libs/agents/saf/imma/imma_cb.h
@@ -98,6 +98,7 @@ typedef struct imma_ccb_node {
                                                  timeout => Ccb-outcome to be 
recovered. */
        bool mApplied;     /* Current mCcbId applied&terminated */
        bool mAborted;     /* Current mCcbId aborted */
+       bool mValidated;   /* Current mCcbId validated */
        bool mAugCcb;      /* Current and only mCcbId is an augment. */
        bool mAugIsTainted;/* AugCcb has tainted root CCB => apply aug or abort 
root*/
 } IMMA_CCB_NODE;
diff --git a/osaf/libs/agents/saf/imma/imma_oi_api.c 
b/osaf/libs/agents/saf/imma/imma_oi_api.c
--- a/osaf/libs/agents/saf/imma/imma_oi_api.c
+++ b/osaf/libs/agents/saf/imma/imma_oi_api.c
@@ -3417,7 +3417,7 @@ SaAisErrorT saImmOiAugmentCcbInitialize(
        IMMA_CALLBACK_INFO * cbi=NULL;
        SaImmHandleT privateOmHandle = 0LL;
        SaImmAdminOwnerHandleT privateAoHandle = 0LL;
-       SaVersionT version = {'A', 2, 11};
+       SaVersionT version = {'A', 2, 14};
        SaUint32T adminOwnerId = 0;
        SaUint32T ccbId = 0;
 
diff --git a/osaf/libs/agents/saf/imma/imma_om_api.c 
b/osaf/libs/agents/saf/imma/imma_om_api.c
--- a/osaf/libs/agents/saf/imma/imma_om_api.c
+++ b/osaf/libs/agents/saf/imma/imma_om_api.c
@@ -54,6 +54,7 @@ static const char *sysaImplName = SA_IMM
 
 static int imma_om_resurrect(IMMA_CB *cb, IMMA_CLIENT_NODE *cl_node, bool 
*locked);
 static SaAisErrorT imma_finalizeCcb(SaImmCcbHandleT ccbHandle, bool 
keepCcbHandleOpen);
+static SaAisErrorT imma_applyCcb(SaImmCcbHandleT ccbHandle, bool onlyValidate);
 
 /****************************************************************************
   Name          :  SaImmOmInitialize
@@ -2873,6 +2874,11 @@ SaAisErrorT saImmOmCcbObjectDelete(SaImm
 ******************************************************************************/
 SaAisErrorT saImmOmCcbApply(SaImmCcbHandleT ccbHandle)
 {
+       return imma_applyCcb(ccbHandle, false);
+}
+
+SaAisErrorT imma_applyCcb(SaImmCcbHandleT ccbHandle, bool onlyValidate)
+{
        SaAisErrorT rc = SA_AIS_OK;
        IMMA_CB *cb = &imma_cb;
        IMMSV_EVT evt;
@@ -2924,7 +2930,9 @@ SaAisErrorT saImmOmCcbApply(SaImmCcbHand
                   ccb-id has been EXPLICITLY applied by the user. 
                   This can only be done by a successful explicit 
                   saImmOmCcbApply. A ccb-handle with an aborted ccb-id
-                  can not be used any more. Only finalize is allowed on handle.
+                  can only be used again after an explcit saImmOmCcbAbort() 
has been
+                  invoked on the handle. Otherwise only finalize is allowed on
+                   the handle.
 
                   Setting mApplied to true opens for the IMPLICIT 
                   start of a new ccb-id with the current and same 
SaImmCcbHandleT value.
@@ -2949,6 +2957,11 @@ SaAisErrorT saImmOmCcbApply(SaImmCcbHand
                goto done;
        }
 
+       if (ccb_node->mValidated && onlyValidate) {
+               rc = SA_AIS_OK; /* Validation is idempotent on clientr side */
+               goto done;
+       }
+
        immHandle = ccb_node->mImmHandle;
 
        /* Free string from previous ccb-op */
@@ -2977,9 +2990,20 @@ SaAisErrorT saImmOmCcbApply(SaImmCcbHand
                goto done;
        }
 
+       if(onlyValidate && !(cl_node->isImmA2e)) {
+               LOG_IN("ERR_VERSION: saImmOmCcbValidate() only supported in 
version A.02.14 or higher");
+               rc = SA_AIS_ERR_VERSION;
+               goto done;
+       }
+
        if(ccb_node->mAugCcb) {
-               TRACE("Apply for augmentation for CcbId %u", ccb_node->mCcbId);
-               ccb_node->mApplied = true;
+               if(onlyValidate) {
+                       LOG_IN("ERR_BAD_OPERATION: saImmOmCcbValidate() not 
allowed on augmented ccbs");
+                       rc = SA_AIS_ERR_BAD_OPERATION;
+               } else {
+                       TRACE("Apply for augmentation for CcbId %u", 
ccb_node->mCcbId);
+                       ccb_node->mApplied = true;
+               }
                goto done;
        }
 
@@ -2988,7 +3012,7 @@ SaAisErrorT saImmOmCcbApply(SaImmCcbHand
        /* Populate the CcbApply event */
        memset(&evt, 0, sizeof(IMMSV_EVT));
        evt.type = IMMSV_EVT_TYPE_IMMND;
-       evt.info.immnd.type = IMMND_EVT_A2ND_CCB_APPLY;
+       evt.info.immnd.type = (onlyValidate) ? IMMND_EVT_A2ND_CCB_VALIDATE : 
IMMND_EVT_A2ND_CCB_APPLY;
        evt.info.immnd.info.ccbId = ccb_node->mCcbId;
 
        if((rc = imma_proc_increment_pending_reply(cl_node, true)) != 
SA_AIS_OK) {
@@ -2997,7 +3021,7 @@ SaAisErrorT saImmOmCcbApply(SaImmCcbHand
        }
 
        ccb_node->mExclusive = true; 
-       ccb_node->mApplying = true;
+       ccb_node->mApplying = !onlyValidate;
        ccbId = ccb_node->mCcbId;
 
        rc = imma_evt_fake_evs(cb, &evt, &out_evt, cl_node->syncr_timeout, 
cl_node->handle, &locked, false);
@@ -3086,7 +3110,8 @@ SaAisErrorT saImmOmCcbApply(SaImmCcbHand
                        ccb_node->mApplying = false;
                        osafassert(ccb_node->mErrorStrings == NULL);
                        if (rc == SA_AIS_OK) {
-                               ccb_node->mApplied = true;  
+                               ccb_node->mValidated = true;
+                               ccb_node->mApplied = !onlyValidate;
                                TRACE_1("CCB APPLY - Successful Apply for ccb 
id %u", 
                                        ccb_node->mCcbId);
                        } else {
@@ -3184,6 +3209,18 @@ SaAisErrorT saImmOmCcbApply(SaImmCcbHand
                  we dont want it to indicate aborted, nor applied, nor active.
                */
                TRACE_3("client_node %p exposed :%u", cl_node, cl_node ? 
(cl_node->exposed) : 0);
+               if(onlyValidate) {
+                       /* Let ERR_TIMEOUT reach the user when only validation 
was attempted.
+                          This means the user does not know if validation 
succeeded or not.
+                          They can choose to try to apply/commit the ccb which 
could succeed if
+                          validation did succeed. But if validation failed 
then the apply/commit
+                          will fail. 
+
+                          If the validation is still on-going when the attempt 
to apply/commit
+                          reaches the immsv, then the user should get 
TRY_AGAIN from local IMMND.
+                        */
+                       goto done;
+               }
 
                /* Reset the ccb_node flags back to indicate apply is in 
progress. */
                ccb_node->mExclusive = true; 
@@ -3269,12 +3306,18 @@ SaAisErrorT saImmOmCcbApply(SaImmCcbHand
  
   Description   :  Aborts a CCB(id) without finalizing the ccb-handle.
                    Discards any ccb operations currently associated with the 
ccb-handle.
-                   If SA_AIS_OK is returned then ccb-handle can continue to be 
used and
-                   is in the same empty state as if it had just been 
initialized.
+                   Also resetts a ccbHandle that has previously received the 
abort
+                   return code SA_AIS_ERR_FAILED_OPERATION. 
 
                    Previously it was only possible to explicitly abort an 
active ccb
                    by invoking saImOmCcbFinalize() which also closes the 
ccb-handle.
 
+                   Previously it was also not possible to reset a ccbHandle 
that had
+                   received the ccb-aborted return code: 
SA_AIS_ERR_FAILED_OPERATION.
+                   
+                   If SA_AIS_OK is returned then ccb-handle can continue to be 
used and
+                   is in the same empty state as if it had just been 
initialized.                   
+
                    This a blocking syncronous call.
 
                    
@@ -3297,6 +3340,52 @@ SaAisErrorT saImmOmCcbAbort(SaImmCcbHand
 }
 
 
+/****************************************************************************
+  Name          :  saImmOmCcbValidate
+ 
+  Description   :  Performs the validation part of ccb-apply for the CCB in 
its 
+                   current active state. If validation succeeds, then no 
additional
+                   ccb-operations can be added to the ccb until the current 
set of
+                   operations have been applied or aborted. An apply of a 
successfully
+                   validated ccb is highly likely to succeed, but still not 
guaranteed
+                   to succeed. If an apply of a validated ccb fails, it will 
obviously
+                   not be due to any failure in validation, but for "physical" 
reasons,
+                   such as some problem with the PBE (persistent back end), or 
a crash
+                   of some vital IMM process. 
+
+                   Thus after a successfull saImmOmCcbVAlidate, the ccb is in 
a state
+                   that only accepts one of:
+
+                      - saImmOmCcbApply(ccbHandle);  - Only applicable if 
saImmOmCcbValidate
+                                                       returned SA_AIS_OK, 
SA_AIS_ERR_TIMEOUT,
+                                                       or SA_AIS_ERR_TRY_AGAIN 
(validation not done).
+
+                      - saImmOmCcbAbort(ccbHandle);    - Always applicable if 
handle is valid.
+                      - saImmOmCcbFinalize(ccbHandle); - Always applicable if 
handle is valid.
+                   
+  Arguments     :  ccbHandle - Ccb Handle
+
+  Return Values :  Same return values as saImmOmCcbApply with the following 
adjustment:
+                   SA_AIS_OK - Validation succeeded. Ccb has not been 
committed/applied.
+                               This ccb can now be aborted or an attempt may 
be made to
+                               apply/commit. 
+
+                   Refer to SAI-AIS IMM A.2.1 specification for the other 
return values
+                   and to the OpenSAF_IMMSv_PR (programmers reference) for 
implementation
+                   specific details. 
+
+                   Note that just as for regular saImm*OmCcbApply an abort is 
signalled by:
+                   SA_AIS_ERR_FAILED_OPERATION - Ccb is aborted possibly due 
to failed
+                                                 validation.
+
+ 
+******************************************************************************/
+SaAisErrorT saImmOmCcbValidate(SaImmCcbHandleT ccbHandle)
+{
+       return imma_applyCcb(ccbHandle, true);
+}
+
+
 
 /****************************************************************************
   Name          :  saImmOmAdminOperationInvoke_2/_o2
@@ -7669,6 +7758,7 @@ static SaAisErrorT imma_finalizeCcb(SaIm
        SaUint32T ccbId = 0;
        SaImmHandleT immHandle = 0LL;
        SaImmAdminOwnerHandleT adminOwnerHdl = 0LL;
+       SaUint32T adminOwnerId = 0;
        SaUint32T timeout = 0;
        TRACE_ENTER();
 
@@ -7823,7 +7913,67 @@ static SaAisErrorT imma_finalizeCcb(SaIm
                if(keepCcbHandleOpen) { /* saImmOmCcbAbort */
                        osafassert(ccb_node);
                        ccb_node->mApplied = true;
+                       ccb_node->mAborted = false;
+                       ccb_node->mValidated = false;
                        ccb_node->mExclusive = false;
+
+                       /* Fetch cl_node again and generate new ccb_id.  We 
need to set up
+                          a pristine ccb-id to make the ccb-handle identical 
to a one just
+                          obtained from a saImmOmCcbInitialize.
+                        */
+                       imma_client_node_get(&cb->client_tree, &immHandle, 
&cl_node);
+                       if (!(cl_node && cl_node->isOm)) {
+                               rc = SA_AIS_ERR_LIBRARY;
+                               TRACE_4("ERR_LIBRARY: No valid SaImmHandleT 
associated with Ccb");
+                               goto done;
+                       }
+
+                       if (cl_node->stale) {
+                               TRACE_1("IMM Handle %llx is stale", immHandle);
+                               rc = SA_AIS_OK; 
+                               imma_ccb_node_delete(cb, ccb_node);
+                               ccb_node = NULL;
+                               goto done;
+                       }
+
+                       /* Get the Admin Owner info  */
+                       imma_admin_owner_node_get(&cb->admin_owner_tree, 
&adminOwnerHdl, &ao_node);
+                       if (!ao_node) {
+                               rc = SA_AIS_ERR_BAD_HANDLE;
+                               TRACE_3("ERR_BAD_HANDLE: Amin-Owner node 
associated with Ccb is missing");
+                               goto done;
+                       }
+
+                       adminOwnerId = ao_node->mAdminOwnerId;
+
+                       if((rc = imma_proc_increment_pending_reply(cl_node, 
true)) != SA_AIS_OK) {
+                               TRACE_4("ERR_LIBRARY: Overlapping use of IMM 
handle by multiple threads");
+                               goto done;
+                       }
+
+                       rc = imma_newCcbId(cb, ccb_node, adminOwnerId, &locked, 
cl_node->syncr_timeout);
+                       cl_node=NULL;
+                       if(rc != SA_AIS_OK) {goto done;}
+                       /* ccb_node still valid if rc == SA_AIS_OK. */
+                       if(rc == SA_AIS_OK) {
+                               osafassert(!(ccb_node->mExclusive));
+                               osafassert(locked);
+                       }
+
+                       imma_client_node_get(&cb->client_tree, &immHandle, 
&cl_node);
+                       if (!(cl_node && cl_node->isOm)) {
+                               rc = SA_AIS_ERR_BAD_HANDLE;
+                               TRACE_3("ERR_BAD_HANDLE: Client node not found 
after down call");
+                               imma_ccb_node_delete(cb, ccb_node); /*Remove 
node from tree and free it. */
+                               ccb_node = NULL;
+                               goto done;
+                       }
+
+                       imma_proc_decrement_pending_reply(cl_node, true);
+
+                       /* Dont care if cl_node is stale here. This will be 
caught
+                          in the next attempt to use the related handle(s).
+                        */
                }
        }
 
diff --git a/osaf/libs/common/immsv/immsv_evt.c 
b/osaf/libs/common/immsv/immsv_evt.c
--- a/osaf/libs/common/immsv/immsv_evt.c
+++ b/osaf/libs/common/immsv/immsv_evt.c
@@ -172,6 +172,7 @@ static const char *immnd_evt_names[] = {
        "IMMND_EVT_A2ND_AUG_ADMO",
        "IMMND_EVT_A2ND_CL_TIMEOUT",
        "IMMND_EVT_A2ND_ACCESSOR_GET",
+       "IMMND_EVT_A2ND_CCB_VALIDATE",  /* saImmOmCcbValidate */
        "undefined (high)"
 };
 
@@ -3365,6 +3366,8 @@ static uint32_t immsv_evt_enc_toplevel(I
                case IMMND_EVT_A2ND_CCB_APPLY:  /* saImmOmCcbApply */
                case IMMND_EVT_A2ND_CCB_FINALIZE:       /* saImmOmCcbFinalize */
                case IMMND_EVT_A2ND_RECOVER_CCB_OUTCOME:
+               case IMMND_EVT_A2ND_CCB_VALIDATE:       /* saImmOmCcbValidate */
+
                        IMMSV_RSRV_SPACE_ASSERT(p8, o_ub, 4);
                        ncs_encode_32bit(&p8, immndevt->info.ccbId);
                        ncs_enc_claim_space(o_ub, 4);
@@ -4698,6 +4701,7 @@ static uint32_t immsv_evt_dec_toplevel(N
                case IMMND_EVT_A2ND_CCB_APPLY:  /* saImmOmCcbApply */
                case IMMND_EVT_A2ND_CCB_FINALIZE:       /* saImmOmCcbFinalize */
                case IMMND_EVT_A2ND_RECOVER_CCB_OUTCOME:
+               case IMMND_EVT_A2ND_CCB_VALIDATE:       /* saImmOmCcbValidate */
                        IMMSV_FLTN_SPACE_ASSERT(p8, local_data, i_ub, 4);
                        immndevt->info.ccbId = ncs_decode_32bit(&p8);
                        ncs_dec_skip_space(i_ub, 4);
diff --git a/osaf/libs/common/immsv/include/immsv_evt.h 
b/osaf/libs/common/immsv/include/immsv_evt.h
--- a/osaf/libs/common/immsv/include/immsv_evt.h
+++ b/osaf/libs/common/immsv/include/immsv_evt.h
@@ -204,6 +204,7 @@ typedef enum immnd_evt_type {
        IMMND_EVT_A2ND_CL_TIMEOUT = 93, /* Inform local IMMND of a library 
timeout. */
 
        IMMND_EVT_A2ND_ACCESSOR_GET = 94,       /* saImmOmAccessorGet_2 */
+       IMMND_EVT_A2ND_CCB_VALIDATE = 95,       /* saImmOmCcbValidate */
 
        IMMND_EVT_MAX
 } IMMND_EVT_TYPE;
diff --git a/osaf/libs/saf/include/saImmOm_A_2_14.h 
b/osaf/libs/saf/include/saImmOm_A_2_14.h
--- a/osaf/libs/saf/include/saImmOm_A_2_14.h
+++ b/osaf/libs/saf/include/saImmOm_A_2_14.h
@@ -37,6 +37,9 @@ extern "C" {
 /* 4.8.x saImmOmCcb */
 
        extern SaAisErrorT
+        saImmOmCcbValidate(SaImmCcbHandleT ccbHandle);
+
+       extern SaAisErrorT
         saImmOmCcbAbort(SaImmCcbHandleT ccbHandle);
 
 #ifdef  __cplusplus
diff --git a/osaf/services/saf/immsv/immnd/ImmModel.cc 
b/osaf/services/saf/immsv/immnd/ImmModel.cc
--- a/osaf/services/saf/immsv/immnd/ImmModel.cc
+++ b/osaf/services/saf/immsv/immnd/ImmModel.cc
@@ -302,12 +302,14 @@ typedef enum {
     IMM_CCB_CREATE_OP = 3,  //Ongoing create (pending implementer 
calls/replies)
     IMM_CCB_MODIFY_OP = 4,  //Ongoing modify (pending implementer 
calls/replies)
     IMM_CCB_DELETE_OP = 5,  //Ongoing delete (pending implementer 
calls/replies)
-    IMM_CCB_PREPARE = 6,    //Waiting for nodes prepare & completed 
calls/replies
-    IMM_CCB_CRITICAL = 7,   //Unilateral abort no longer allowed (except by 
PBE). 
-    IMM_CCB_PBE_ABORT = 8,  //The Persistent back end replied with abort
-    IMM_CCB_COMMITTED = 9,  //Committed at nodes pending implementer apply 
calls
-    IMM_CCB_ABORTED = 10,   //READY->ABORTED PREPARE->ABORTED
-    IMM_CCB_ILLEGAL = 11   //CCB has been removed.
+    IMM_CCB_VALIDATING = 6, //Explicit validate started (saImmOmCcbValidate 
only)
+    IMM_CCB_VALIDATED = 7,  //Explicit validate has completed 
(saImmOmCcbValidate only)
+    IMM_CCB_PREPARE = 8,    //Waiting for nodes prepare & completed 
calls/replies
+    IMM_CCB_CRITICAL = 9,   //Unilateral abort no longer allowed (except by 
PBE). 
+    IMM_CCB_PBE_ABORT = 10, //The Persistent back end replied with abort
+    IMM_CCB_COMMITTED = 11, //Committed at nodes pending implementer apply 
calls
+    IMM_CCB_ABORTED = 12,   //READY->ABORTED PREPARE->ABORTED
+    IMM_CCB_ILLEGAL = 13    //CCB has been removed.
 } ImmCcbState;
 
 
@@ -978,7 +980,8 @@ immModel_ccbApply(IMMND_CB *cb,
     SaUint32T* arrSize,
     SaUint32T** implConnArr,
     SaUint32T** implIdArr,
-    SaUint32T** ctnArr)
+    SaUint32T** ctnArr,
+    bool validateOnly)
 {
     ConnVector cv;
     IdVector implsv;
@@ -990,7 +993,7 @@ immModel_ccbApply(IMMND_CB *cb,
     
     
     SaAisErrorT err = ImmModel::instance(&cb->immModel)->
-        ccbApply(ccbId, reqConn, cv, implsv, ctnv);
+        ccbApply(ccbId, reqConn, cv, implsv, ctnv, validateOnly);
     
     *arrSize = (SaUint32T) cv.size();
     if(*arrSize) {
@@ -4322,20 +4325,32 @@ ImmModel::ccbResult(SaUint32T ccbId)
             case IMM_CCB_CREATE_OP:
             case IMM_CCB_MODIFY_OP:
             case IMM_CCB_DELETE_OP:
-                LOG_WA("ccbResult: CCB %u is active! state:%u.", ccbId, 
(*i)->mState);
+                LOG_WA("ccbResult: CCB %u is active, state:%u.", ccbId, 
(*i)->mState);
                     err = SA_AIS_ERR_TRY_AGAIN;
-                break; //Unusual
+                break; 
 
             case IMM_CCB_PREPARE:
-                LOG_WA("ccbResult: CCB %u in prepare! Commit/abort in 
progress?", 
+                LOG_WA("ccbResult: CCB %u in state 'prepare' validation in 
progress", 
                     ccbId);
                 err = SA_AIS_ERR_TRY_AGAIN;
-                break; //Unusual
+                break; 
+
+            case IMM_CCB_VALIDATING:
+                LOG_WA("ccbResult: CCB %u in state 'validating' explicit 
validation in progress", 
+                    ccbId);
+                err = SA_AIS_ERR_TRY_AGAIN;
+                break; 
+
+            case IMM_CCB_VALIDATED:
+                LOG_WA("ccbResult: CCB %u in state 'validated'. Waiting for 
apply or abort/finalize", 
+                    ccbId);
+                err = SA_AIS_ERR_TRY_AGAIN;
+                break;
 
             case IMM_CCB_CRITICAL:
-                LOG_NO("ccbResult: CCB %u in critical state! Commit/apply in 
progress?", ccbId);
+                LOG_NO("ccbResult: CCB %u in critical state! Commit/apply in 
progress", ccbId);
                 err = SA_AIS_ERR_TRY_AGAIN;
-                break; //Can happen if PBE crashes.
+                break; //Can be here if PBE is crashed, hung on fs, or 
backloged.
 
             default:
                 LOG_ER("ccbResult: Illegal state %u in ccb %u", (*i)->mState, 
ccbId);
@@ -4505,11 +4520,16 @@ ImmModel::ccbApply(SaUint32T ccbId,
     SaUint32T reqConn,
     ConnVector& connVector,
     IdVector& implIds,
-    IdVector& continuations)
+    IdVector& continuations,
+    bool validateOnly)
 {
     SaAisErrorT err = SA_AIS_OK;
     TRACE_ENTER();
-    TRACE_5("APPLYING CCB ID:%u", ccbId);
+    if(validateOnly) {
+        TRACE_5("Explicit VALIDATE started for CCB ID:%u", ccbId);
+    } else {
+        TRACE_5("APPLYING CCB ID:%u", ccbId);
+    }
     
     CcbVector::iterator i;
     AdminOwnerVector::iterator i2;
@@ -4530,10 +4550,41 @@ ImmModel::ccbApply(SaUint32T ccbId,
                 ccb->mAdminOwnerId);
             ccb->mVeto = SA_AIS_ERR_BAD_HANDLE;
         } else if(ccb->mState > IMM_CCB_READY) {
-            LOG_NO("Ccb <%u> not in correct state (%u) for Apply ignoring 
request",
-                ccb->mId, ccb->mState);
-            err = SA_AIS_ERR_ACCESS_DENIED;
-            goto ignore;
+            if(ccb->mState == IMM_CCB_VALIDATING) {
+                LOG_IN("Ccb <%u> in incorrect state 'CCB_VALIDATING for "
+                       "saImmOmCcbValidate() ignoring request", ccb->mId);
+                err = SA_AIS_ERR_ACCESS_DENIED;
+                goto done;
+            } else if(ccb->mState == IMM_CCB_VALIDATED) {
+                if(validateOnly) {
+                    LOG_IN("Ccb <%u> in incorrect state 'CCB_VALIDATED for "
+                           "saImmOmCcbValidate() ignoring request", ccb->mId);
+                    err = SA_AIS_ERR_ACCESS_DENIED;
+                    goto done;
+                } else {
+                    LOG_IN("Ccb <%u> in state (IMM_CCB_VALIDATED) received 
apply/commit",
+                               ccb->mId);
+                    err = ccb->mVeto;
+                    if(err == SA_AIS_OK) {
+                        if(!ccb->mImplementers.empty()) {
+                            /* apply callback needs to be sent to 
implementers. */
+                           err = SA_AIS_ERR_INTERRUPT;
+                        }
+                    }
+                    goto done;
+               }
+
+            } else {
+                LOG_NO("Ccb <%u> not in correct state (%u) for Apply ignoring 
request",
+                    ccb->mId, ccb->mState);
+                /* This includes state IMM_CCB_VALIDATING which means explicit 
validation
+                   is in progress. Neihter a redundant saImmOmValidate(), nor 
a premature
+                   saImmOmCcbApply() can be accepted in this state. But a 
saImmOmCcbAbort()
+                   or a saImmOmCcbFinalize would be accepted. 
+                */
+                err = SA_AIS_ERR_ACCESS_DENIED;
+                goto done;
+            }
         }
 
         osafassert(reqConn==0 || (ccb->mOriginatingConn == reqConn));
@@ -4550,12 +4601,21 @@ ImmModel::ccbApply(SaUint32T ccbId,
             }
             err = SA_AIS_ERR_FAILED_OPERATION;
             ccb->mVeto = SA_AIS_ERR_FAILED_OPERATION;
-        } else if(validateNoDanglingRefs(ccb)) {
+        } else if(!validateNoDanglingRefs(ccb)) {
+            err = SA_AIS_ERR_FAILED_OPERATION;
+            ccb->mVeto = SA_AIS_ERR_FAILED_OPERATION;
+        } else {
             /* sMissingParents must be empty if err is SA_AIS_OK */
             osafassert(sMissingParents.empty());
 
-            TRACE_5("Apply CCB %u", ccb->mId);
-            ccb->mState = IMM_CCB_PREPARE;
+            if(validateOnly) {
+                TRACE_5("Validate CCB %u", ccb->mId);
+                ccb->mState = IMM_CCB_VALIDATING;
+            } else {
+                TRACE_5("Apply CCB %u", ccb->mId);
+                ccb->mState = IMM_CCB_PREPARE;
+            }
+
             CcbImplementerMap::iterator isi;
             for(isi = ccb->mImplementers.begin();
                 isi != ccb->mImplementers.end();
@@ -4600,11 +4660,9 @@ ImmModel::ccbApply(SaUint32T ccbId,
                     continuations.push_back(sLastContinuationId);
                 }
             }
-        } else {
-            err = SA_AIS_ERR_FAILED_OPERATION;
-        }
-    }
- ignore:
+        }
+    }
+ done:
     
     TRACE_LEAVE();
     return err;
@@ -4972,8 +5030,9 @@ ImmModel::ccbCommit(SaUint32T ccbId, Con
     if(ccb->mState == IMM_CCB_PREPARE) {
         ccb->mState = IMM_CCB_CRITICAL;
     } else {
+        TRACE_5("Ccb %u comitted by PBE now in state:%u", ccbId, ccb->mState);
         osafassert(ccb->mState == IMM_CCB_CRITICAL);
-        TRACE_5("Ccb %u comitted by persistent back end", ccbId);
+        TRACE_5("Comitting Ccb %u in IMMND", ccbId);
     }
     ccb->mWaitStartTime = 0;
 
@@ -5096,7 +5155,13 @@ ImmModel::ccbAbort(SaUint32T ccbId, Conn
             break;
             
         case IMM_CCB_PREPARE:
-            LOG_NO("Ccb %u aborted in COMPLETED processing", ccbId);
+        case IMM_CCB_VALIDATING:
+            LOG_NO("Ccb %u aborted in COMPLETED processing (validation)", 
ccbId);
+            *client = ccb->mOriginatingConn;
+            break;
+            
+        case IMM_CCB_VALIDATED:
+            LOG_NO("Ccb %u aborted after explicit validation", ccbId);
             *client = ccb->mOriginatingConn;
             break;
             
@@ -5232,12 +5297,18 @@ ImmModel::ccbTerminate(SaUint32T ccbId)
                 return SA_AIS_ERR_TRY_AGAIN;
                 
             case IMM_CCB_PREPARE:
+            case IMM_CCB_VALIDATING:
             case IMM_CCB_PBE_ABORT:
                 LOG_WA("Will not terminate ccb %u while waiting for "
                     "replies from implementers on completed ack", ccbId);
                 TRACE_LEAVE();
                 return SA_AIS_ERR_TRY_AGAIN;
                 
+            case IMM_CCB_VALIDATED: /* Could logically be allowed, but prefer 
propper cleanup */
+                LOG_NO("Will not terminate ccb %u in validated state, should 
be aborted first",  ccbId);
+                TRACE_LEAVE();
+                return SA_AIS_ERR_TRY_AGAIN;
+
             case IMM_CCB_CRITICAL:
                 LOG_WA("Will not terminate ccb %u in critical state ",  ccbId);
                 TRACE_LEAVE();
@@ -5467,6 +5538,8 @@ ImmModel::ccbAugmentInit(immsv_oi_ccb_up
             case IMM_CCB_COMMITTED:
             case IMM_CCB_ABORTED:
             case IMM_CCB_PREPARE:
+            case IMM_CCB_VALIDATING:
+            case IMM_CCB_VALIDATED:
             case IMM_CCB_PBE_ABORT:
             case IMM_CCB_CRITICAL:
                 LOG_ER("Ccb Augment attempted in wrong CCB state");
@@ -8498,6 +8571,27 @@ ImmModel::ccbWaitForCompletedAck(SaUint3
        will force attempts to recover ccb-outcome from the current PBE.
     */
 
+    if(ccb->mState == IMM_CCB_VALIDATING) {
+        /* Explicit validation (saImmOmCcbValidate) stop waiting. */
+        ccb->mState = IMM_CCB_VALIDATED;
+        TRACE_5("Explicit validation completed err state:%u for ccb:%u", *err, 
ccbId);
+        if((*err) == SA_AIS_OK) {
+           /* We interrupt the validate/apply after validation is completed 
and before
+              the commit phase of hte apply.
+           */
+            *err = SA_AIS_ERR_INTERRUPT;
+        }
+
+        return false;
+    }
+
+    if((ccb->mState == IMM_CCB_VALIDATED) && ((*err) == SA_AIS_OK)) {
+        TRACE("Continuing apply/commit for explicitly validated ccb:%u err 
state:%u", ccbId, *err);
+        TRACE("Dont think we ever get here.");
+        LOG_IN("GOING FROM IMM_CCB_VALIDATED to IMM_CCB_PREPARE Ccb:%u", 
ccbId);
+        ccb->mState = IMM_CCB_PREPARE;
+    }
+
     if(ccb->mState == IMM_CCB_CRITICAL) {
         /* This must be the PBE reply. Stop waiting. */
         TRACE_5("PBE replied with rc:%u for ccb:%u", *err, ccbId);
@@ -8684,6 +8778,7 @@ ImmModel::ccbCompletedContinuation(immsv
     SaUint32T ccbId = rsp->ccbId;
     CcbInfo* ccb = 0;
     CcbVector::iterator i1;
+    CcbImplementerMap::iterator ix;
     
     i1 = std::find_if(sCcbVector.begin(), sCcbVector.end(), CcbIdIs(ccbId));
     if(i1 == sCcbVector.end() || (!(*i1)->isActive()) ) {
@@ -8692,9 +8787,14 @@ ImmModel::ccbCompletedContinuation(immsv
         return;
     }
     ccb = *i1;
-    
-    CcbImplementerMap::iterator ix =
-        ccb->mImplementers.find(rsp->implId);
+
+    if(ccb->mState == IMM_CCB_VALIDATED) {
+        LOG_IN("GOING FROM IMM_CCB_VALIDATED to IMM_CCB_PREPARE Ccb:%u", 
ccbId);
+        ccb->mState = IMM_CCB_PREPARE;
+        goto done;
+    }
+    
+    ix = ccb->mImplementers.find(rsp->implId);
     if(ix == ccb->mImplementers.end()) {
         if((ccb->mVeto == SA_AIS_OK) && (ccb->mState < IMM_CCB_CRITICAL)) {
             LOG_WA("Completed continuation: implementer '%u' Not found "
@@ -8789,6 +8889,7 @@ ImmModel::ccbCompletedContinuation(immsv
             }
         }
     }
+ done:
     TRACE_LEAVE();  
 }
 
diff --git a/osaf/services/saf/immsv/immnd/ImmModel.hh 
b/osaf/services/saf/immsv/immnd/ImmModel.hh
--- a/osaf/services/saf/immsv/immnd/ImmModel.hh
+++ b/osaf/services/saf/immsv/immnd/ImmModel.hh
@@ -176,7 +176,8 @@ public:
                                  SaUint32T reqConn,
                                  ConnVector& connVector,
                                  IdVector& implIds,
-                                 IdVector& continuations);
+                                 IdVector& continuations,
+                                 bool validateOnly);
     
     SaAisErrorT         ccbTerminate(SaUint32T ccbId);
 
diff --git a/osaf/services/saf/immsv/immnd/immnd_evt.c 
b/osaf/services/saf/immsv/immnd/immnd_evt.c
--- a/osaf/services/saf/immsv/immnd/immnd_evt.c
+++ b/osaf/services/saf/immsv/immnd/immnd_evt.c
@@ -167,9 +167,8 @@ static void immnd_evt_pbe_admop_rsp(IMMN
 static void immnd_evt_proc_object_sync(IMMND_CB *cb,
        IMMND_EVT *evt, SaBoolT originatedAtThisNd, SaImmHandleT clnt_hdl, 
MDS_DEST reply_dest, SaUint64T msgNo);
 
-static void immnd_evt_proc_ccb_apply(IMMND_CB *cb,
-                                    IMMND_EVT *evt,
-                                    SaBoolT originatedAtThisNd, SaImmHandleT 
clnt_hdl, MDS_DEST reply_dest);
+static void immnd_evt_proc_ccb_apply(IMMND_CB *cb, IMMND_EVT *evt, SaBoolT 
originatedAtThisNd,
+                                    SaImmHandleT clnt_hdl, MDS_DEST 
reply_dest, SaBoolT validateOnly);
 
 static void immnd_evt_proc_ccb_compl_rsp(IMMND_CB *cb,
                                         IMMND_EVT *evt,
@@ -3053,6 +3052,7 @@ static SaAisErrorT immnd_fevs_local_chec
                break;
 
        case IMMND_EVT_A2ND_CCB_APPLY:
+       case IMMND_EVT_A2ND_CCB_VALIDATE:
                if(immModel_pbeNotWritable(cb)) {
                        /* NO_RESOURCES is here imm internal proxy for 
TRY_AGAIN.
                           The library code for saImmOmCcbApply will translate 
NO_RESOURCES
@@ -3652,8 +3652,9 @@ static void immnd_evt_proc_ccb_compl_rsp
                        reqConn = 0; /* Ensure we dont reply to OM client yet. 
*/
                }
        } else {
-               TRACE("Finished waiting for completed Acks from implementers 
(and PBE)");
+               bool validateOnly = false;
                if (err == SA_AIS_OK) { /*Proceed with commit */
+                       TRACE("Finished waiting for completed Acks from 
implementers (and PBE)");
                        /*If we arrive here, the assumption is that all 
implementors have agreed
                           to commit and all immnds are prepared to commit this 
ccb. Fevs must
                           guarantee that all have seen the same replies from 
implementers. 
@@ -3786,10 +3787,18 @@ static void immnd_evt_proc_ccb_compl_rsp
                                free(applConnArr);
                                applConnArr = NULL;
                        }                       
-               } else {        /*err != SA_AIS_OK => generate 
SaImmOiCcbAbortCallbackT upcall
-                                  for all local implementers involved in the 
Ccb */
+               } else if (err == SA_AIS_ERR_INTERRUPT) {
+                       /* ERR_INTERRUPT => validateOnly. */
+                               TRACE("Explicit validation finished OK with 
completed Acks from OIs "
+                                       "(not includingPBE)");
+
+                               err = SA_AIS_OK;
+                               validateOnly = true;
+               } else {
                        errStrings = immModel_ccbGrabErrStrings(cb, 
evt->info.ccbUpcallRsp.ccbId);
                        TRACE("Abort in immnd_evt_proc_ccb_compl_rsp reqConn: 
%u", reqConn);
+                       /*err != SA_AIS_OK => generate SaImmOiCcbAbortCallbackT 
upcall
+                                  for all local implementers involved in the 
Ccb */
                        immnd_evt_ccb_abort(cb, evt->info.ccbUpcallRsp.ccbId, 
NULL);
                }
                /* Either commit or abort has been decided. Ccb is now done.
@@ -3799,8 +3808,8 @@ static void immnd_evt_proc_ccb_compl_rsp
                 */
 
                if (reqConn) {
+
                        SaImmHandleT tmp_hdl = m_IMMSV_PACK_HANDLE(reqConn, 
cb->node_id);
-
                        immnd_client_node_get(cb, tmp_hdl, &cl_node);
                        if (cl_node == NULL || cl_node->mIsStale) {
                                LOG_WA("IMMND - Client went down so no 
response");
@@ -3828,7 +3837,10 @@ static void immnd_evt_proc_ccb_compl_rsp
                        }
                        
immsv_evt_free_attrNames(send_evt.info.imma.info.errRsp.errStrings);
                }
+
        finalize_ccb:
+               if(validateOnly) { goto done;}
+
                TRACE_2("CCB COMPLETED: TERMINATING CCB:%u", 
evt->info.ccbUpcallRsp.ccbId);
                err = immModel_ccbFinalize(cb, evt->info.ccbUpcallRsp.ccbId);
                if (err != SA_AIS_OK) {
@@ -3836,6 +3848,7 @@ static void immnd_evt_proc_ccb_compl_rsp
                        /* There is really not much we can do here. */
                }
        }
+ done:
        TRACE_LEAVE();
 }
 
@@ -7066,12 +7079,12 @@ static void immnd_evt_proc_ccb_finalize(
  *                 IMM_DEST reply_dest - The dest of the ND to where reply
  *                                         is to be sent (only relevant if
  *                                         originatedAtThisNode is false).
+ *                 SaBoolT validateOnly - SA_TRUE => only do validation, not 
commit/apply.
  * Return Values : None
  *
  *****************************************************************************/
-static void immnd_evt_proc_ccb_apply(IMMND_CB *cb,
-                                    IMMND_EVT *evt,
-                                    SaBoolT originatedAtThisNd, SaImmHandleT 
clnt_hdl, MDS_DEST reply_dest)
+static void immnd_evt_proc_ccb_apply(IMMND_CB *cb, IMMND_EVT *evt, SaBoolT 
originatedAtThisNd,
+                                     SaImmHandleT clnt_hdl, MDS_DEST 
reply_dest, SaBoolT validateOnly)
 {
        SaAisErrorT err = SA_AIS_OK;
        IMMSV_EVT send_evt;
@@ -7118,11 +7131,11 @@ static void immnd_evt_proc_ccb_apply(IMM
        }
 
        err = immModel_ccbApply(cb, evt->info.ccbId, originatedAtThisNd ? conn 
: 0,
-                               &arrSize, &implConnArr, &implIdArr, &ctnArr);
+               &arrSize, &implConnArr, &implIdArr, &ctnArr, validateOnly);
 
        if (err == SA_AIS_OK) {
                if (arrSize) {
-                       TRACE_2("THERE ARE LOCAL IMPLEMENTERS in ccb:%u", 
evt->info.ccbId);
+                       TRACE_2("THERE ARE LOCAL IMPLEMENTERS in ccb:%u sending 
completed upcall", evt->info.ccbId);
                        delayedReply = SA_TRUE;
                        memset(&send_evt, '\0', sizeof(IMMSV_EVT));
                        send_evt.type = IMMSV_EVT_TYPE_IMMA;
@@ -7203,6 +7216,7 @@ static void immnd_evt_proc_ccb_apply(IMM
                        }
                } else {
                        TRACE_2("NO IMPLEMENTERS AT ALL AND NO PBE. for ccb:%u 
err:%u sz:%u", evt->info.ccbId, err, arrSize);
+                       TRACE("delayedReply:%u", delayedReply);
                }
        }
        /* err != SA_AIS_OK or no implementers => immediate reply. */
@@ -7222,6 +7236,35 @@ static void immnd_evt_proc_ccb_apply(IMM
        /*REPLY can not be sent immediately if there are implementers or PBE.
           Must wait for prepare votes. */
        if (!delayedReply) {
+               if (err == SA_AIS_ERR_INTERRUPT) {
+                       /* ERR_INTERUPT is an IMMND internal error code 
indicating
+                          that this was etiher a saImmOmCcbValidate that is now
+                          complete, i.e. we need to interrupt and return 
without
+                          continuing with the commit/apply part. Or it was an
+                          saImmOmCcbApply continuting such interrupted 
processing,
+                          i.e. we must here skip sending completed callbacks 
(validation)
+                          since this is done already for this ccb.             
           
+                        */
+                       err = SA_AIS_OK;
+                       if(validateOnly) {
+                               TRACE("Explicit validation finished OK");
+                               goto immediate_reply;
+                       } else {
+                               /* Fake continuation of end of validation. 
+                                  To avoid writing large ammounts of new code 
almost
+                                  identical to existing, we fake a reply from 
a non existent
+                                  OI on a final completed callback. This is 
what would trigger
+                                  the commit/apply of a normal CCB. 
+                                */
+                               memset(&send_evt, '\0', sizeof(IMMSV_EVT));
+                               send_evt.type = IMMSV_EVT_TYPE_IMMND;
+                               send_evt.info.immnd.type = 
IMMND_EVT_A2ND_CCB_COMPLETED_RSP;
+                               send_evt.info.immnd.info.ccbUpcallRsp.ccbId = 
evt->info.ccbId;
+                               immnd_evt_proc_ccb_compl_rsp(cb, 
&(send_evt.info.immnd), originatedAtThisNd, clnt_hdl, reply_dest);
+                               goto done;
+                       }
+               }
+
                if (err == SA_AIS_OK) {
                        /*No implementers anywhere and all is OK */
                        /* Check for appliers. */
@@ -7538,7 +7581,11 @@ immnd_evt_proc_fevs_dispatch(IMMND_CB *c
                break;
 
        case IMMND_EVT_A2ND_CCB_APPLY:
-               immnd_evt_proc_ccb_apply(cb, &frwrd_evt.info.immnd, 
originatedAtThisNd, clnt_hdl, reply_dest);
+               immnd_evt_proc_ccb_apply(cb, &frwrd_evt.info.immnd, 
originatedAtThisNd, clnt_hdl, reply_dest, SA_FALSE);
+               break;
+
+       case IMMND_EVT_A2ND_CCB_VALIDATE:
+               immnd_evt_proc_ccb_apply(cb, &frwrd_evt.info.immnd, 
originatedAtThisNd, clnt_hdl, reply_dest, SA_TRUE);
                break;
 
        case IMMND_EVT_A2ND_CCB_FINALIZE:
diff --git a/osaf/services/saf/immsv/immnd/immnd_init.h 
b/osaf/services/saf/immsv/immnd/immnd_init.h
--- a/osaf/services/saf/immsv/immnd/immnd_init.h
+++ b/osaf/services/saf/immsv/immnd/immnd_init.h
@@ -140,8 +140,8 @@ extern "C" {
        SaAisErrorT
            immModel_ccbApply(IMMND_CB *cb,
                              SaUint32T ccbId,
-                             SaUint32T replyConn,
-                             SaUint32T *arrSize, SaUint32T **implConnArr, 
SaUint32T **implIdArr, SaUint32T **ctnArr);
+                             SaUint32T replyConn, SaUint32T *arrSize, 
SaUint32T **implConnArr,
+                   SaUint32T **implIdArr, SaUint32T **ctnArr, bool 
validateOnly);
 
        bool immModel_ccbAbort(IMMND_CB *cb,
                SaUint32T ccbId, SaUint32T *arrSize, SaUint32T **implConnArr, 
SaUint32T *client,
diff --git a/tests/immsv/implementer/test_SaImmOiCcb.c 
b/tests/immsv/implementer/test_SaImmOiCcb.c
--- a/tests/immsv/implementer/test_SaImmOiCcb.c
+++ b/tests/immsv/implementer/test_SaImmOiCcb.c
@@ -260,7 +260,7 @@ static void *classImplementerThreadMain(
     return NULL;
 }
 
-static SaAisErrorT om_ccb_exec(void)
+static SaAisErrorT om_ccb_exec(unsigned int control)
 {
     SaAisErrorT rc;
     SaImmHandleT handle;
@@ -293,7 +293,29 @@ static SaAisErrorT om_ccb_exec(void)
     if ((rc = saImmOmCcbObjectCreate_2(ccbHandle, configClassName, &rootObj, 
attrValues)) != SA_AIS_OK)
         goto done;
 
-    safassert(saImmOmCcbApply(ccbHandle), SA_AIS_OK);
+    switch(control) {
+
+           case 0:
+                   safassert(saImmOmCcbApply(ccbHandle), SA_AIS_OK);
+                   break;
+
+           case 1: safassert(saImmOmCcbValidate(ccbHandle), SA_AIS_OK);
+                   safassert(saImmOmCcbApply(ccbHandle), SA_AIS_OK);
+                   break;
+
+           case 2: safassert(saImmOmCcbValidate(ccbHandle), SA_AIS_OK);
+                   safassert(saImmOmCcbAbort(ccbHandle), SA_AIS_OK);
+                   break;
+
+           case 3: safassert(saImmOmCcbValidate(ccbHandle), 
SA_AIS_ERR_FAILED_OPERATION);
+                   safassert(saImmOmCcbAbort(ccbHandle), SA_AIS_OK);
+                   break;
+
+
+
+           default:
+                   TRACE("Incorrect control parameter:%u to om_ccb_exec", 
control);
+    }
 
 done:
     if(rc != SA_AIS_OK) {
@@ -392,7 +414,7 @@ static void saImmOiCcb_01(void)
     assert(res == 0);
 
     sleep(1); /* Race condition, allow implementer threads to set up !*/
-    rc = om_ccb_exec();
+    rc = om_ccb_exec(0);
 
     pthread_join(thread[0], NULL);
     pthread_join(thread[1], NULL);
@@ -419,7 +441,7 @@ static void saImmOiCcb_02(void)
 
     saImmOiCcbObjectDeleteCallback_response = SA_AIS_ERR_BAD_OPERATION;
     sleep(1); /* Race condition, allow implementer threads to set up!*/
-    rc = om_ccb_exec();
+    rc = om_ccb_exec(0);
 
     pthread_join(thread[0], NULL);
     pthread_join(thread[1], NULL);
@@ -453,7 +475,7 @@ static void saImmOiCcb_03(void)
     assert(res == 0);
     
     sleep(1); /* Race condition, allow implementer threads to set up!*/
-    rc = om_ccb_exec();
+    rc = om_ccb_exec(0);
 
     pthread_join(thread[0], NULL);
  
@@ -477,7 +499,7 @@ static void saImmOiCcb_04(void)
  
     saImmOiCcbObjectModifyCallback_response = SA_AIS_ERR_BAD_OPERATION;
     sleep(1); /* Race condition, allow implementer threads to set up!*/
-    rc = om_ccb_exec();
+    rc = om_ccb_exec(0);
 
     pthread_join(threadid, NULL);
  
@@ -516,7 +538,7 @@ static void saImmOiCcb_05(void)
     /* Set saveErrorStrings to 1 and save error strings in om_ccb_exec() */
     returnErrorStrings = NULL;
     saveErrorStrings = 1;
-    rc = om_ccb_exec();
+    rc = om_ccb_exec(0);
     saveErrorStrings = 0;
 
     /* There is at least one error string */
@@ -549,6 +571,87 @@ static void saImmOiCcb_05(void)
     TRACE_LEAVE();
 }
 
+static void saImmOiCcb_06(void)
+{
+    int res;
+    pthread_t thread[2];
+
+    TRACE_ENTER();
+    om_setup();
+
+    /* Create implementer threads */
+    res = pthread_create(&thread[0], NULL, objectImplementerThreadMain, 
&dnObj1);
+    assert(res == 0);
+    res = pthread_create(&thread[1], NULL, objectImplementerThreadMain, 
&dnObj2);
+    assert(res == 0);
+
+    sleep(1); /* Race condition, allow implementer threads to set up !*/
+    rc = om_ccb_exec(1);
+
+    pthread_join(thread[0], NULL);
+    pthread_join(thread[1], NULL);
+
+    test_validate(rc, SA_AIS_OK);
+
+    om_teardown();
+    TRACE_LEAVE();
+}
+
+static void saImmOiCcb_07(void)
+{
+    int res;
+    pthread_t thread[2];
+
+    TRACE_ENTER();
+    om_setup();
+
+    /* Create implementer threads */
+    res = pthread_create(&thread[0], NULL, objectImplementerThreadMain, 
&dnObj1);
+    assert(res == 0);
+    res = pthread_create(&thread[1], NULL, objectImplementerThreadMain, 
&dnObj2);
+    assert(res == 0);
+
+    sleep(1); /* Race condition, allow implementer threads to set up !*/
+    rc = om_ccb_exec(2);
+
+    pthread_join(thread[0], NULL);
+    pthread_join(thread[1], NULL);
+
+    test_validate(rc, SA_AIS_OK);
+
+    om_teardown();
+    TRACE_LEAVE();
+}
+
+static void saImmOiCcb_08(void)
+{
+    int res;
+    pthread_t thread[2];
+
+    TRACE_ENTER();
+    om_setup();
+
+    /* Create implementer threads */
+    res = pthread_create(&thread[0], NULL, objectImplementerThreadMain, 
&dnObj1);
+    assert(res == 0);
+    res = pthread_create(&thread[1], NULL, objectImplementerThreadMain, 
&dnObj2);
+    assert(res == 0);
+
+    saImmOiCcbObjectDeleteCallback_response = SA_AIS_ERR_BAD_OPERATION;
+    sleep(1); /* Race condition, allow implementer threads to set up!*/
+    rc = om_ccb_exec(3);
+
+    pthread_join(thread[0], NULL);
+    pthread_join(thread[1], NULL);
+
+    test_validate(rc, SA_AIS_ERR_FAILED_OPERATION);
+
+    om_teardown();
+    saImmOiCcbObjectDeleteCallback_response = SA_AIS_OK;
+    TRACE_LEAVE();
+}
+
+
 __attribute__ ((constructor)) static void saImmOiCcb_constructor(void)
 {
     dnObj1.length = (SaUint16T) sprintf((char*) dnObj1.value, "%s,%s", 
rdnObj1.value, rootObj.value);
@@ -561,5 +664,8 @@ static void saImmOiCcb_05(void)
     test_case_add(4, saImmOiCcb_03, "saImmOiCcb - SA_AIS_OK - 1 
classImplementer thread");
     test_case_add(4, saImmOiCcb_04, "saImmOiCcb - 
SA_AIS_ERR_BAD_OPERATION/FAILED_OPERATION - 1 classImplementer thread");
     test_case_add(4, saImmOiCcb_05, "saImmOiCcb - 
SA_AIS_ERR_BAD_OPERATION/FAILED_OPERATION - saImmOiCcbSetErrorString and 
saImmOmCcbGetErrorStrings");
+    test_case_add(4, saImmOiCcb_06, "saImmOiCcb - SA_AIS_OK - 
saImmOmCcbValidate followed by saImmOmCcbApply");
+    test_case_add(4, saImmOiCcb_07, "saImmOiCcb - SA_AIS_OK - 
saImmOmCcbValidate followed by saImmOmCcbAbort");
+    test_case_add(4, saImmOiCcb_08, "saImmOiCcb - SA_AIS_ERR_FAILED_OPERATION 
- saImmOmCcbValidate (OI reports error) followed by saImmOmCcbAbort");
 }
 
diff --git a/tests/immsv/implementer/test_saImmOiAugmentCcbInitialize.c 
b/tests/immsv/implementer/test_saImmOiAugmentCcbInitialize.c
--- a/tests/immsv/implementer/test_saImmOiAugmentCcbInitialize.c
+++ b/tests/immsv/implementer/test_saImmOiAugmentCcbInitialize.c
@@ -30,6 +30,8 @@ typedef struct ImmThreadArg {
 static int objectDispatchThreadIsSet = 0;
 static int classDispatchThreadIsSet = 0;
 static int useAdminOwner = 0;
+static int testValidate = 0;
+static SaAisErrorT globalRc = SA_AIS_OK;
 
 static const SaNameT rdnObj1 = {sizeof("Obj1"), "Obj1"};
 static const SaNameT rdnObj2 = {sizeof("Obj2"), "Obj2"};
@@ -229,9 +231,14 @@ static SaAisErrorT saImmOiAugCcbObjectMo
     callbackCounter++;
     safassert(saImmOiAugmentCcbInitialize(immOiHandle, ccbId, &ccbHandle, 
&ownerHandle), SA_AIS_OK);
     if(useAdminOwner)
-       safassert(saImmOmAdminOwnerSet(ownerHandle, objectNames, SA_IMM_ONE), 
SA_AIS_OK);
-    if((rc = saImmOmCcbObjectModify_2(ccbHandle, &rdnObj2, attrMods)) == 
SA_AIS_OK)
-       rc = saImmOmCcbApply(ccbHandle);
+       safassert(saImmOmAdminOwnerSet(ownerHandle, objectNames, SA_IMM_ONE), 
SA_AIS_OK); 
+    if((rc = saImmOmCcbObjectModify_2(ccbHandle, &rdnObj2, attrMods)) == 
SA_AIS_OK) {
+           if(testValidate) {
+                   globalRc = saImmOmCcbValidate(ccbHandle);
+           } //else {
+                   rc = saImmOmCcbApply(ccbHandle);
+           //}
+    }
     TRACE_LEAVE2();
     return rc;
 }
@@ -523,7 +530,9 @@ static void saImmOiCcbAugmentInitialize_
 done:
        pthread_join(threadid, NULL);
 
-       test_validate(rc, SA_AIS_OK);
+       if(!testValidate) {
+               test_validate(rc, SA_AIS_OK);
+       }
 
     safassert(saImmOmCcbFinalize(ccbHandle), SA_AIS_OK);
     safassert(saImmOmAdminOwnerFinalize(ownerHandle), SA_AIS_OK);
@@ -711,6 +720,26 @@ done:
     TRACE_LEAVE();
 }
 
+static void saImmOiCcbAugmentInitialize_05(void)
+{
+       /*
+    SaImmHandleT handle;
+    ImmThreadArg arg;
+    const SaImmAdminOwnerNameT adminOwnerName = (SaImmAdminOwnerNameT) 
__FILE__;
+    SaImmAdminOwnerHandleT ownerHandle;
+    const SaNameT *objectNames[] = { &rdnObj1, &rdnObj2, NULL };
+    SaImmCcbHandleT ccbHandle;
+    SaAisErrorT rc;
+    pthread_t threadid;
+       */
+    TRACE_ENTER();
+
+    testValidate = 1;
+    saImmOiCcbAugmentInitialize_02();
+    test_validate(globalRc, SA_AIS_ERR_BAD_OPERATION);
+
+    TRACE_LEAVE();
+}
 
 __attribute__ ((constructor)) static void 
saImmOiCcbAugmentInitialize_constructor(void)
 {
@@ -719,5 +748,6 @@ done:
     test_case_add(6, saImmOiCcbAugmentInitialize_02, 
"saImmOiCcbAugmentInitialize - SA_AIS_OK - one object implementer: modify, 
delete");
     test_case_add(6, saImmOiCcbAugmentInitialize_03, 
"saImmOiCcbAugmentInitialize - SA_AIS_OK - two object implementers: modify, 
delete");
     test_case_add(6, saImmOiCcbAugmentInitialize_04, 
"saImmOiCcbAugmentInitialize - SA_AIS_OK - two object implementers: modify and 
delete with saImmOmAdminOwnerSet");
+    test_case_add(6, saImmOiCcbAugmentInitialize_05, 
"saImmOiCcbAugmentInitialize - SA_AIS_ERR_BAD_OPERATION - saImmOmValidate not 
allowed in augmentation");
 }
 
diff --git a/tests/immsv/management/test_saImmOmCcbApply.c 
b/tests/immsv/management/test_saImmOmCcbApply.c
--- a/tests/immsv/management/test_saImmOmCcbApply.c
+++ b/tests/immsv/management/test_saImmOmCcbApply.c
@@ -16,6 +16,7 @@
  */
 
 #include "immtest.h"
+#include <unistd.h>
 
 void saImmOmCcbApply_01(void)
 {
@@ -189,3 +190,91 @@ done:
     TRACE_LEAVE();
 }
 
+void saImmOmCcbValidate_01(void)
+{
+    TRACE_ENTER();
+    const SaImmAdminOwnerNameT adminOwnerName = (SaImmAdminOwnerNameT) 
__FUNCTION__;
+    SaImmAdminOwnerHandleT ownerHandle;
+    SaImmCcbHandleT ccbHandle;
+    SaNameT rdn = {strlen("Obj1"), "Obj1"};
+    SaNameT* nameValues[] = {&rdn};
+    SaImmAttrValuesT_2 v2 = {"rdn",  SA_IMM_ATTR_SANAMET, 1, 
(void**)nameValues};
+    SaUint32T  int1Value1 = 7;
+    SaUint32T* int1Values[] = {&int1Value1};
+    SaImmAttrValuesT_2 v1 = {"attr1", SA_IMM_ATTR_SAUINT32T, 1, 
(void**)int1Values};
+    const SaImmAttrValuesT_2 * attrValues[] = {&v1, &v2, NULL};
+    const SaNameT *objectNames[] = {&rootObj, NULL};
+
+    safassert(saImmOmInitialize(&immOmHandle, &immOmCallbacks, &immVersion), 
SA_AIS_OK);
+    safassert(saImmOmAdminOwnerInitialize(immOmHandle, adminOwnerName,
+        SA_TRUE, &ownerHandle), SA_AIS_OK);
+    safassert(saImmOmAdminOwnerSet(ownerHandle, objectNames, SA_IMM_ONE), 
SA_AIS_OK);
+    safassert(saImmOmCcbInitialize(ownerHandle, 0, &ccbHandle), SA_AIS_OK);
+    safassert(saImmOmCcbObjectCreate_2(ccbHandle, configClassName,
+        &rootObj, attrValues), SA_AIS_OK);
+
+    /* validate the ccb */
+    if ((rc = saImmOmCcbValidate(ccbHandle)) != SA_AIS_OK)
+           goto done;
+
+    if ((rc = saImmOmCcbAbort(ccbHandle)) != SA_AIS_OK) /* Abort after 
valiadtion should be ok */
+           goto done;
+
+    if ((rc = saImmOmCcbApply(ccbHandle)) != SA_AIS_OK) /* Aply after abort 
(empty ccb) should be ok */
+           goto done;
+
+    safassert(saImmOmCcbFinalize(ccbHandle), SA_AIS_OK);
+
+done:
+    test_validate(rc, SA_AIS_OK);
+    safassert(saImmOmAdminOwnerFinalize(ownerHandle), SA_AIS_OK);
+    safassert(saImmOmFinalize(immOmHandle), SA_AIS_OK);
+    TRACE_LEAVE();
+}
+
+void saImmOmCcbValidate_02(void)
+{
+    TRACE_ENTER();
+    const SaImmAdminOwnerNameT adminOwnerName = (SaImmAdminOwnerNameT) 
__FUNCTION__;
+    SaImmAdminOwnerHandleT ownerHandle;
+    SaImmCcbHandleT ccbHandle;
+    SaNameT rdn = {strlen("Obj1"), "Obj1"};
+    SaNameT* nameValues[] = {&rdn};
+    SaImmAttrValuesT_2 v2 = {"rdn",  SA_IMM_ATTR_SANAMET, 1, 
(void**)nameValues};
+    SaUint32T  int1Value1 = 7;
+    SaUint32T* int1Values[] = {&int1Value1};
+    SaImmAttrValuesT_2 v1 = {"attr1", SA_IMM_ATTR_SAUINT32T, 1, 
(void**)int1Values};
+    const SaImmAttrValuesT_2 * attrValues[] = {&v1, &v2, NULL};
+    const SaNameT *objectNames[] = {&rootObj, NULL};
+    const SaNameT objectName =
+        {strlen("Obj1,rdn=root"), "Obj1,rdn=root"};
+
+    safassert(saImmOmInitialize(&immOmHandle, &immOmCallbacks, &immVersion), 
SA_AIS_OK);
+    safassert(saImmOmAdminOwnerInitialize(immOmHandle, adminOwnerName,
+        SA_TRUE, &ownerHandle), SA_AIS_OK);
+    safassert(saImmOmAdminOwnerSet(ownerHandle, objectNames, SA_IMM_ONE), 
SA_AIS_OK);
+    safassert(saImmOmCcbInitialize(ownerHandle, 0, &ccbHandle), SA_AIS_OK);
+    safassert(saImmOmCcbObjectCreate_2(ccbHandle, configClassName,
+        &rootObj, attrValues), SA_AIS_OK);
+
+    /* validate the ccb */
+    if ((rc = saImmOmCcbValidate(ccbHandle)) != SA_AIS_OK)
+           goto done;
+
+    if ((rc = saImmOmCcbApply(ccbHandle)) != SA_AIS_OK) /* Aply after validate 
(normal case) should be ok */
+           goto done;
+
+    if ((rc = saImmOmCcbAbort(ccbHandle)) != SA_AIS_OK) /* Abort after apply 
(empty ccb) should be ok */
+           goto done;
+
+    safassert(saImmOmCcbObjectDelete(ccbHandle, &objectName), SA_AIS_OK);
+    safassert(saImmOmCcbApply(ccbHandle), SA_AIS_OK); 
+
+    safassert(saImmOmCcbFinalize(ccbHandle), SA_AIS_OK);
+
+done:
+    test_validate(rc, SA_AIS_OK);
+    safassert(saImmOmAdminOwnerFinalize(ownerHandle), SA_AIS_OK);
+    safassert(saImmOmFinalize(immOmHandle), SA_AIS_OK);
+    TRACE_LEAVE();
+}
diff --git a/tests/immsv/management/test_saImmOmCcbInitialize.c 
b/tests/immsv/management/test_saImmOmCcbInitialize.c
--- a/tests/immsv/management/test_saImmOmCcbInitialize.c
+++ b/tests/immsv/management/test_saImmOmCcbInitialize.c
@@ -166,6 +166,8 @@ extern void saImmOmCcbFinalize_01(void);
 extern void saImmOmCcbFinalize_02(void);
 extern void saImmOmCcbAbort_01(void);
 extern void saImmOmCcbAbort_02(void);
+extern void saImmOmCcbValidate_01(void);
+extern void saImmOmCcbValidate_02(void);
 
 __attribute__ ((constructor)) static void saImmOmInitialize_constructor(void)
 {
@@ -239,5 +241,7 @@ extern void saImmOmCcbAbort_02(void);
     test_case_add(6, saImmOmCcbObjectModify_2_07, "saImmOmCcbObjectModify_2 
SA_IMM_ATTR_DELETE multi/multi - SA_AIS_OK");
     test_case_add(6, saImmOmCcbAbort_01, "saImmOmCcbAbort - SA_AIS_OK");
     test_case_add(6, saImmOmCcbAbort_02, "saImmOmCcbAbort - SA_AIS_OK on 
continued ccb handle usage.");
+    test_case_add(6, saImmOmCcbValidate_01, "saImmOmCcbValidate followed by 
apply - SA_AIS_OK");
+    test_case_add(6, saImmOmCcbValidate_02, "saImmOmCcbValidate followed by 
abort - SA_AIS_OK");
 }
 
diff --git a/tests/unit_test_fw/src/utest.c b/tests/unit_test_fw/src/utest.c
--- a/tests/unit_test_fw/src/utest.c
+++ b/tests/unit_test_fw/src/utest.c
@@ -37,7 +37,7 @@ static unsigned int test_passed;
 static unsigned int test_failed;
 
 #define NO_SUITES 64
-#define NR_TESTS 64
+#define NR_TESTS 128
 static struct test testlist[NO_SUITES][NR_TESTS];
 static const char *suite_name[NO_SUITES];
 static int last_test_status;

------------------------------------------------------------------------------
Put Bad Developers to Shame
Dominate Development with Jenkins Continuous Integration
Continuously Automate Build, Test & Deployment 
Start a new project now. Try Jenkins in the cloud.
http://p.sf.net/sfu/13600_Cloudbees
_______________________________________________
Opensaf-devel mailing list
Opensaf-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/opensaf-devel

Reply via email to