ClRcT
clAmsPeSIIsActiveAssignableCustom(CL_IN ClAmsSIT *si)
{
    ClAmsAdminStateT adminState;
    ClAmsSGT *sg;

    AMS_CHECK_SI (si);
    AMS_CHECK_SG (sg = (ClAmsSGT *) si->config.parentSG.ptr);
    
    AMS_FUNC_ENTER ( ("SI [%s]\n", si->config.entity.name.value) );

    AMS_CALL ( clAmsPeSIComputeAdminState(si, &adminState) );

    if ( adminState == CL_AMS_ADMIN_STATE_UNLOCKED )
    {
        if ( si->status.numActiveAssignments < sg->config.numPrefActiveSUsPerSI )
        {
            return clAmsEntityListWalkGetEntity(
                                                &si->config.siDependenciesList,
                                                (ClAmsEntityCallbackT)clAmsPeSIIsActiveAssignable2);
        }
    }

    return CL_AMS_RC(CL_AMS_ERR_SI_NOT_ASSIGNABLE);
}
ClRcT
clAmsPeSGFindSIForStandbyAssignmentCustom(
                                          CL_IN ClAmsSGT *sg,
                                          CL_OUT ClAmsSIT **targetSI,
                                          CL_OUT ClAmsSUT **targetSU,
                                          CL_IN ClAmsSIT **scannedSIList,
                                          CL_IN ClUint32T numScannedSIs)
{
    ClAmsEntityRefT *entityRef = NULL;

    AMS_CHECK_SG ( sg );
    AMS_CHECKPTR ( !targetSI );

    AMS_FUNC_ENTER ( ("SG [%s]\n",sg->config.entity.name.value) );

    *targetSI = NULL;
    if(targetSU)
        *targetSU = NULL;

    for(entityRef = clAmsEntityListGetFirst(&sg->config.siList);
        entityRef != NULL;
        entityRef = clAmsEntityListGetNext(&sg->config.siList, entityRef))
    {
        ClAmsSIT *si = (ClAmsSIT*)entityRef->ptr;
        ClAmsSISURefT *siSURef = NULL;
        ClAmsCustomAssignmentIterT iter = {0};
        if(clAmsPeSIIsStandbyAssignable(si) != CL_OK)
            continue;
        clAmsCustomAssignmentIterInit(&iter, si);
        while ( (siSURef = clAmsCustomAssignmentIterNext(&iter) ) )
        {
            ClAmsSUT *su ;
            /* These lines cause the assignments to be taken literally;
               an entity configured as active will not be assigned standby, for example.
               They are commented out to implement more of a DWIM-style custom
               redundancy mode.
            if(siSURef->haState != CL_AMS_HA_STATE_STANDBY)
                continue;
            */
            su = (ClAmsSUT*)siSURef->entityRef.ptr;
            if(clAmsPeSUIsAssignable(su) != CL_OK)
                continue;
            if(su->status.readinessState != CL_AMS_READINESS_STATE_INSERVICE)
                continue;
            if(clAmsPeCheckAssignedCustom(su, si))
                continue;
            if(su->status.numStandbySIs >= sg->config.maxStandbySIsPerSU)
                continue;
            *targetSI = si;
            if(targetSU)
                *targetSU = su;
            break;
        }
        clAmsCustomAssignmentIterEnd(&iter);
        if(*targetSI)
            return CL_OK;
    }
    return clAmsPeSGFindSIForStandbyAssignment(sg, targetSI, scannedSIList, numScannedSIs);
}
ClRcT
clAmsGetFaultReport(
        CL_IN  const SaNameT  *compName,
        CL_IN  ClAmsLocalRecoveryT  recommendedRecovery,
        CL_IN  ClUint64T instantiateCookie)
{

    ClRcT  rc = CL_OK;
    ClUint32T  escalation = 0;
    ClAmsEntityRefT  entityRef = {{CL_AMS_ENTITY_TYPE_ENTITY},0,0};

    AMS_FUNC_ENTER (("\n"));

    if(!compName) 
        return CL_AMS_RC(CL_ERR_INVALID_PARAMETER);

    /*
     * First find the name reported by the fault API in the component database
     * If the name is not found in the component database it means that its a node
     * level fault escalation. In this case find the node in the node database.
     */

    memcpy (&entityRef.entity.name, compName, sizeof (SaNameT));
    entityRef.entity.type = CL_AMS_ENTITY_TYPE_COMP; 
    
    AMS_CALL ( clOsalMutexLock(gAms.mutex));

    if ( (rc = clAmsEntityDbFindEntity(&gAms.db.entityDb[CL_AMS_ENTITY_TYPE_COMP],&entityRef)) == CL_OK )
    {
        ClUint64T currentInstantiateCookie = 0;
        ClAmsCompT *comp = (ClAmsCompT*)entityRef.ptr;
        clLogInfo("COMP", "FAILURE", "Processing fault for component [%s], instantiate Cookie [%lld]", comp->config.entity.name.value, instantiateCookie);
        
        currentInstantiateCookie = comp->status.instantiateCookie;
        if(instantiateCookie && instantiateCookie < currentInstantiateCookie)
        {
            clLogInfo("COMP", "FAILURE", 
                      "Ignoring fault for component [%s], instantiation identifier [%lld] "
                      "as component has already been recovered with instantiation identifier [%lld]",
                      comp->config.entity.name.value, instantiateCookie,
                      comp->status.instantiateCookie);
            goto exitfn;
        }
        AMS_CHECK_RC_ERROR ( clAmsPeEntityFaultReport(
                    entityRef.ptr,
                    &recommendedRecovery,
                    &escalation) );

    }

    else
    {

        if ( CL_GET_ERROR_CODE (rc) != CL_ERR_NOT_EXIST )
        {
            clLogWarning("COMP", "FAILURE", "Unable to find component [%s:%d] in AMS database", 
                         compName->value, compName->length);
            goto exitfn;
        }

         /*
          * See if its node level fault escalation
          */

         entityRef.entity.type = CL_AMS_ENTITY_TYPE_NODE;

         AMS_CHECK_RC_ERROR( clAmsEntityDbFindEntity(
                     &gAms.db.entityDb[CL_AMS_ENTITY_TYPE_NODE],
                     &entityRef) ); 
         
         AMS_CHECK_RC_ERROR( clAmsPeEntityFaultReport(
                     entityRef.ptr,
                     &recommendedRecovery,
                     &escalation) );

     }

exitfn:

    AMS_CALL ( clOsalMutexUnlock(gAms.mutex));
    return CL_AMS_RC (rc);

}
ClRcT
clAmsPeSGFindSIForActiveAssignmentCustom(
        CL_IN ClAmsSGT *sg,
        CL_INOUT ClAmsSIT **targetSI,
        CL_INOUT ClAmsSUT **targetSU) 
{
    ClAmsEntityRefT *entityRef;
    ClAmsSIT* lookAfter = NULL;

    AMS_CHECK_SG ( sg );
    AMS_CHECKPTR ( !targetSI );

    AMS_FUNC_ENTER ( ("SG [%s]\n",sg->config.entity.name.value) );

    if (*targetSI) lookAfter = *targetSI;
    if (targetSU) *targetSU = NULL;

    /*
     * For SI preference loading strategy, try to find the SI which has an assignable preferred SU first
     */
    if(sg->config.loadingStrategy == CL_AMS_SG_LOADING_STRATEGY_BY_SI_PREFERENCE)
    {
        for (entityRef = clAmsEntityListGetFirst(&sg->config.siList);
             entityRef !=  NULL;
             entityRef = clAmsEntityListGetNext(&sg->config.siList, entityRef)) 
        {
            ClAmsEntityRefT *suRef;
            ClAmsSIT *si = (ClAmsSIT*)entityRef->ptr;
            AMS_CHECK_SI(si);
            if(lookAfter == si) continue;
            if(clAmsPeSIIsActiveAssignableCustom(si) != CL_OK)
                continue;
            for(suRef = clAmsEntityListGetFirst(&si->config.suList);
                suRef != NULL;
                suRef = clAmsEntityListGetNext(&si->config.suList, suRef))
            {
                ClAmsSUT *su = (ClAmsSUT*)suRef->ptr;
                if(clAmsPeSUIsAssignable(su) != CL_OK) 
                    continue;
                if(su->status.readinessState != CL_AMS_READINESS_STATE_INSERVICE)
                    continue;
                if(clAmsPeCheckAssignedCustom(su, si))
                    continue;
                if(su->status.numActiveSIs >= sg->config.maxActiveSIsPerSU)
                    continue;
                *targetSI = si;
                return CL_OK;
            }
        }
        lookAfter = NULL;
    }
    else
    {
        /*
         * Check assignment preference enqueued in each of the SIs
         */
        *targetSI = NULL;
        for(entityRef = clAmsEntityListGetFirst(&sg->config.siList);
            entityRef != NULL;
            entityRef = clAmsEntityListGetNext(&sg->config.siList, entityRef))
        {
            ClAmsSISURefT *siSURef = NULL;
            ClAmsSIT *si = (ClAmsSIT*)entityRef->ptr;
            ClAmsCustomAssignmentIterT iter = {0};
            if(!si) 
                continue;
            if(clAmsPeSIIsActiveAssignableCustom(si) != CL_OK) 
                continue;
            clAmsCustomAssignmentIterInit(&iter, si);
            while( (siSURef = clAmsCustomAssignmentIterNext(&iter)) )
            {
                ClAmsSUT *su;
                if(siSURef->haState != CL_AMS_HA_STATE_ACTIVE) continue;
                su = (ClAmsSUT*)siSURef->entityRef.ptr;
                if(clAmsPeSUIsAssignable(su) != CL_OK)
                    continue;
                if(su->status.readinessState != CL_AMS_READINESS_STATE_INSERVICE)
                    continue;
                if(clAmsPeCheckAssignedCustom(su, si))
                    continue;
                if(su->status.numActiveSIs >= sg->config.maxActiveSIsPerSU)
                    continue;
                *targetSI = si;
                if(targetSU)
                    *targetSU = su;
                break;
            }
            clAmsCustomAssignmentIterEnd(&iter);
            if(*targetSI) return CL_OK;
        }
    }
    *targetSI = NULL;
    return CL_AMS_RC(CL_ERR_NOT_EXIST);
}
ClRcT
clAmsPeSGAssignSUCustom(
                        CL_IN ClAmsSGT *sg
                        )
{
    ClAmsSIT **scannedSIList = NULL;
    ClUint32T numScannedSIs = 0;
    ClUint32T numMaxSIs = 0;

    AMS_CHECK_SG ( sg );

    AMS_FUNC_ENTER ( ("SG [%s]\n",sg->config.entity.name.value) );

    /*
     * Find SU assignments for SIs requiring active assignments
     */
 
    {
        ClRcT rc1 = CL_OK;
        ClRcT rc2 = CL_OK;
        ClAmsSIT *lastSI = NULL;
        ClAmsSUT *lastSU = NULL;

        while ( 1 )
        {
            ClAmsSUT *su = NULL;
            ClAmsSIT *si=NULL;

            rc1 = clAmsPeSGFindSIForActiveAssignmentCustom(sg, &si, &su);

            if ( rc1 != CL_OK ) 
            {
                break;
            }
            
            clLogInfo("SG", "ASI",
                      "SI [%.*s] needs assignment...",
                      si->config.entity.name.length-1,
                      si->config.entity.name.value);
            
            if(!su)
            {
                rc2 = clAmsPeSGFindSUForActiveAssignmentCustom(sg, &su, si);

                if ( rc2 != CL_OK )
                {
                    break;
                }
            }

            if( (lastSI == si) && (lastSU == su) )
            {
                AMS_LOG(CL_DEBUG_ERROR, 
                        ("Assign active to SG - Current SI and SU same as "\
                         "last selection. Breaking out of assignment\n"));
                break;
            }

            lastSI = si;
            lastSU = su;
            su->status.numWaitAdjustments = 0;
            AMS_CALL ( clAmsPeSUAssignSI(su, si, CL_AMS_HA_STATE_ACTIVE) );
        }

        if ( (rc1 != CL_OK) && (CL_GET_ERROR_CODE(rc1) != CL_ERR_NOT_EXIST) )
        {
            return rc1;
        }

        if ( (rc2 != CL_OK) && (CL_GET_ERROR_CODE(rc2) != CL_ERR_NOT_EXIST) )
        {
            return rc2;
        }
    }

    /*
     * Find SU assignments for SIs requiring standby assignments
     */
 
    {
        ClRcT rc1 = CL_OK;
        ClRcT rc2 = CL_OK;
        ClAmsSIT *lastSI = NULL;
        ClAmsSUT *lastSU = NULL;

        while ( 1 )
        {
            ClAmsSIT *si = NULL;
            ClAmsSUT *su = NULL;

            rc1 = clAmsPeSGFindSIForStandbyAssignmentCustom(sg, &si, &su, scannedSIList, numScannedSIs);

            if ( rc1 != CL_OK ) 
            {
                break;
            }

            if(!su)
            {
                rc2 = clAmsPeSGFindSUForStandbyAssignmentCustom(sg, &su, si);

                if ( rc2 != CL_OK )
                {
                    break;
                }
            }

            if( (lastSI == si) && (lastSU == su) )
            {
                AMS_LOG(CL_DEBUG_ERROR, 
                        ("Assign standby to SG - Current SI and SU same as "\
                         "last selection. Breaking out of assignment step\n"));
                break;
            }

            lastSI = si;
            lastSU = su;

            rc2 = clAmsPeSUAssignSI(su, si, CL_AMS_HA_STATE_STANDBY);

            if(rc2 != CL_OK)
            {
                if(CL_GET_ERROR_CODE(rc2) == CL_ERR_DOESNT_EXIST
                   ||
                   CL_GET_ERROR_CODE(rc2) == CL_ERR_NOT_EXIST)
                {
                    /*
                     * We could be encountering fixed slot protection config.
                     * So skip this SI and check for other SIs that could be
                     * assigned as standby
                     */
                    ClUint32T numSIs = sg->config.siList.numEntities;

                    if(!numSIs) 
                    {
                        if(scannedSIList) clHeapFree(scannedSIList);
                        return rc2;
                    }

                    if(numSIs > numMaxSIs)
                    {
                        numMaxSIs = numSIs;

                        scannedSIList = clHeapRealloc(scannedSIList,
                                                      numSIs * sizeof(*scannedSIList));
                        CL_ASSERT(scannedSIList != NULL);
                    }
                    scannedSIList[numScannedSIs++] = si;
                    rc2 = CL_OK;
                    continue;
                }
                else
                {
                    if(scannedSIList) clHeapFree(scannedSIList);
                    return rc2;
                }
            }
        }

        if ( (rc1 != CL_OK) && (CL_GET_ERROR_CODE(rc1) != CL_ERR_NOT_EXIST) )
        {
            if(scannedSIList) clHeapFree(scannedSIList);
            return rc1;
        }

        if ( (rc2 != CL_OK) && (CL_GET_ERROR_CODE(rc2) != CL_ERR_NOT_EXIST) )
        {
            if(scannedSIList) clHeapFree(scannedSIList);
            return rc2;
        }
    }

    if(scannedSIList) clHeapFree(scannedSIList);
    return CL_OK;
}
ClRcT
clAmsPeSGFindSUForActiveAssignmentCustom(
        CL_IN ClAmsSGT *sg,
        CL_IN ClAmsSUT **su,
        CL_IN ClAmsSIT *si)
{
    ClAmsEntityRefT *eRef;

    AMS_CHECK_SG ( sg );
    AMS_CHECKPTR ( !su );

    AMS_FUNC_ENTER ( ("SG [%s]\n",sg->config.entity.name.value) );

    *su = (ClAmsSUT *) NULL;

    switch ( sg->config.loadingStrategy )
    {
        /*
         * This loading strategy picks the SU based on the SI's preference.
         */
        case CL_AMS_SG_LOADING_STRATEGY_BY_SI_PREFERENCE:
        {
            for ( eRef = clAmsEntityListGetFirst(&si->config.suList);
                  eRef != (ClAmsEntityRefT *) NULL;
                  eRef = clAmsEntityListGetNext(&si->config.suList, eRef) )
            {
                ClAmsSUT *tmpSU = (ClAmsSUT *) eRef->ptr;

                if(clAmsPeCheckSUReassignOp(tmpSU, si, CL_TRUE))
                {
                    *su = tmpSU;
                    return CL_OK;
                }

                if(clAmsPeSUIsAssignable(tmpSU) != CL_OK)
                {
                    tmpSU->status.numDelayAssignments = 0;
                    if(tmpSU->status.suAssignmentTimer.count > 0)
                    {
                        clAmsEntityTimerStop((ClAmsEntityT*)tmpSU,
                                             CL_AMS_SU_TIMER_ASSIGNMENT);
                    }
                    continue;
                }

                if ( tmpSU->status.readinessState != CL_AMS_READINESS_STATE_INSERVICE )
                {
                    /*
                     * Delay assignments if possible
                     */
                    if(tmpSU->status.suAssignmentTimer.count > 0)
                    {
                        return CL_AMS_RC(CL_ERR_NOT_EXIST);
                    }
                    
                    if(tmpSU->status.numDelayAssignments < 2 )
                    {
                        ++tmpSU->status.numDelayAssignments;
                        
                        clLogDebug("SU", "PREF-ASSGN",
                                   "Delaying preferred active SI [%s] assignment to SU [%s] "
                                   "by [%d] ms",
                                   si->config.entity.name.value, 
                                   tmpSU->config.entity.name.value,
                                   CL_AMS_SU_ASSIGNMENT_DELAY);

                        AMS_CALL ( clAmsEntityTimerStart((ClAmsEntityT*)tmpSU, 
                                                         CL_AMS_SU_TIMER_ASSIGNMENT) );
                        return CL_AMS_RC(CL_ERR_NOT_EXIST);
                    }

                    tmpSU->status.numDelayAssignments = 0;

                    continue;
                }
                
                if(clAmsPeCheckAssignedCustom(tmpSU, si))
                    continue;

                tmpSU->status.numDelayAssignments = 0;

                if(tmpSU->status.suAssignmentTimer.count > 0)
                {
                    clAmsEntityTimerStop((ClAmsEntityT*)tmpSU,
                                         CL_AMS_SU_TIMER_ASSIGNMENT);
                }

                if ( tmpSU->status.numActiveSIs < sg->config.maxActiveSIsPerSU )
                {
                    *su = tmpSU;

                    return CL_OK;
                }
            }

            /*
             * Now check if any of the SU in the SG are waiting for a preferred
             * SU. If so, delay assignment of this guy.
             */
            if(clAmsPeSGCheckSUAssignmentDelay(sg) == CL_OK)
            {
                return CL_AMS_RC(CL_ERR_NOT_EXIST);
            }

            return CL_AMS_RC(CL_ERR_NOT_EXIST);
        }

        default:
            break;
    }
    return CL_AMS_RC(CL_ERR_NOT_EXIST);
}