} END_TEST_EXTERN


START_TEST_EXTERN(eject_with_proper_values)
{
    ClRcT       rc = CL_OK;

    /* Initialize gms client without any callbacks being registered. */
    rc = clGmsInitialize(&handle,NULL,&correct_version);
    fail_unless(rc == CL_OK,
            "clGmsInitialize with NULL callbacks failed with rc 0x%x",rc);

    rc = clGmsClusterJoin(handle, &clusterManageCallbacks, 0, 0, 100, &nodeName);
    fail_unless(CL_GET_ERROR_CODE(rc) == CL_OK,
            "ClusterJoin with 0 handle failed with rc = 0x%x",rc);

    callback_invoked = CL_FALSE;

    rc = clGmsClusterMemberEject(handle, 100, CL_GMS_MEMBER_EJECT_REASON_API_REQUEST);
    fail_unless(rc == CL_OK,
            "Cluster Leave for nodeId 100 failed with rc 0x%x",rc);

    /* Wait for the callback to be invoked */
    sleep(5);
    fail_unless(callback_invoked == CL_TRUE,
            "Cluster Eject callback is not invoked");
    
    /* Finalize */
    rc = clGmsFinalize(handle);
    fail_unless(rc == CL_OK,
            "clGmsFinalize failed with rc = 0x%x",rc);
 
} END_TEST_EXTERN
} END_TEST_EXTERN



START_TEST_EXTERN(eject_with_non_existing_node_id)
{
    ClRcT       rc = CL_OK;

    /* Initialize gms client without any callbacks being registered. */
    rc = clGmsInitialize(&handle,NULL,&correct_version);
    fail_unless(rc == CL_OK,
            "clGmsInitialize with NULL callbacks failed with rc 0x%x",rc);

    rc = clGmsClusterMemberEject(handle, 100, CL_GMS_MEMBER_EJECT_REASON_API_REQUEST);
    fail_unless(CL_GET_ERROR_CODE(rc) == CL_ERR_INVALID_PARAMETER,
            "ClusterJoin with NULL clusterManage callback failed with rc = 0x%x",rc);

    /* Finalize */
    rc = clGmsFinalize(handle);
    fail_unless(rc == CL_OK,
            "clGmsFinalize failed with rc = 0x%x",rc);

} END_TEST_EXTERN
ClRcT clCkptMasterAddressesSet()
{
    ClRcT rc = CL_OK;
#ifdef CL_CKPT_GMS 
    /*
     * GMS available.
     */
     
    /* 
     * Contact GMS to get master and backup master addresses.
     */
    ClGmsClusterNotificationBufferT notBuffer;
    memset((void*)&notBuffer , 0, sizeof(notBuffer));
    memset( &notBuffer , '\0' ,sizeof(ClGmsClusterNotificationBufferT));
    
    /*
     * Initialize the gms client library.
     */
    rc = clGmsInitialize(&gCkptSvr->gmsHdl, &ckptGmsCallbacks,
                        &gVersion);
    CKPT_ERR_CHECK(CL_CKPT_SVR,CL_LOG_SEV_ERROR,
    (" clCkptMasterAddressesSet failed rc[0x %x]\n",rc),
        rc);
        
    /* 
     * Register for current track and track changes.
     */
    rc = clGmsClusterTrack(gCkptSvr->gmsHdl, 
                           CL_GMS_TRACK_CHANGES|CL_GMS_TRACK_CURRENT, 
                           &notBuffer);
                           
    /*
     * Call the track change callback function. This is needed for getting
     * current track information.
     */
    if(rc == CL_OK && notBuffer.leader &&
       notBuffer.leader != CL_GMS_INVALID_NODE_ID)
    {
        _clCkptAddressesUpdate(&notBuffer);
    }

    if (notBuffer.notification != NULL)
      clHeapFree(notBuffer.notification);

#else
    /*
     * GMS unavailable.
     */

     /*
      * Select cpm master as ckpt master. Deputy will be unspecified.
      */
    clCpmMasterAddressGet(&gCkptSvr->masterInfo.masterAddr);
    gCkptSvr->masterInfo.deputyAddr = -1;

    /*
     * Update the TL with ckpt master address.
     */
    if(gCkptSvr->masterInfo.masterAddr == gCkptSvr->localAddr )
    {
        ClIocTLInfoT tlInfo = {0};
        ClUint32T    compId = 0; 
        SaNameT      name   = {0};
 
        clCpmComponentNameGet(gCkptSvr->amfHdl, &name);
        clCpmComponentIdGet(gCkptSvr->amfHdl, &name, &compId);
        tlInfo.compId                   = compId;
        gCkptSvr->masterInfo.compId     = compId;
        ckptOwnLogicalAddressGet(&tlInfo.logicalAddr);
        tlInfo.contextType              = CL_IOC_TL_GLOBAL_SCOPE;
        tlInfo.physicalAddr.nodeAddress = clIocLocalAddressGet();
        tlInfo.physicalAddr.portId      = CL_IOC_CKPT_PORT;
        tlInfo.haState                  = CL_IOC_TL_ACTIVE;
        rc = clIocTransparencyRegister(&tlInfo);
    }
#endif
#ifdef CL_CKPT_GMS 
exitOnError:
#endif
    {
        return rc;
    }
}
SaAisErrorT
saClmInitialize (
        SaClmHandleT*const clmHandle,
        const SaClmCallbacksT*const clmCallbacks,
        SaVersionT*const version
        )
{
    ClRcT rc = CL_OK;
    ClGmsCallbacksT gmsCallbacks = {0};
    ClGmsHandleT gmsHandle = CL_HANDLE_INVALID_VALUE;
    /* 
     * Use a 32 bit handle type to create a handle. Dont
     * use the 64 bit clmHandle directly as it would create issues
     * in mixed mode operations.
     */
    ClHandleT   localHandle = CL_HANDLE_INVALID_VALUE;
    SaClmInstanceT      *clmInstance = NULL;
    ClHandleT   dispatchHandle = CL_HANDLE_INVALID_VALUE;

    if (clmHandle == (const void*)NULL)
    {
        return _aspErrToAisErr(CL_ERR_NULL_POINTER);
    }

    rc = clASPInitialize();
    if(CL_OK != rc)
    {
        clLogCritical("CLM", "INI",
                      "ASP initialize failed, rc[0x%X]", rc);
        return SA_AIS_ERR_LIBRARY;
    }
    
    /* 
     * Create the handle database for clm first, if it is 
     * not already created
     */
    saClmLibInitialize();

    if (clmCallbacks != (const void*)NULL)
    {
        /*Set GMS callbacks to callback wrappers*/
        if (clmCallbacks->saClmClusterNodeGetCallback != NULL)
        {
            gmsCallbacks.clGmsClusterMemberGetCallback =
                clGmsClusterMemberGetCallbackWrapper;
        }

        if (clmCallbacks->saClmClusterTrackCallback != NULL)
        {
            gmsCallbacks.clGmsClusterTrackCallback =
                clGmsClusterTrackCallbackWrapper;
    }
    }

    /* Initialize GMS */
    rc = clGmsInitialize(&gmsHandle,
            &gmsCallbacks, 
                         (ClVersionT*)version);
    if(rc != CL_OK)
    {
        clLogError("CLM","INI",
                   "clGmsInitialize failed with rc 0x%x\n",rc);
        return _aspErrToAisErr(rc);
    }

    localHandle = gmsHandle;
    /*
     * Create a local handle to be returned as clmHandle
     * Here we will create the handle value same as that of
     * gmsHandle so that we can keep the reference during
     * callback invocation through thread specific data
     */
    rc = clHandleCreateSpecifiedHandle(databaseHandle,
                                       sizeof(SaClmInstanceT),
                                       localHandle);
    if (rc != CL_OK)
    {
        clLogError("CLM","INI",
                   "clHandleCreateSpecifiedHandle failed with rc 0x%x\n",rc);
        return _aspErrToAisErr(rc);
    }

    /*
     * Assign localHandle to clmHandle, but keep using localHandle,
     * as it is compliant with ASP APIs.
     */
    *clmHandle = localHandle;
    SA_GMS_INIT_COUNT_INC();

    /* Checkout the handle */
    rc = clHandleCheckout(databaseHandle, localHandle, (void**)&clmInstance);
    CL_ASSERT((rc == CL_OK) && (clmInstance != NULL));


    if (clmCallbacks != (const void*)NULL)
    {
        /*Save the saf callbacks in the handle*/
        memcpy(&clmInstance->callbacks, clmCallbacks, sizeof(SaClmCallbacksT));
    }

    /* Initialize dispatch */
    rc = clDispatchRegister(&dispatchHandle,
                            localHandle,
                            dispatchWrapperCallback,
                            dispatchQDestroyCallback);
    if (rc != CL_OK)
    {
        clLogError("CLM","INI",
                   "clDispatchRegister failed with rc 0x%x\n",rc);
        goto error_return;
    }
    /* Store dispatchHandle in the clmHandle */
    clmInstance->dispatchHandle = dispatchHandle;

error_return:
    if ((clHandleCheckin(databaseHandle, localHandle)) != CL_OK)
    {
        clLogError(CL_LOG_AREA_UNSPECIFIED,CL_LOG_CONTEXT_UNSPECIFIED,
                   "clHandleCheckin failed");
}

    return _aspErrToAisErr(rc);
}
ClRcT cpmGmsInitialize(void)
{
    ClRcT rc = CL_OK;
    ClTimerTimeOutT timeOut = {0, 0};

    ClGmsCallbacksT cpmGmsCallbacks = { NULL, cpmClusterTrackCallBack, NULL, NULL };
    
    gpClCpm->version.releaseCode = 'B';
    gpClCpm->version.majorVersion = 0x01;
    gpClCpm->version.minorVersion = 0x01;
    rc = clGmsInitialize(&gpClCpm->cpmGmsHdl,
                         &cpmGmsCallbacks,
                         &gpClCpm->version);
    if (CL_OK != rc)
    {
        clLogError(CPM_LOG_AREA_CPM, CPM_LOG_CTX_CPM_GMS,
                   "Failed to do GMS initialization, error [%#x]",
                   rc);
        gpClCpm->cpmGmsHdl = CL_HANDLE_INVALID_VALUE;
        goto failure;
    }

    rc = clOsalMutexLock(&gpClCpm->cpmGmsMutex);
    CL_CPM_CHECK_1(CL_LOG_SEV_ERROR, CL_CPM_LOG_1_OSAL_MUTEX_LOCK_ERR, rc, rc,
                   CL_LOG_HANDLE_APP);

    rc = clGmsClusterTrack(gpClCpm->cpmGmsHdl, CL_GMS_TRACK_CHANGES_ONLY | CL_GMS_TRACK_CURRENT, NULL);
    if (CL_OK != rc)
    {
        clOsalMutexUnlock(&gpClCpm->cpmGmsMutex);
        clLogError(CPM_LOG_AREA_CPM, CPM_LOG_CTX_CPM_GMS,
                   "The GMS cluster track function failed, error [%#x]",
                   rc);
        goto failure;
    }

    /*
     * Wait for the GMS callback
     */
    retry:
#ifdef VXWORKS_BUILD
    timeOut.tsSec = gpClCpm->cpmGmsTimeout + 20;
#else
    timeOut.tsSec = gpClCpm->cpmGmsTimeout + 60;  /* There is no reason to not wait for a long time.  100% cpu could cause GMS to come up slowly */
#endif
    timeOut.tsMilliSec = 0;

    clLogInfo(CPM_LOG_AREA_CPM, CPM_LOG_CTX_CPM_GMS,
              "Node [%s] waiting for GMS cluster track callback for [%d.%d] secs",
              gpClCpm->pCpmLocalInfo->nodeName, timeOut.tsSec, timeOut.tsMilliSec);

    rc = clOsalCondWait(&gpClCpm->cpmGmsCondVar,
                        &gpClCpm->cpmGmsMutex,
                        timeOut);
    if (CL_OK != rc)
    {
        if(gpClCpm->trackCallbackInProgress)
        {
            clLogWarning(CPM_LOG_AREA_CPM, CPM_LOG_CTX_CPM_GMS,
                         "GMS cluster track callback in progress. Waiting for it to complete ...");
            goto retry;
        }
        clLogError(CPM_LOG_AREA_CPM, CPM_LOG_CTX_CPM_GMS,
                   "Failed to receive GMS cluster track callback, "
                   "error [%#x].",
                   rc);
        clOsalMutexUnlock(&gpClCpm->cpmGmsMutex);
        goto failure;
    }

    rc = clOsalMutexUnlock(&gpClCpm->cpmGmsMutex);
    CL_CPM_CHECK_1(CL_LOG_SEV_ERROR, CL_CPM_LOG_1_OSAL_MUTEX_UNLOCK_ERR, rc,
                   rc, CL_LOG_HANDLE_APP);

    return CL_OK;

    failure:
    return rc;
}