Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

4254 lines
135 KiB

/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
fmgum.c
Abstract:
Cluster FM Global Update processing routines.
Author:
Rod Gamache (rodga) 24-Apr-1996
Revision History:
--*/
#include "fmp.h"
#include "ntrtl.h"
#if NO_SHARED_LOCKS
extern CRITICAL_SECTION gLockDmpRoot;
#else
extern RTL_RESOURCE gLockDmpRoot;
#endif
#define NODE_ID_SZ 6
#define LOG_MODULE FMGUM
DWORD
WINAPI
FmpGumReceiveUpdates(
IN DWORD Context,
IN BOOL SourceNode,
IN DWORD BufferLength,
IN PVOID Buffer
)
/*++
Routine Description:
Updates the specified resource (contained within Buffer) with a new
state.
Arguments:
Context - The message update type.
SourceNode - TRUE if this is the source node for this update.
FALSE otherwise.
BufferLength - Length of the received buffer.
Buffer - The actual buffer
Returns:
ERROR_SUCCESS
--*/
{
PFM_RESOURCE resource;
//
// Chittur Subbaraman (chitturs) - 4/18/99
//
// If FM groups are not fully initialized or FM is shutting down, then
// don't do anything.
//
if ( !FmpFMGroupsInited ||
FmpShutdown ) {
return(ERROR_SUCCESS);
}
switch ( Context ) {
case FmUpdateFailureCount:
{
PGUM_FAILURE_COUNT failureCount;
PFM_GROUP group;
//
// This update type is always sent.
// On the originating node, all of the work must be done by
// the sending thread.
// On the non-originating nodes, no locks can be acquired! This
// would cause hang situations with operations like move.
// ... this is okay, since the locking must be done on the sending
// node anyway, which owns the group.
//
if ( SourceNode == FALSE ) {
if ( BufferLength <= sizeof(GUM_FAILURE_COUNT) ) {
ClRtlLogPrint(LOG_UNUSUAL, "[FM] Gum FailureCount receive buffer too small!\n");
return(ERROR_SUCCESS);
}
failureCount = (PGUM_FAILURE_COUNT)Buffer;
group = OmReferenceObjectById( ObjectTypeGroup,
(LPCWSTR)&failureCount->GroupId[0] );
if ( group == NULL ) {
ClRtlLogPrint(LOG_UNUSUAL,
"[FM] Gum FailureCount failed to find group %1!ws!\n",
failureCount->GroupId);
return(ERROR_SUCCESS);
}
ClRtlLogPrint(LOG_NOISE,
"[FM] GUM update failure count %1!ws!, count %2!u!\n",
failureCount->GroupId,
failureCount->Count);
//FmpAcquireLocalGroupLock( group );
if ( group->OwnerNode == NmLocalNode ) {
ClRtlLogPrint(LOG_NOISE,
"[FM] Gum FailureCount wrong owner for %1!ws!\n",
failureCount->GroupId);
} else {
group->NumberOfFailures = failureCount->Count;
if ( failureCount->NewTime ) {
group->FailureTime = GetTickCount();
}
}
//FmpReleaseLocalGroupLock( group );
OmDereferenceObject( group );
}
break;
}
case FmUpdateCreateGroup:
{
PGUM_CREATE_GROUP GumGroup;
DWORD Status = ERROR_SUCCESS;
GumGroup = (PGUM_CREATE_GROUP)Buffer;
Status = FmpUpdateCreateGroup( GumGroup, SourceNode );
return(Status);
}
case FmUpdateCreateResource:
{
DWORD dwStatus = ERROR_SUCCESS;
PGUM_CREATE_RESOURCE GumResource =
(PGUM_CREATE_RESOURCE)Buffer;
dwStatus = FmpUpdateCreateResource( GumResource );
return( dwStatus );
}
case FmUpdateAddPossibleNode:
case FmUpdateRemovePossibleNode:
{
PGUM_CHANGE_POSSIBLE_NODE pGumChange;
PFM_RESOURCE pResource;
LPWSTR pszResourceId;
LPWSTR pszNodeId;
PNM_NODE pNode;
DWORD dwStatus;
DWORD dwControlCode;
PFMP_POSSIBLE_NODE pPossibleNode;
pGumChange = (PGUM_CHANGE_POSSIBLE_NODE)Buffer;
pszResourceId = pGumChange->ResourceId;
pszNodeId = (LPWSTR)((PCHAR)pszResourceId +
pGumChange->ResourceIdLen);
pResource = OmReferenceObjectById(ObjectTypeResource,pszResourceId);
pNode = OmReferenceObjectById(ObjectTypeNode, pszNodeId);
CL_ASSERT(pResource != NULL);
CL_ASSERT(pNode != NULL);
pPossibleNode = LocalAlloc( LMEM_FIXED,
sizeof(FMP_POSSIBLE_NODE) );
if ( pPossibleNode == NULL )
{
return(ERROR_NOT_ENOUGH_MEMORY);
}
if (Context == FmUpdateAddPossibleNode)
{
dwControlCode = CLUSCTL_RESOURCE_ADD_OWNER;
}
else
{
dwControlCode = CLUSCTL_RESOURCE_REMOVE_OWNER;
}
dwStatus = FmpUpdateChangeResourceNode(SourceNode,
pResource, pNode, dwControlCode);
//if status is not successful then return, else notify
//resource dlls
if (dwStatus != ERROR_SUCCESS)
{
//dereference the objects
OmDereferenceObject(pResource);
OmDereferenceObject(pNode);
//free the memory
LocalFree(pPossibleNode);
return(dwStatus);
}
pPossibleNode->Resource = pResource;
pPossibleNode->Node = pNode;
pPossibleNode->ControlCode = dwControlCode;
//
// Tell the resource about the ADD/REMOVE in a worker thread.
//
FmpPostWorkItem( FM_EVENT_RESOURCE_CHANGE,
pPossibleNode,
0 );
//
// Chittur Subbaraman (chitturs) - 6/7/99
//
// Don't reference pPossibleNode any more. It could have
// been freed by the worker thread by the time you get
// here.
//
ClusterEvent( CLUSTER_EVENT_RESOURCE_PROPERTY_CHANGE,
pResource );
// Let the worker thread perform the derefs/Frees
return(dwStatus);
}
case FmUpdateJoin:
if ( CsDmOrFmHasChanged )
{
//
// We can only send back SEQMISMATCH if we're a pure Windows Server 2003 (or later) environment.
// In a mixed mode cluster, a W2K node would end up infinitely retrying the FM join.
//
DWORD dwClusterHighestVersion;
NmGetClusterOperationalVersion( &dwClusterHighestVersion, NULL, NULL );
if ( CLUSTER_GET_MAJOR_VERSION( dwClusterHighestVersion ) < NT51_MAJOR_VERSION )
{
ClRtlLogPrint(LOG_UNUSUAL,"[FM] DM or FM update has occured during join; rejecting FmUpdateJoin.\n" );
return ERROR_CLUSTER_DATABASE_SEQMISMATCH;
}
// Don't need to reset CsDmOrFmHasChanged here -- we will reset it when we see the DmUpdateJoin.
}
break;
case FmUpdateCreateResourceType:
{
DWORD dwStatus;
dwStatus = FmpUpdateCreateResourceType( Buffer );
return( dwStatus );
}
break;
case FmUpdateDeleteResourceType:
{
BOOL ResourceExists = FALSE;
PFM_RESTYPE Type;
Type = OmReferenceObjectById( ObjectTypeResType,
(LPWSTR)Buffer);
if (Type == NULL) {
return(ERROR_CLUSTER_RESOURCE_TYPE_NOT_FOUND);
}
//
// Make sure no resources exist of this type.
//
OmEnumObjects( ObjectTypeResource,
FmpFindResourceType,
Type,
&ResourceExists);
if (ResourceExists) {
OmDereferenceObject(Type);
return(ERROR_DIR_NOT_EMPTY);
}
//
// We need to dereference the object twice to get
// rid of it. But then any notification handlers will
// not get a chance to see the object by the time
// the handler gets called. So we use the EP_DEREF_CONTEXT
// flag to get the event processor to do the second deref
// once everything has been dispatched.
//
FmpDeleteResType(Type);
ClusterEventEx( CLUSTER_EVENT_RESTYPE_DELETED,
EP_DEREF_CONTEXT,
Type );
}
break;
case FmUpdateChangeGroup:
{
PGUM_CHANGE_GROUP pGumChange;
PFM_RESOURCE pResource;
LPWSTR pszResourceId;
LPWSTR pszGroupId;
PFM_GROUP pNewGroup;
DWORD dwStatus;
DWORD dwClusterHighestVersion;
pGumChange = (PGUM_CHANGE_GROUP)Buffer;
pszResourceId = pGumChange->ResourceId;
pszGroupId = (LPWSTR)((PCHAR)pszResourceId +
pGumChange->ResourceIdLen);
//
// Find the specified resource and group.
//
pResource = OmReferenceObjectById(ObjectTypeResource,
pszResourceId);
if (pResource == NULL) {
return(ERROR_RESOURCE_NOT_FOUND);
}
pNewGroup = OmReferenceObjectById(ObjectTypeGroup,
pszGroupId);
if (pNewGroup == NULL) {
OmDereferenceObject(pResource);
return(ERROR_SUCCESS);
}
dwStatus = FmpUpdateChangeResourceGroup(SourceNode,
pResource, pNewGroup);
OmDereferenceObject(pNewGroup);
OmDereferenceObject(pResource);
return(dwStatus);
}
break;
default:
{
}
ClRtlLogPrint(LOG_UNUSUAL,"[FM] Gum received bad context, %1!u!\n",
Context);
}
return(ERROR_SUCCESS);
} // FmpGumReceiveUpdates
DWORD
FmpUpdateChangeQuorumResource2(
IN BOOL SourceNode,
IN LPCWSTR NewQuorumResourceId,
IN LPCWSTR szRootClusFilePath,
IN LPDWORD pdwMaxQuorumLogSize,
IN LPDWORD pdwQuorumArbTimeout,
IN LPDWORD pdwNewQuorumResourceCharacterictics OPTIONAL
)
/*++
Routine Description:
Perform updates related to changing of the quorum resource.
Arguments:
Return Value:
ERROR_SUCCESS if successful.
A Win32 error code otherwise.
--*/
{
PFM_RESOURCE pResource;
PFM_RESOURCE pOldQuoResource=NULL;
DWORD dwStatus;
DWORD dwChkPtSeq;
HDMKEY ResKey;
HLOCALXSACTION hXsaction = NULL;
HLOG hNewQuoLog=NULL;
WCHAR szQuorumLogPath[MAX_PATH];
if ( !FmpFMGroupsInited ||
FmpShutdown ) {
return(ERROR_SUCCESS);
}
lstrcpyW(szQuorumLogPath, szRootClusFilePath);
//lstrcatW(szQuorumLogPath, cszClusLogFileRootDir);
pResource = OmReferenceObjectById( ObjectTypeResource,
NewQuorumResourceId );
if (pResource == NULL)
{
ClRtlLogPrint(LOG_UNUSUAL,
"[FM] FmpUpdateChangeQuorumResource: Resource <%1!ws!> could not be found....\n",
NewQuorumResourceId);
return(ERROR_SUCCESS);
}
DmPauseDiskManTimer();
//since the resource->quorum is going to change, acquire the quocritsec
//always acquire the gQuoCritsec before gQuoLock
ACQUIRE_EXCLUSIVE_LOCK(gQuoChangeLock);
//prevent any resources from going online at this time
ACQUIRE_EXCLUSIVE_LOCK(gQuoLock);
//pause any changes to the cluster database
//always acquire this lock after gQuoLock(refer to the ordering of locks
// in fminit.c)
ACQUIRE_EXCLUSIVE_LOCK(gLockDmpRoot);
//if this resource was already a quorum resource
if (!pResource->QuorumResource)
{
//
// Now find the current quorum resource.
//
OmEnumObjects( ObjectTypeResource,
FmpFindQuorumResource,
&pOldQuoResource,
NULL );
if ( pOldQuoResource != NULL )
{
CL_ASSERT( pOldQuoResource->QuorumResource );
// Stop the quorum reservation thread!
pOldQuoResource->QuorumResource = FALSE;
}
//set the new resource to be the quorum resource
pResource->QuorumResource = TRUE;
}
//writes to the old log file
hXsaction = DmBeginLocalUpdate();
if (!hXsaction)
{
dwStatus = GetLastError();
goto FnExit;
}
dwStatus = DmLocalSetValue( hXsaction,
DmQuorumKey,
cszPath,
REG_SZ,
(LPBYTE)szQuorumLogPath,
(lstrlenW(szQuorumLogPath)+1) * sizeof(WCHAR));
if (dwStatus != ERROR_SUCCESS)
goto FnExit;
#ifdef CLUSTER_TESTPOINT
TESTPT(TpFailLocalXsaction) {
LPWSTR pszStr = szQuorumLogPath;
dwStatus = (MAX_PATH * sizeof(WCHAR));
dwStatus = DmQuerySz( DmQuorumKey,
cszPath,
&pszStr,
&dwStatus,
&dwStatus);
ClRtlLogPrint(LOG_UNUSUAL,
"[FM] Testing failing a local transaction midway- new quorum path %1!ws!\r\n",
szQuorumLogPath);
dwStatus = 999999;
goto FnExit;
}
#endif
dwStatus = DmLocalSetValue( hXsaction,
DmQuorumKey,
cszMaxQuorumLogSize,
REG_DWORD,
(LPBYTE)pdwMaxQuorumLogSize,
sizeof(DWORD));
if (dwStatus != ERROR_SUCCESS)
goto FnExit;
//if the old quorum resource is different from the new quorum resource
if ((pOldQuoResource) && (pOldQuoResource != pResource))
{
//get/set the new/old resource's flags
//set the core flag on the new quorum resource
ResKey = DmOpenKey( DmResourcesKey,
NewQuorumResourceId,
KEY_READ | KEY_SET_VALUE);
if (!ResKey)
{
dwStatus = GetLastError();
goto FnExit;
}
pResource->ExFlags |= CLUS_FLAG_CORE;
dwStatus = DmLocalSetValue( hXsaction,
ResKey,
CLUSREG_NAME_FLAGS,
REG_DWORD,
(LPBYTE)&(pResource->ExFlags),
sizeof(DWORD));
DmCloseKey( ResKey );
if (dwStatus != ERROR_SUCCESS)
goto FnExit;
//unset the core flag on the old quorum resource
ResKey = DmOpenKey( DmResourcesKey,
OmObjectId(pOldQuoResource),
KEY_READ | KEY_SET_VALUE);
if (!ResKey)
{
dwStatus = GetLastError();
goto FnExit;
}
pOldQuoResource->ExFlags &= ~CLUS_FLAG_CORE;
//unset the core flag on the old quorum resource
dwStatus = DmLocalSetValue( hXsaction,
ResKey,
CLUSREG_NAME_FLAGS,
REG_DWORD,
(LPBYTE)&(pOldQuoResource->ExFlags),
sizeof(DWORD));
DmCloseKey( ResKey );
if (dwStatus != ERROR_SUCCESS)
goto FnExit;
}
//
// Set the quorum resource value.
//
dwStatus = DmLocalSetValue( hXsaction,
DmQuorumKey,
CLUSREG_NAME_QUORUM_RESOURCE,
REG_SZ,
(CONST BYTE *)OmObjectId(pResource),
(lstrlenW(OmObjectId(pResource))+1)*sizeof(WCHAR));
if (dwStatus != ERROR_SUCCESS)
{
goto FnExit;
}
if (pdwQuorumArbTimeout)
{
//if this is a mixed mode cluster pdwQourumArbTimeout will be NULL
// Update the cluster registry from there
// MM reads this location on startup
dwStatus = DmLocalSetValue( hXsaction,
DmClusterParametersKey,
CLUSREG_NAME_QUORUM_ARBITRATION_TIMEOUT,
REG_DWORD,
(CONST PUCHAR)pdwQuorumArbTimeout,
sizeof(DWORD) );
if ( dwStatus != ERROR_SUCCESS )
{
ClRtlLogPrint(LOG_UNUSUAL,
"[FM] FmSetQuorumResource: failed to set the cluster arbitration timeout, status = %1!u!\n",
dwStatus);
goto FnExit;
}
ClRtlLogPrint(LOG_NOISE,
"[FM] FmSetQuorumResource: setting QuorumArbitratrionTimeout to be = %1!u!\n",
*pdwQuorumArbTimeout);
// Tell MM about the change...
MmQuorumArbitrationTimeout = *pdwQuorumArbTimeout;
}
FnExit:
if (dwStatus == ERROR_SUCCESS)
{
LPWSTR szClusterName=NULL;
DWORD dwSize=0;
DWORD dwCharacteristics = CLUS_CHAR_UNKNOWN;
//commit the update on the old log file,
//any nodes that were done, will get this change
//I cant delete this file
DmCommitLocalUpdate(hXsaction);
if ( !ARGUMENT_PRESENT ( pdwNewQuorumResourceCharacterictics ) )
{
pdwNewQuorumResourceCharacterictics = &dwCharacteristics;
}
//
// If the caller has passed in characteristics, then don't bother to drop a control
// code into a resource dll to find the characteristics in the following function.
// Dropping a control code from within a GUM handler is disaster waiting to happen.
//
//
//close the old log file, open the new one and take a checkpoint
DmSwitchToNewQuorumLog(szQuorumLogPath, *pdwNewQuorumResourceCharacterictics);
// SS:the buffer should contain the current cluster name ?
DmQuerySz( DmClusterParametersKey,
CLUSREG_NAME_CLUS_NAME,
&szClusterName,
&dwSize,
&dwSize);
if (szClusterName)
ClusterEventEx(CLUSTER_EVENT_PROPERTY_CHANGE,
EP_FREE_CONTEXT,
szClusterName);
if ((pOldQuoResource) && (pOldQuoResource != pResource))
{
//generate the resource property change events
ClusterEvent( CLUSTER_EVENT_RESOURCE_PROPERTY_CHANGE,
pResource );
ClusterEvent( CLUSTER_EVENT_RESOURCE_PROPERTY_CHANGE,
pOldQuoResource );
}
}
else
{
if (hXsaction) DmAbortLocalUpdate(hXsaction);
//reinstall the tombstone
DmReinstallTombStone(szQuorumLogPath);
//
// Make sure the flags are reset back
//
if ((pOldQuoResource) && (pOldQuoResource != pResource))
{
pOldQuoResource->QuorumResource = TRUE;
pResource->QuorumResource = FALSE;
}
}
if (pOldQuoResource) OmDereferenceObject(pOldQuoResource);
OmDereferenceObject(pResource);
//release locks
RELEASE_LOCK(gLockDmpRoot);
RELEASE_LOCK(gQuoLock);
RELEASE_LOCK(gQuoChangeLock);
DmRestartDiskManTimer();
return(dwStatus);
}
DWORD
FmpUpdateChangeQuorumResource(
IN BOOL SourceNode,
IN LPCWSTR NewQuorumResourceId,
IN LPCWSTR szRootClusFilePath,
IN LPDWORD pdwMaxQuorumLogSize
)
/*++
Routine Description:
Perform updates related to changing of the quorum resource.
Arguments:
Return Value:
ERROR_SUCCESS if successful.
A Win32 error code otherwise.
--*/
{
return(FmpUpdateChangeQuorumResource2(SourceNode, NewQuorumResourceId, szRootClusFilePath,
pdwMaxQuorumLogSize, NULL, NULL));
}
DWORD
FmpUpdateResourceState(
IN BOOL SourceNode,
IN LPCWSTR ResourceId,
IN PGUM_RESOURCE_STATE ResourceState
)
/*++
Routine Description:
GUM update handler for resource state changes.
Arguments:
SourceNode - Supplies whether or not this node was the source of the update
ResourceId - Supplies the id of the resource whose state is changing
ResourceState - Supplies the new state of the resource.
Return Value:
ERROR_SUCCESS if successful
Win32 error code otherwise
--*/
{
PFM_RESOURCE resource;
if ( !FmpFMGroupsInited ) {
return(ERROR_SUCCESS);
}
//
// This update type is always sent.
// On the originating node, all of the work must be done by
// the sending thread.
// On the non-originating nodes, no locks can be acquired! This
// would cause some hang situations with operations like move.
// ... this is okay, since the locking must be done on the sending
// node anyway, which owns the group.
//
if ( SourceNode == FALSE ) {
resource = OmReferenceObjectById( ObjectTypeResource, ResourceId );
if ( resource == NULL ) {
ClRtlLogPrint(LOG_UNUSUAL,
"[FM] Gum ResourceState failed to find resource %1!ws!\n",
ResourceId);
CL_LOGFAILURE( ERROR_RESOURCE_NOT_FOUND );
return(ERROR_SUCCESS);
}
ClRtlLogPrint(LOG_NOISE,
"[FM] Gum update resource %1!ws!, state %2!u!, current state %3!u!.\n",
ResourceId,
ResourceState->State,
ResourceState->PersistentState);
//FmpAcquireLocalResourceLock( resource );
if ( resource->Group->OwnerNode == NmLocalNode ) {
ClRtlLogPrint(LOG_NOISE,
"[FM] Gum ResourceState wrong owner for %1!ws!\n",
ResourceId);
} else {
resource->State = ResourceState->State;
resource->PersistentState = ResourceState->PersistentState;
resource->StateSequence = ResourceState->StateSequence;
switch ( ResourceState->State ) {
case ClusterResourceOnline:
ClusterEvent( CLUSTER_EVENT_RESOURCE_ONLINE, resource );
break;
case ClusterResourceOffline:
ClusterEvent( CLUSTER_EVENT_RESOURCE_OFFLINE, resource );
break;
case ClusterResourceFailed:
ClusterEvent( CLUSTER_EVENT_RESOURCE_FAILED, resource );
break;
case ClusterResourceOnlinePending:
case ClusterResourceOfflinePending:
ClusterEvent( CLUSTER_EVENT_RESOURCE_CHANGE, resource );
break;
default:
ClRtlLogPrint(LOG_UNUSUAL,
"[FM] Gum update resource state, bad state %1!u!\n",
ResourceState->State);
break;
}
}
OmDereferenceObject( resource );
}
return(ERROR_SUCCESS);
}
DWORD
FmpUpdateGroupState(
IN BOOL SourceNode,
IN LPCWSTR GroupId,
IN LPCWSTR NodeId,
IN PGUM_GROUP_STATE GroupState
)
/*++
Routine Description:
GUM update handler for group state changes.
Arguments:
SourceNode - Supplies whether or not this node was the source of the update
GroupId - Supplies the id of the resource whose state is changing
NodeId - Supplies the node id of the group owner.
GroupState - Supplies the new state of the group.
Return Value:
ERROR_SUCCESS if successful
Win32 error code otherwise
--*/
{
PFM_GROUP group;
PWSTR nodeId;
PNM_NODE node;
if ( !FmpFMGroupsInited ) {
return(ERROR_SUCCESS);
}
//
// This update type is always sent.
// On the originating node, all of the work must be done by
// the sending thread.
// On the non-originating nodes, no locks can be acquired! This
// would cause some hang situations with operations like move.
// ... this is okay, since the locking must be done on the sending
// node anyway, which owns the group.
//
if ( SourceNode == FALSE ) {
group = OmReferenceObjectById( ObjectTypeGroup,
GroupId );
if ( group == NULL ) {
ClRtlLogPrint(LOG_UNUSUAL,
"[FM] Gum GroupState failed to find group %1!ws!\n",
GroupId);
return(ERROR_SUCCESS);
}
ClRtlLogPrint(LOG_NOISE,
"[FM] GUM update group %1!ws!, state %2!u!\n",
GroupId,
GroupState->State);
if ( group->OwnerNode == NmLocalNode ) {
ClRtlLogPrint(LOG_NOISE,
"[FM] Gum GroupState wrong owner for %1!ws!\n",
GroupId);
} else {
group->State = GroupState->State;
group->PersistentState = GroupState->PersistentState;
group->StateSequence = GroupState->StateSequence;
node = OmReferenceObjectById( ObjectTypeNode,
NodeId );
if ( node == NULL ) {
ClRtlLogPrint(LOG_UNUSUAL,
"[FM] Owner of Group %1!ws! cannot be found %2!ws!\n",
GroupId,
NodeId);
} else {
ClRtlLogPrint(LOG_NOISE,
"[FM] New owner of Group %1!ws! is %2!ws!, state %3!u!, curstate %4!u!.\n",
OmObjectId( group ),
OmObjectId( node ),
group->State,
group->PersistentState);
if ( !FmpInPreferredList( group, node, FALSE, NULL ) ) {
ClRtlLogPrint( LOG_UNUSUAL,
"[FM] New owner %1!ws! is not in preferred list for group %2!ws!.\n",
OmObjectId( node ),
OmObjectId( group ));
}
}
group->OwnerNode = node;
switch ( GroupState->State ) {
case ClusterGroupOnline:
case ClusterGroupPartialOnline:
ClusterEvent( CLUSTER_EVENT_GROUP_ONLINE, group );
break;
case ClusterGroupOffline:
ClusterEvent( CLUSTER_EVENT_GROUP_OFFLINE, group );
break;
default:
ClRtlLogPrint(LOG_UNUSUAL,"[FM] Gum update group state, bad state %1!u!\n", GroupState->State);
break;
}
}
OmDereferenceObject( group );
}
return(ERROR_SUCCESS);
}
DWORD
FmpUpdateGroupNode(
IN BOOL SourceNode,
IN LPCWSTR GroupId,
IN LPCWSTR NodeId
)
/*++
Routine Description:
GUM update handler for group node changes. This is required for
notification
when a group moves between nodes but does not change state (i.e. it was
already offline)
Arguments:
SourceNode - Supplies whether or not this node was the source of the update
GroupId - Supplies the id of the resource whose state is changing
NodeId - Supplies the node id of the group owner.
Return Value:
ERROR_SUCCESS if successful
Win32 error code otherwise
--*/
{
PFM_GROUP pGroup;
DWORD dwStatus = ERROR_SUCCESS;
PNM_NODE pNode = NULL;
PNM_NODE pPrevNode = NULL;
if ( !FmpFMGroupsInited )
{
return(ERROR_SUCCESS);
}
pGroup = OmReferenceObjectById( ObjectTypeGroup,
GroupId );
if (pGroup == NULL)
{
ClRtlLogPrint(LOG_CRITICAL,
"[FM] FmpUpdateGroupNode: GroupID = %1!ws! could not be found...\n",
GroupId);
//
// Chittur Subbaraman (chitturs) - 6/12/99
//
// Return ERROR_SUCCESS here since this is what NT4 side does.
// Compatibility pain !
//
goto FnExit;
}
pNode = OmReferenceObjectById(ObjectTypeNode,
NodeId);
if (pNode == NULL)
{
dwStatus = ERROR_CLUSTER_NODE_NOT_FOUND;
goto FnExit;
}
//
// HACKHACK: Chittur Subbaraman (chitturs) - 5/20/99
// Comment out as a temporary solution to avoid deadlocks.
//
// FmpAcquireLocalGroupLock(pGroup);
pPrevNode = pGroup->OwnerNode;
//set the new owner node, incr ref count
OmReferenceObject(pNode);
pGroup->OwnerNode = pNode;
//decr ref count on previous owner
OmDereferenceObject(pPrevNode);
//
// HACKHACK: Chittur Subbaraman (chitturs) - 5/20/99
// Comment out as a temporary solution to avoid deadlocks.
//
// FmpReleaseLocalGroupLock(pGroup);
//generate an event to signify group owner node change
ClusterEvent(CLUSTER_EVENT_GROUP_CHANGE, pGroup);
FnExit:
if (pGroup) OmDereferenceObject(pGroup);
if (pNode) OmDereferenceObject(pNode);
return(dwStatus);
}
DWORD
FmpUpdateChangeClusterName(
IN BOOL SourceNode,
IN LPCWSTR szNewName
)
/*++
Routine Description:
GUM update routine for changing the name of the cluster.
This changes the name property of the core network name resource
as well. The resource is notified about it by a worker thread that
the name has been changed.
Arguments:
SourceNode - Supplies whether or not this node originated the update.
NewName - Supplies the new name of the cluster.
Return Value:
ERROR_SUCCESS if successful
Win32 error code otherwise
--*/
{
LPWSTR Buffer;
DWORD Length;
DWORD Status = ERROR_SUCCESS;
LPWSTR ClusterNameId=NULL;
DWORD idMaxSize = 0;
DWORD idSize = 0;
PFM_RESOURCE Resource=NULL;
HDMKEY ResKey = NULL;
HDMKEY ParamKey = NULL;
HLOCALXSACTION hXsaction=NULL;
DWORD cbNewClusterName;
LPWSTR lpszNewClusterName;
if ( !FmpFMGroupsInited ||
FmpShutdown )
{
return(ERROR_SUCCESS);
}
cbNewClusterName = ( lstrlen ( szNewName ) + 1 ) * sizeof ( WCHAR );
hXsaction = DmBeginLocalUpdate();
if (!hXsaction)
{
Status = ERROR_SUCCESS;
goto FnExit;
}
//find the core network name resource, set its private properties
Status = DmQuerySz( DmClusterParametersKey,
CLUSREG_NAME_CLUS_CLUSTER_NAME_RES,
(LPWSTR*)&ClusterNameId,
&idMaxSize,
&idSize);
if (Status != ERROR_SUCCESS) {
ClRtlLogPrint(LOG_CRITICAL,
"[FM] FmpUpdateChangeClusterName: failed to get cluster name resource, status=%1!u!.\n",
Status);
goto FnExit;
}
//
// Reference the specified resource ID.
//
Resource = OmReferenceObjectById( ObjectTypeResource, ClusterNameId );
if (Resource == NULL) {
Status = ERROR_RESOURCE_NOT_FOUND;
ClRtlLogPrint(LOG_CRITICAL,
"[FM] FmpUpdateChangeClusterName: failed to find the cluster name resource, status=%1!u!\n",
Status);
goto FnExit;
}
ResKey = DmOpenKey(DmResourcesKey, ClusterNameId, KEY_READ | KEY_SET_VALUE);
if (!ResKey)
{
Status = GetLastError();
goto FnExit;
}
ParamKey = DmOpenKey(ResKey, cszParameters, KEY_READ | KEY_SET_VALUE);
if (!ParamKey)
{
Status = GetLastError();
goto FnExit;
}
Status = DmLocalSetValue(hXsaction,
ParamKey,
CLUSREG_NAME_NET_NAME,
REG_SZ,
(CONST BYTE *)szNewName,
cbNewClusterName);
if ( Status != ERROR_SUCCESS )
{
ClRtlLogPrint(LOG_CRITICAL,
"[FM] FmpUpdateChangeClusterName: failed to set the Name property, Status=%1!u!\n",
Status);
goto FnExit;
}
//update the default cluster name
Status = DmLocalSetValue(hXsaction,
DmClusterParametersKey,
CLUSREG_NAME_CLUS_NAME,
REG_SZ,
(CONST BYTE *)szNewName,
cbNewClusterName);
if (Status != ERROR_SUCCESS)
{
ClRtlLogPrint(LOG_CRITICAL,
"[FM] FmpUpdateChangeClusterName: failed to set the cluster name property,Status=%1!u!\n",
Status);
goto FnExit;
}
//
// Update the CsClusterName variable.
// TODO: Need synchronization on CsClusterName
//
lpszNewClusterName = LocalAlloc ( LPTR, cbNewClusterName );
if ( lpszNewClusterName == NULL )
{
Status = GetLastError();
ClRtlLogPrint(LOG_CRITICAL, "[FM] FmpUpdateChangeClusterName: Alloc for name failed, Status=%1!u!\n",
Status);
goto FnExit;
}
LocalFree ( CsClusterName );
CsClusterName = lpszNewClusterName;
lstrcpy ( CsClusterName, szNewName );
//the cluster_event_property_change is generated by the api itself using
//the cluster wide event after netname has finished the applying the changes
FnExit:
if (ClusterNameId) LocalFree(ClusterNameId);
if (ParamKey) DmCloseKey(ParamKey);
if (ResKey) DmCloseKey(ResKey);
if (Resource) OmDereferenceObject(Resource);
if (hXsaction)
{
if (Status == ERROR_SUCCESS)
DmCommitLocalUpdate(hXsaction);
else
DmAbortLocalUpdate(hXsaction);
}
return(Status);
}
DWORD
FmpUpdateChangeResourceName(
IN BOOL bSourceNode,
IN LPCWSTR lpszResourceId,
IN LPCWSTR lpszNewName
)
/*++
Routine Description:
GUM dispatch routine for changing the friendly name of a resource.
Arguments:
bSourceNode - Supplies whether or not this node initiated the GUM update.
Not used.
lpszResourceId - Supplies the resource ID.
lpszNewName - Supplies the new friendly name.
Return Value:
ERROR_SUCCESS if successful.
Win32 error code otherwise.
--*/
{
PFM_RESOURCE pResource = NULL;
DWORD dwStatus;
HDMKEY hKey = NULL;
DWORD dwDisposition;
HLOCALXSACTION
hXsaction = NULL;
PFM_RES_CHANGE_NAME pResChangeName = NULL;
if ( !FmpFMGroupsInited ||
FmpShutdown ) {
return( ERROR_SUCCESS );
}
//
// Chittur Subbaraman (chitturs) - 6/28/99
//
// Restructure this GUM update as a local transaction.
//
//
pResource = OmReferenceObjectById( ObjectTypeResource, lpszResourceId );
if ( pResource == NULL )
{
ClRtlLogPrint(LOG_UNUSUAL,
"[FM] FmpUpdateChangeResourceName: Resource <%1!ws!> could not be found....\n",
lpszResourceId);
return( ERROR_RESOURCE_NOT_FOUND );
}
ClRtlLogPrint(LOG_NOISE,
"[FM] FmpUpdateChangeResourceName: Entry for resource <%1!ws!>, New name = <%2!ws!>...\n",
lpszResourceId,
lpszNewName);
//
// Start a transaction
//
hXsaction = DmBeginLocalUpdate();
if ( !hXsaction )
{
dwStatus = GetLastError();
ClRtlLogPrint(LOG_UNUSUAL,
"[FM] FmpUpdateChangeResourceName: Failed in starting a transaction for resource %1!ws!, Status =%2!d!....\n",
lpszResourceId,
dwStatus);
goto FnExit;
}
//
// Open the resources key.
//
hKey = DmLocalCreateKey( hXsaction,
DmResourcesKey,
lpszResourceId,
0,
KEY_READ | KEY_WRITE,
NULL,
&dwDisposition );
if ( hKey == NULL )
{
dwStatus = GetLastError();
ClRtlLogPrint(LOG_UNUSUAL,
"[FM] FmpUpdateChangeResourceName: Failed in opening the resources key for resource %1!ws!, Status =%2!d!....\n",
lpszResourceId,
dwStatus);
goto FnExit;
}
CL_ASSERT( dwDisposition != REG_CREATED_NEW_KEY );
//
// Set the resource name in the registry
//
dwStatus = DmLocalSetValue( hXsaction,
hKey,
CLUSREG_NAME_RES_NAME,
REG_SZ,
( CONST BYTE * ) lpszNewName,
( lstrlenW( lpszNewName ) + 1 ) *
sizeof( WCHAR ) );
if( dwStatus != ERROR_SUCCESS )
{
ClRtlLogPrint(LOG_CRITICAL,
"[FM] FmpUpdateChangeResourceName: DmLocalSetValue for resource %1!ws! fails, Status = %2!d!...\n",
lpszResourceId,
dwStatus);
goto FnExit;
}
pResChangeName = LocalAlloc( LMEM_FIXED,
lstrlenW( lpszNewName ) * sizeof ( WCHAR ) +
sizeof( FM_RES_CHANGE_NAME ) );
if ( pResChangeName == NULL )
{
dwStatus = GetLastError();
ClRtlLogPrint(LOG_UNUSUAL,
"[FM] FmpUpdateChangeResourceName: Unable to allocate memory for ResChangeName structure for resource <%1!ws!>, Status =%2!d!....\n",
lpszResourceId,
dwStatus);
goto FnExit;
}
dwStatus = OmSetObjectName( pResource, lpszNewName );
if ( dwStatus != ERROR_SUCCESS )
{
ClRtlLogPrint(LOG_CRITICAL,
"[FM] FmpUpdateChangeResourceName: Unable to set name <%3!ws!> for resource <%1!ws!>, Status =%2!d!....\n",
lpszResourceId,
dwStatus,
lpszNewName );
LocalFree( pResChangeName );
goto FnExit;
}
pResChangeName->pResource = pResource;
lstrcpyW( pResChangeName->szNewResourceName, lpszNewName );
//
// The FM worker thread will free the memory for the pResChangeName
// structure as well as dereference the pResource object.
//
FmpPostWorkItem( FM_EVENT_RESOURCE_NAME_CHANGE, pResChangeName, 0 );
pResource = NULL;
ClRtlLogPrint(LOG_NOISE,
"[FM] FmpUpdateChangeResourceName: Successfully changed name of resource <%1!ws!> to <%2!ws!>...\n",
lpszResourceId,
lpszNewName);
FnExit:
if ( pResource != NULL )
{
OmDereferenceObject( pResource );
}
if ( hKey != NULL )
{
DmCloseKey( hKey );
}
if ( ( dwStatus == ERROR_SUCCESS ) && ( hXsaction ) )
{
DmCommitLocalUpdate( hXsaction );
}
else
{
if ( hXsaction ) DmAbortLocalUpdate( hXsaction );
}
ClRtlLogPrint(LOG_NOISE,
"[FM] FmpUpdateChangeResourceName: Exit for resource %1!ws!, Status=%2!u!...\n",
lpszResourceId,
dwStatus);
return( dwStatus );
}
/****
@func DWORD | FmpUpdatePossibleNodesForResType| This update is called to
update the possible nodes for a resource type.
@parm IN BOOL | SourceNode | set to TRUE, if the update originated at this
node.
@parm IN LPCWSTR | lpszResTypeName | The name of the resource type.
@parm IN DWORD | dwBufLength | The size of the multi-sz string pointed
to by pBuf
@parm IN PVOID | pBuf | A pointer to the buffer containing the names of
the nodes that support this resource type.
@comm The possible list of nodes that supports the given resource type is
updated with the list provided.
@rdesc Returns a result code. ERROR_SUCCESS on success.
@xref <f FmpDecisionPossibleDmSwitchToNewQuorumLog>
****/
DWORD
FmpUpdatePossibleNodeForResType(
IN BOOL SourceNode,
IN LPCWSTR lpszResTypeName,
IN LPDWORD pdwBufLength,
IN PVOID pBuf
)
{
PFM_RESTYPE pResType;
DWORD dwStatus;
HDMKEY hResTypeKey = NULL;
HLOCALXSACTION hXsaction = NULL;
LIST_ENTRY NewPosNodeList;
PLIST_ENTRY pListEntry;
PRESTYPE_POSSIBLE_ENTRY pResTypePosEntry = NULL;
//
// Chittur Subbaraman (chitturs) - 5/13/99
//
// Don't check for FmpFMGroupsInited condition since this GUM
// handler is called by the forming node before that variable
// is set to TRUE. This update always comes after the
// corresponding restypes have been created and is made
// internally by the clussvc following this order. Note that
// a joining node cannot receive this update until groups are
// inited since GUM receive updates are turned on only after
// the FmpFMGroupsInited variable is set to TRUE. Also, the
// intracluster RPC is fired up in a forming node only after
// the groups are inited. Hence, there is no major danger
// of this GUM handler being called if the corresponding
// restype is not created.
//
if ( FmpShutdown ) {
return(ERROR_SUCCESS);
}
InitializeListHead(&NewPosNodeList);
pResType = OmReferenceObjectById( ObjectTypeResType,
lpszResTypeName);
if (!pResType)
{
dwStatus = ERROR_CLUSTER_RESOURCE_TYPE_NOT_FOUND;
goto FnExit;
}
dwStatus = FmpAddPossibleNodeToList(pBuf, *pdwBufLength, &NewPosNodeList);
if (dwStatus != ERROR_SUCCESS)
{
goto FnExit;
}
//writes to the old log file
hXsaction = DmBeginLocalUpdate();
if (!hXsaction)
{
dwStatus = GetLastError();
goto FnExit;
}
hResTypeKey = DmOpenKey(DmResourceTypesKey,
lpszResTypeName,
KEY_READ | KEY_WRITE);
if (hResTypeKey == NULL)
{
dwStatus = GetLastError();
goto FnExit;
}
//if there are no possible owners, delete the value
if (pBuf && *pdwBufLength)
{
dwStatus = DmLocalSetValue( hXsaction,
hResTypeKey,
CLUSREG_NAME_RESTYPE_POSSIBLE_NODES,
REG_MULTI_SZ,
(LPBYTE)pBuf,
*pdwBufLength);
}
else
{
dwStatus = DmLocalDeleteValue( hXsaction,
hResTypeKey,
CLUSREG_NAME_RESTYPE_POSSIBLE_NODES);
if (dwStatus == ERROR_FILE_NOT_FOUND)
{
dwStatus = ERROR_SUCCESS;
}
}
FnExit:
if (dwStatus == ERROR_SUCCESS)
{
//commit the update on the old log file,
//any nodes that were done, will get this change
//I cant delete this file
DmCommitLocalUpdate(hXsaction);
ACQUIRE_EXCLUSIVE_LOCK(gResTypeLock);
//free the old list
while (!IsListEmpty(&pResType->PossibleNodeList))
{
pListEntry = RemoveHeadList(&pResType->PossibleNodeList);
pResTypePosEntry = CONTAINING_RECORD(pListEntry, RESTYPE_POSSIBLE_ENTRY,
PossibleLinkage);
OmDereferenceObject(pResTypePosEntry->PossibleNode);
LocalFree(pResTypePosEntry);
}
//now switch the possible owners list for the
//resource type
while (!IsListEmpty(&(NewPosNodeList)))
{
//remove from the new prepared list and hang
//it of the restype structure
pListEntry = RemoveHeadList(&NewPosNodeList);
InsertTailList(&pResType->PossibleNodeList, pListEntry);
pResTypePosEntry = CONTAINING_RECORD(pListEntry, RESTYPE_POSSIBLE_ENTRY,
PossibleLinkage);
ClRtlLogPrint(LOG_NOISE,
"[FM] FmpUpdatePossibleNodesForRestype:Adding node %1!ws! to %2!ws! resource type's possible node list...\n",
OmObjectId(pResTypePosEntry->PossibleNode),
lpszResTypeName);
}
RELEASE_LOCK(gResTypeLock);
ClusterEvent( CLUSTER_EVENT_RESTYPE_PROPERTY_CHANGE,
pResType );
}
else
{
//free up the NewPostNodeList
if (hXsaction) DmAbortLocalUpdate(hXsaction);
//if a new list was prepared, free it
while (!IsListEmpty(&(NewPosNodeList)))
{
pListEntry = RemoveHeadList(&NewPosNodeList);
pResTypePosEntry = CONTAINING_RECORD(pListEntry, RESTYPE_POSSIBLE_ENTRY,
PossibleLinkage);
OmDereferenceObject(pResTypePosEntry->PossibleNode);
LocalFree(pResTypePosEntry);
}
}
if (hResTypeKey) DmCloseKey(hResTypeKey);
if (pResType) OmDereferenceObject(pResType);
return(dwStatus);
}
/****
@func DWORD | FmpDecidePossibleNodeForResType| When the quorum resource is changed,
the FM invokes this api on the owner node of the new quorum resource
to create a new quorum log file.
@parm IN PVOID | pResource | The new quorum resource.
@parm IN LPCWSTR | lpszPath | The path for temporary cluster files.
@parm IN DWORD | dwMaxQuoLogSize | The maximum size limit for the quorum log file.
@comm When a quorum resource is changed, the fm calls this funtion before it
updates the quorum resource. If a new log file needs to be created,
a checkpoint is taken.
@rdesc Returns a result code. ERROR_SUCCESS on success.
@xref <f DmSwitchToNewQuorumLog>
****/
DWORD FmpDecidePossibleNodeForResType
(
IN PGUM_VOTE_DECISION_CONTEXT pDecisionContext,
IN DWORD dwVoteBufLength,
IN PVOID pVoteBuf,
IN DWORD dwNumVotes,
IN BOOL bDidAllActiveNodesVote,
OUT LPDWORD pdwOutputBufSize,
OUT PVOID *ppOutputBuf
)
{
DWORD dwStatus = ERROR_SUCCESS;
DWORD i;
PFMP_VOTE_POSSIBLE_NODE_FOR_RESTYPE pFmpVote;
LPWSTR lpmszPossibleNodes = NULL;
DWORD dwlpmszLen = 0;
PVOID pGumBuffer = NULL;
DWORD dwNodeId;
WCHAR szNodeId[NODE_ID_SZ];
LPWSTR lpmszCurrentPossibleNodes=NULL;
BOOL bChange = FALSE;
HDMKEY hResTypeKey = NULL;
DWORD dwSize;
DWORD dwStringBufSize = 0;
BOOL bAssumeSupported;
LPWSTR TypeName = NULL;
//First get the type name from pDecisionContext
TypeName=(LPWSTR)LocalAlloc(LMEM_FIXED,pDecisionContext->dwInputBufLength);
if(TypeName==NULL)
{
ClRtlLogPrint(LOG_CRITICAL,"[FM] FmpDecidePossibleNodeForResType: Not Enough Memory, error= %1!d!\r\n",
GetLastError());
goto FnExit;
}
CopyMemory(TypeName,pDecisionContext->pInputBuf,pDecisionContext->dwInputBufLength);
//initialize the out params
*ppOutputBuf = NULL;
*pdwOutputBufSize = 0;
bAssumeSupported= *((BOOL*)pDecisionContext->pContext);
if (bAssumeSupported)
{
hResTypeKey = DmOpenKey(DmResourceTypesKey,
TypeName,
KEY_READ | KEY_WRITE);
if (hResTypeKey == NULL)
{
dwStatus = GetLastError();
CL_LOGFAILURE(dwStatus);
goto FnExit;
}
//pass the current possible node list to the decider
dwStatus = DmQueryString(hResTypeKey,
CLUSREG_NAME_RESTYPE_POSSIBLE_NODES,
REG_MULTI_SZ,
&lpmszCurrentPossibleNodes,
&dwStringBufSize,
&dwSize);
if (dwStatus != ERROR_SUCCESS)
{
//if the possible node list is not found this is ok
//ie. only if there is some other error we give up
if ( dwStatus != ERROR_FILE_NOT_FOUND )
{
CL_LOGFAILURE(dwStatus);
goto FnExit;
}
}
DmCloseKey(hResTypeKey);
hResTypeKey = NULL;
}
//if the current list is passed in, dont remove any possible
//nodes from the list if they dont vote, simply add the new ones
if (lpmszCurrentPossibleNodes)
{
DWORD dwStrLen;
//make a copy of the multi-sz
dwlpmszLen = ClRtlMultiSzLength(lpmszCurrentPossibleNodes);
dwStrLen = dwlpmszLen * sizeof(WCHAR);
lpmszPossibleNodes = LocalAlloc(LMEM_FIXED, dwStrLen);
if (!lpmszPossibleNodes)
{
dwStatus = ERROR_NOT_ENOUGH_MEMORY;
CL_LOGFAILURE(dwStatus);
goto FnExit;
}
CopyMemory(lpmszPossibleNodes, lpmszCurrentPossibleNodes, dwStrLen);
}
for (i = 0; i< dwNumVotes; i++)
{
pFmpVote = (PFMP_VOTE_POSSIBLE_NODE_FOR_RESTYPE)
GETVOTEFROMBUF(pVoteBuf, pDecisionContext->dwVoteLength, i+1 , &dwNodeId);
//if not a valid vote, skip
if (!pFmpVote)
continue;
CL_ASSERT((PBYTE)pFmpVote <= ((PBYTE)pVoteBuf + dwVoteBufLength -
sizeof(FMP_VOTE_POSSIBLE_NODE_FOR_RESTYPE)));
szNodeId[ NODE_ID_SZ-1 ] = UNICODE_NULL;
_snwprintf( szNodeId, NODE_ID_SZ-1, L"%d" , dwNodeId );
if (pFmpVote->bPossibleNode)
{
if (lpmszCurrentPossibleNodes)
{
//if the string is already there, dont append it again
if (ClRtlMultiSzScan(lpmszCurrentPossibleNodes, szNodeId))
continue;
}
dwStatus = ClRtlMultiSzAppend(&lpmszPossibleNodes,
&dwlpmszLen, szNodeId);
bChange = TRUE;
if (dwStatus != ERROR_SUCCESS)
goto FnExit;
}
else
{
//if a current list was specified
//this node is not a possible node anymore, remove it from the list
if (lpmszCurrentPossibleNodes)
{
ClRtlLogPrint(LOG_NOISE,
"[FM] FmpDecidePossibleNodesForRestype: Removing node %1!ws! from %2!ws! restype possibleowner list \r\n",
szNodeId,TypeName);
dwStatus = ClRtlMultiSzRemove(lpmszPossibleNodes, &dwlpmszLen, szNodeId);
if (dwStatus == ERROR_SUCCESS)
{
//if the node is successfully removed
bChange = TRUE;
}
else if (dwStatus != ERROR_FILE_NOT_FOUND)
{
//if the node exists but cannot be removed return with error
//if the node didnt exist, we dont do anything bChange remains
//set at FALSE
goto FnExit;
}
else
{
dwStatus = ERROR_SUCCESS;
}
}
}
}
//if nothing has changed dont issue a gum update
if (!bChange)
{
dwStatus = ERROR_ALREADY_EXISTS;
goto FnExit;
}
//dwlpmszLen contains the size of the multi-sz string in the
//number of characters, make it the number of bytes
dwlpmszLen *= sizeof(WCHAR);
pGumBuffer = GumMarshallArgs(pdwOutputBufSize, 3,
pDecisionContext->dwInputBufLength, pDecisionContext->pInputBuf,
sizeof(DWORD), &dwlpmszLen, dwlpmszLen, lpmszPossibleNodes);
*ppOutputBuf = pGumBuffer;
FnExit:
if (lpmszPossibleNodes) LocalFree(lpmszPossibleNodes);
if (hResTypeKey)
DmCloseKey(hResTypeKey);
if (lpmszCurrentPossibleNodes)
LocalFree(lpmszCurrentPossibleNodes);
if(TypeName)
LocalFree(TypeName);
return(dwStatus);
}
/****
@func DWORD | FmpUpdateChangeResourceNode| This update is called to
update the possible nodes for a resource.
@parm IN BOOL | SourceNode | set to TRUE, if the update originated at this
node.
@parm IN PFM_RESOURCE | pResource | A pointer to the resource whose
possible node list is being updated.
@parm IN PNM_NODE | pNode | A pointer to the node to be added/removed
from the possible node lis.
@parm IN DWORD | dwControlCode | If CLUSCTL_RESOURCE_ADD_OWNER then
the node is added to the possible node list, else it is removed.
@comm The possible list of nodes for a resource is updated.
@rdesc Returns a result code. ERROR_SUCCESS on success.
****/
DWORD
FmpUpdateChangeResourceNode(
IN BOOL SourceNode,
IN PFM_RESOURCE pResource,
IN PNM_NODE pNode,
IN DWORD dwControlCode
)
{
DWORD dwStatus;
HDMKEY hResKey = NULL;
HLOCALXSACTION hXsaction = NULL;
//Dont acquire the local resource lock since acquiring that
//within gum updates causes deadlock
//use the global resource lock to synchronize this call
//with the enumeration of possible nodes
FmpAcquireResourceLock();
//start a transaction
hXsaction = DmBeginLocalUpdate();
if (!hXsaction)
{
dwStatus = GetLastError();
goto FnExit;
}
//
// BUGBUG: What if in-memory stuff succeeds and registry stuff fails ? We get into
// inconsistent state. Better to move this code to the end after succeeding in
// registry changes.
//
if (dwControlCode == CLUSCTL_RESOURCE_ADD_OWNER)
{
dwStatus = FmpAddPossibleNode(pResource,
pNode);
} else
{
dwStatus = FmpRemovePossibleNode(pResource,
pNode,
FALSE);
}
if (dwStatus != ERROR_SUCCESS)
{
ClRtlLogPrint( LOG_NOISE,
"[FM] FmpUpdateChangeResourceNode, failed possible node updatefor resource <%1!ws!>, error %2!u!\n",
OmObjectName(pResource),
dwStatus );
goto FnExit;
}
//fix the registry
//SS - do we need to fix the preferred node list
hResKey = DmOpenKey(DmResourcesKey,
OmObjectId(pResource),
KEY_READ | KEY_WRITE);
if (hResKey == NULL)
{
dwStatus = GetLastError();
goto FnExit;
}
if (dwControlCode == CLUSCTL_RESOURCE_ADD_OWNER)
{
dwStatus = DmLocalAppendToMultiSz(
hXsaction,
hResKey,
CLUSREG_NAME_RES_POSSIBLE_OWNERS,
OmObjectId(pNode));
}
else
{
dwStatus = DmLocalRemoveFromMultiSz(
hXsaction,
hResKey,
CLUSREG_NAME_RES_POSSIBLE_OWNERS,
OmObjectId(pNode));
if (dwStatus == ERROR_FILE_NOT_FOUND)
{
DWORD i;
DWORD Result;
PNM_NODE pEnumNode;
//
// Possible nodes did not exist, so create a new entry
// with every possible node in it. FM will already have
// removed the passed in node from the possible node list.
//
i=0;
do {
Result = FmEnumResourceNode(pResource,
i,
&pEnumNode);
if (Result == ERROR_SUCCESS)
{
dwStatus = DmLocalAppendToMultiSz(
hXsaction,
hResKey,
CLUSREG_NAME_RES_POSSIBLE_OWNERS,
OmObjectId(pEnumNode));
OmDereferenceObject(pEnumNode);
}
else if ((Result == ERROR_NO_MORE_ITEMS) &&
(i == 0))
{
//
// This is a funny corner case where there is a one
// node cluster and a resource with no possibleowners
// entry, and somebody removes the only node in the cluster
// from the possible owners list. Set PossibleOwners to
// the empty set.
//
dwStatus = DmLocalSetValue(
hXsaction,
hResKey,
CLUSREG_NAME_RES_POSSIBLE_OWNERS,
REG_MULTI_SZ,
(CONST BYTE *)L"\0",
2);
}
++i;
} while ( Result == ERROR_SUCCESS );
//map the error to success
dwStatus = ERROR_SUCCESS;
}
}
DmCloseKey(hResKey);
FnExit:
//release the lock
FmpReleaseResourceLock();
if (dwStatus == ERROR_SUCCESS)
{
//commit the update on the old log file,
//any nodes that were done, will get this change
//I cant delete this file
DmCommitLocalUpdate(hXsaction);
}
else
{
//SS: BUGBUG :: validation for possible node should
//be done before the registry is switched
//the inmemory structure should be changed only on success
//if there is a failure in the registry apis..the
//in memory structure will be out of sync with registry
if (hXsaction) DmAbortLocalUpdate(hXsaction);
}
return(dwStatus);
}
/****
@func DWORD | FmpUpdateChangeResourceGroup| This update is called to
update the group to which the resource belongs.
@parm IN BOOL | bSourceNode | set to TRUE, if the update originated at this
node.
@parm IN PFM_RESOURCE | pResource | A pointer to the resource whose
possible node list is being updated.
@parm IN PFM_GROUP | pNewGroup | A pointer to the node to be added/removed
from the possible node lis.
@comm The possible list of nodes for a resource is updated.
@rdesc Returns a result code. ERROR_SUCCESS on success.
****/
DWORD FmpUpdateChangeResourceGroup(
IN BOOL bSourceNode,
IN PFM_RESOURCE pResource,
IN PFM_GROUP pNewGroup)
{
DWORD dwStatus = ERROR_SUCCESS;
PFM_GROUP pOldGroup;
PFM_DEPENDENCY_TREE pTree = NULL;
HLOCALXSACTION hXsaction = NULL;
HDMKEY hOldGroupKey = NULL;
HDMKEY hNewGroupKey = NULL;
PLIST_ENTRY pListEntry;
PFM_DEPENDTREE_ENTRY pEntry;
pOldGroup = pResource->Group;
//
// Check to make sure the resource is not already in the group.
//
if (pOldGroup == pNewGroup)
{
dwStatus = ERROR_ALREADY_EXISTS;
goto FnExit;
}
//
// Synchronize both the old and the new groups.
// Lock the lowest by lowest Group Id first - to prevent deadlocks!
// Note - the order of release is unimportant.
//
// strictly, the comparison below cannot be equal!
//
if ( lstrcmpiW( OmObjectId( pOldGroup ), OmObjectId( pNewGroup ) ) <= 0 )
{
FmpAcquireLocalGroupLock( pOldGroup );
FmpAcquireLocalGroupLock( pNewGroup );
}
else
{
FmpAcquireLocalGroupLock( pNewGroup );
FmpAcquireLocalGroupLock( pOldGroup );
}
//start a transaction
hXsaction = DmBeginLocalUpdate();
if (!hXsaction)
{
dwStatus = GetLastError();
goto FnUnlock;
}
//
// For now... both Groups must be owned by the same node.
//
if ( pResource->Group->OwnerNode != pNewGroup->OwnerNode )
{
dwStatus = ERROR_HOST_NODE_NOT_GROUP_OWNER;
goto FnUnlock;
}
//
// Create a full dependency tree,
//
pTree = FmCreateFullDependencyTree(pResource);
if ( pTree == NULL )
{
dwStatus = GetLastError();
goto FnUnlock;
}
//
// Add each resource in the dependency tree to its new group's list.
//
hNewGroupKey = DmOpenKey(DmGroupsKey,
OmObjectId(pNewGroup),
KEY_READ | KEY_WRITE);
if (hNewGroupKey == NULL) {
dwStatus = GetLastError();
goto FnUnlock;
}
hOldGroupKey = DmOpenKey(DmGroupsKey,
OmObjectId(pOldGroup),
KEY_READ | KEY_WRITE);
if (hOldGroupKey == NULL) {
dwStatus = GetLastError();
goto FnUnlock;
}
//
// For each resource in the dependency tree, remove it from the
// old group list and add it to the new group list
//
pListEntry = pTree->ListHead.Flink;
while (pListEntry != &pTree->ListHead) {
pEntry = CONTAINING_RECORD(pListEntry,
FM_DEPENDTREE_ENTRY,
ListEntry);
pListEntry = pListEntry->Flink;
dwStatus = DmLocalRemoveFromMultiSz(hXsaction,
hOldGroupKey,
CLUSREG_NAME_GRP_CONTAINS,
OmObjectId(pEntry->Resource));
if (dwStatus != ERROR_SUCCESS) {
goto FnUnlock;
}
dwStatus = DmLocalAppendToMultiSz(hXsaction,
hNewGroupKey,
CLUSREG_NAME_GRP_CONTAINS,
OmObjectId(pEntry->Resource));
if (dwStatus != ERROR_SUCCESS) {
goto FnUnlock;
}
}
//
// Passed all the checks, do the in-memorymove.
//
pListEntry = pTree->ListHead.Flink;
while (pListEntry != &pTree->ListHead)
{
pEntry = CONTAINING_RECORD(pListEntry,
FM_DEPENDTREE_ENTRY,
ListEntry);
pListEntry = pListEntry->Flink;
//
// Move this resource
//
RemoveEntryList(&pEntry->Resource->ContainsLinkage);
InsertHeadList(&pNewGroup->Contains,
&pEntry->Resource->ContainsLinkage);
OmReferenceObject(pNewGroup);
pEntry->Resource->Group = pNewGroup;
++pEntry->Resource->StateSequence;
ClusterEvent(CLUSTER_EVENT_RESOURCE_CHANGE,pEntry->Resource);
OmDereferenceObject(pOldGroup);
}
FnUnlock:
//
// Now release all locks.
//
FmpReleaseLocalGroupLock( pNewGroup );
FmpReleaseLocalGroupLock( pOldGroup );
FnExit:
if (pTree) FmDestroyFullDependencyTree(pTree);
if (hOldGroupKey) DmCloseKey(hOldGroupKey);
if (hNewGroupKey) DmCloseKey(hNewGroupKey);
if (dwStatus == ERROR_SUCCESS)
{
ClusterEvent(CLUSTER_EVENT_GROUP_PROPERTY_CHANGE,pNewGroup);
ClusterEvent(CLUSTER_EVENT_GROUP_PROPERTY_CHANGE,pOldGroup);
DmCommitLocalUpdate(hXsaction);
}
else
{
if (hXsaction) DmAbortLocalUpdate(hXsaction);
}
return(dwStatus);
}
DWORD
FmpUpdateAddDependency(
IN BOOL SourceNode,
IN LPCWSTR ResourceId,
IN LPCWSTR DependsOnId
)
/*++
Routine Description:
GUM dispatch routine for adding a dependency
Arguments:
SourceNode - Supplies whether or not this node initiated the GUM update.
Not used.
ResourceId - Supplies the resource ID of the resource that should have a
dependency added.
DependsOnId - Supplies the resource ID of the resource that should provide
for ResourceId.
Return Value:
ERROR_SUCCESS if successful.
Win32 error code otherwise.
--*/
{
PFM_RESOURCE Resource;
PFM_RESOURCE DependsOn;
PDEPENDENCY dependency;
//
// Chittur Subbaraman (chitturs) - 4/18/99
//
// If FM groups are not fully initialized or FM is shutting down, then
// don't do anything.
//
if ( !FmpFMGroupsInited ||
FmpShutdown ) {
return(ERROR_SUCCESS);
}
dependency = LocalAlloc(LMEM_FIXED, sizeof(DEPENDENCY));
if (dependency == NULL) {
CsInconsistencyHalt( ERROR_NOT_ENOUGH_MEMORY );
return(ERROR_NOT_ENOUGH_MEMORY);
}
Resource = OmReferenceObjectById(ObjectTypeResource,
ResourceId);
if (Resource == NULL) {
CL_LOGFAILURE( ERROR_RESOURCE_NOT_FOUND );
LocalFree(dependency);
return(ERROR_RESOURCE_NOT_FOUND);
}
DependsOn = OmReferenceObjectById(ObjectTypeResource,
DependsOnId);
if (DependsOn == NULL) {
OmDereferenceObject(Resource);
LocalFree(dependency);
CL_LOGFAILURE( ERROR_DEPENDENCY_NOT_FOUND );
return(ERROR_DEPENDENCY_NOT_FOUND);
}
dependency->DependentResource = Resource;
dependency->ProviderResource = DependsOn;
FmpAcquireResourceLock();
InsertTailList( &DependsOn->ProvidesFor,
&dependency->ProviderLinkage );
InsertTailList( &Resource->DependsOn,
&dependency->DependentLinkage );
FmpReleaseResourceLock();
ClusterEvent( CLUSTER_EVENT_RESOURCE_PROPERTY_CHANGE,
Resource );
//SS: we leave the reference counts on both the objects
//as a dependency referrring to them has been created.
return(ERROR_SUCCESS);
} // FmpUpdateAddDependency
DWORD
FmpUpdateRemoveDependency(
IN BOOL SourceNode,
IN LPCWSTR ResourceId,
IN LPCWSTR DependsOnId
)
/*++
Routine Description:
GUM dispatch routine for adding a dependency
Arguments:
SourceNode - Supplies whether or not this node initiated the GUM update.
Not used.
ResourceId - Supplies the resource ID of the resource that should have a
dependency removed.
DependsOnId - Supplies the resource ID of the resource that provides
for ResourceId.
Return Value:
ERROR_SUCCESS if successful.
Win32 error code otherwise.
--*/
{
PFM_RESOURCE Resource;
PFM_RESOURCE DependsOn;
PDEPENDENCY dependency;
PLIST_ENTRY ListEntry;
DWORD Status=ERROR_SUCCESS;
//
// Chittur Subbaraman (chitturs) - 4/18/99
//
// If FM groups are not fully initialized or FM is shutting down, then
// don't do anything.
//
if ( !FmpFMGroupsInited ||
FmpShutdown ) {
return(ERROR_SUCCESS);
}
Resource = OmReferenceObjectById(ObjectTypeResource,
ResourceId);
if (Resource == NULL) {
CL_LOGFAILURE( ERROR_RESOURCE_NOT_FOUND );
return(ERROR_RESOURCE_NOT_FOUND);
}
DependsOn = OmReferenceObjectById(ObjectTypeResource,
DependsOnId);
if (DependsOn == NULL) {
OmDereferenceObject(Resource);
CL_LOGFAILURE( ERROR_RESOURCE_NOT_FOUND );
return(ERROR_RESOURCE_NOT_FOUND);
}
//
// Walk through the dependency list of the resource searching
// for a match.
//
FmpAcquireResourceLock();
ListEntry = Resource->DependsOn.Flink;
while (ListEntry != &Resource->DependsOn) {
dependency = CONTAINING_RECORD(ListEntry,
DEPENDENCY,
DependentLinkage);
CL_ASSERT(dependency->DependentResource == Resource);
if (dependency->ProviderResource == DependsOn) {
//
// Found a match. Remove it from its list and
// free it up.
//
RemoveEntryList(&dependency->ProviderLinkage);
RemoveEntryList(&dependency->DependentLinkage);
// dereference the providor and dependent resource
OmDereferenceObject(dependency->DependentResource);
OmDereferenceObject(dependency->ProviderResource);
LocalFree(dependency);
break;
}
ListEntry = ListEntry->Flink;
}
FmpReleaseResourceLock();
if (ListEntry != &Resource->DependsOn) {
//
// A match was found. Dereference the provider resource
// to account for the dependency removal and return success.
//
ClusterEvent( CLUSTER_EVENT_RESOURCE_PROPERTY_CHANGE,
Resource );
Status = ERROR_SUCCESS;
} else {
Status = ERROR_DEPENDENCY_NOT_FOUND;
}
//SS: dereference the objects earlier referenced
OmDereferenceObject(Resource);
OmDereferenceObject(DependsOn);
return(Status);
} // FmpUpdateRemoveDependency
DWORD
FmpUpdateDeleteGroup(
IN BOOL SourceNode,
IN LPCWSTR GroupId
)
/*++
Routine Description:
GUM dispatch routine for deleting a group.
Arguments:
SourceNode - Supplies whether or not this node initiated the GUM update.
Not used.
GroupId - Supplies the group ID.
Return Value:
ERROR_SUCCESS if successful.
Win32 error code otherwise.
--*/
{
DWORD dwStatus = ERROR_SUCCESS;
PFM_GROUP pGroup = NULL;
PLIST_ENTRY listEntry;
PPREFERRED_ENTRY preferredEntry;
BOOL bLocked = FALSE;
//
// Chittur Subbaraman (chitturs) - 4/18/99
//
// If FM groups are not fully initialized or FM is shutting down, then
// don't do anything.
//
if ( !FmpFMGroupsInited ||
FmpShutdown ) {
return(ERROR_SUCCESS);
}
//
// Find the specified Group.
//
pGroup = OmReferenceObjectById( ObjectTypeGroup,
GroupId );
if ( pGroup == NULL ) {
dwStatus = ERROR_GROUP_NOT_FOUND;
return(dwStatus);
}
ClRtlLogPrint(LOG_NOISE,
"[FM] DeleteGroup %1!ws!, address = %2!lx!.\n",
OmObjectId(pGroup),
pGroup );
//
// Chittur Subbaraman (chitturs) - 1/12/99
//
// Try to acquire lock, and make sure the Contains list is empty.
//
// Most of the calls to manipulate groups make calls to the owner
// node of the group and this operation is serialized by GUM. So,
// there is no major danger if we do the operations in this function
// without holding the group lock. However, we can't rule out
// corruption 100% as of now.
//
// If you block within the GUM handler here, then no events in
// the cluster proceed forward and things come to a grinding halt.
//
// A case in point:
// (1) Thread 1 (the thread that calls this function) grabs the
// GUM lock and waits for the group lock.
// (2) Thread 2 (FmWorkerThread) grabs the group lock and calls
// resmon attempting to close a resource. It gets blocked on
// the resmon eventlist lock.
// (3) Thread 3 calls RmResourceControl to set the resource name
// which grabs the resmon eventlist lock and then in turn calls
// ClusterRegSetValue and then gets blocked on the GUM lock.
//
FmpTryAcquireLocalGroupLock( pGroup, bLocked );
if ( !IsListEmpty( &pGroup->Contains ) )
{
dwStatus = ERROR_DIR_NOT_EMPTY;
goto FnExit;
}
//
// Close the Group's registry key.
//
DmRundownList( &pGroup->DmRundownList );
if ( pGroup->RegistryKey != NULL ) {
DmCloseKey( pGroup->RegistryKey );
pGroup->RegistryKey = NULL;
}
//
// Remove from the node list
//
dwStatus = OmRemoveObject( pGroup );
ClusterEvent( CLUSTER_EVENT_GROUP_DELETED, pGroup );
//
// This dereference would normally cause the group to eventually disappear,
// however the event notification above will keep a ref on the object
// until all notifications have been delivered.
//
OmDereferenceObject( pGroup );
//
// Make sure the preferred owners list is drained.
//
while ( !IsListEmpty( &pGroup->PreferredOwners ) ) {
listEntry = RemoveHeadList(&pGroup->PreferredOwners);
preferredEntry = CONTAINING_RECORD( listEntry,
PREFERRED_ENTRY,
PreferredLinkage );
OmDereferenceObject( preferredEntry->PreferredNode );
LocalFree( preferredEntry );
}
//
// Free the string associated with the AntiAffinityClassName field.
//
LocalFree ( pGroup->lpszAntiAffinityClassName );
pGroup->dwStructState |= FM_GROUP_STRUCT_MARKED_FOR_DELETE;
FnExit:
if( bLocked )
{
FmpReleaseLocalGroupLock( pGroup );
}
//
// Dereference for reference above.
//
if (pGroup) OmDereferenceObject( pGroup );
return(dwStatus);
} // FmpUpdateDeleteGroup
/****
@func DWORD | FmpUpdateGroupIntendedOwner| This update is called on
a move just before the source node requests the target node
to take over the group.
@parm IN BOOL | bSourceNode | set to TRUE, if the update originated at
this
node.
@parm IN PFM_GROUP | pszGroupId | The ID of the group that is about
to move.
@parm IN PDWORD | pdwNodeId| A pointer to a DWORD that contains the
ID of the node that is the destination of this move. It is
set to ClusterInvalidNodeId by the destination node when it has
accepted the group.
@comm The purpose of this update is to let all nodes know that a move
is impending. If the source node dies while a move is in progress
then preference is given to the target of the move rather than the
node that is chosen by the FmpUpdateAssignOwnerToGroups
@rdesc Returns a result code. ERROR_SUCCESS on success.
****/
DWORD
FmpUpdateGroupIntendedOwner(
IN BOOL SourceNode,
IN LPCWSTR pszGroupId,
IN PDWORD pdwNodeId
)
{
PFM_GROUP pGroup = NULL;
DWORD dwStatus = ERROR_SUCCESS;
PNM_NODE pNode = NULL;
PNM_NODE pPrevNode;
WCHAR pszNodeId[ NODE_ID_SZ ];
if ( !FmpFMGroupsInited )
{
return(ERROR_SUCCESS);
}
pGroup = OmReferenceObjectById( ObjectTypeGroup,
pszGroupId );
if (pGroup == NULL)
{
dwStatus = ERROR_GROUP_NOT_FOUND;
goto FnExit;
}
if (*pdwNodeId != ClusterInvalidNodeId)
{
pszNodeId [ NODE_ID_SZ - 1 ] = UNICODE_NULL;
_snwprintf(pszNodeId, NODE_ID_SZ-1, L"%u", *pdwNodeId);
pNode = OmReferenceObjectById(ObjectTypeNode,
pszNodeId);
if (pNode == NULL)
{
dwStatus = ERROR_CLUSTER_NODE_NOT_FOUND;
goto FnExit;
}
} else if (pGroup->pIntendedOwner == NULL)
{
dwStatus = ERROR_CLUSTER_INVALID_NODE;
ClRtlLogPrint(LOG_NOISE,
"[FM] FmpUpdateGroupIntendedOwner: Group <%1!ws!> intended owner is already invalid, not setting....\n",
pszGroupId);
goto FnExit;
}
//
// HACKHACK: Chittur Subbaraman (chitturs) - 5/20/99
// Comment out as a temporary solution to avoid deadlocks.
//
// FmpAcquireLocalGroupLock(pGroup);
pPrevNode = pGroup->pIntendedOwner;
//set the new owner node, incr ref count
if (pNode) OmReferenceObject(pNode);
pGroup->pIntendedOwner = pNode;
//decr ref count on previous owner
if (pPrevNode) OmDereferenceObject(pPrevNode);
//
// HACKHACK: Chittur Subbaraman (chitturs) - 5/20/99
// Comment out as a temporary solution to avoid deadlocks.
//
// FmpReleaseLocalGroupLock(pGroup);
FnExit:
if (pGroup) OmDereferenceObject(pGroup);
if (pNode) OmDereferenceObject(pNode);
return(dwStatus);
}
/****
@func DWORD | FmpUpdateAssignOwnerToGroups| This update is made when
a node goes down to take ownership of all the orphaned groups.
@parm IN BOOL | bSourceNode | set to TRUE, if the update originated at
this
node.
@parm IN LPCWSTR | pszGroupId | The ID of the group that is about
to move.
@parm IN PDWORD | pdwNodeId| A pointer to a DWORD that contains the
ID of the node that is the destination of this move. It is
set to ClusterInvalidNodeId by the destination node when it has
accepted the group.
@comm The purpose of this update is to let all nodes know that a move
is impending. If the source node dies while a move is in progress
,
then preference is given to the target of the move rather than the
node that is chosen by the FmpClaimNodeGroups algorithm.
@rdesc returns ERROR_SUCCESS.
****/
DWORD
FmpUpdateAssignOwnerToGroups(
IN BOOL SourceNode,
IN LPCWSTR pszNodeId
)
{
PNM_NODE pNode = NULL;
DWORD dwStatus = ERROR_SUCCESS;
DWORD dwNodeId;
//
// Chittur Subbaraman (chitturs) - 4/18/99
//
// If FM groups are not fully initialized or FM is shutting down, then
// don't do anything.
//
if ( !FmpFMGroupsInited || FmpShutdown )
{
return(ERROR_SUCCESS);
}
//
// In a node evict, the NM GUM handler gets rids of the dead node from the OM list.
// This node down FM GUM handler could follow the NM evict GUM handler since NM
// lets an evict through once *it* declares a node as down. At that time, there is no
// guarantee that the FM node down GUM has executed since that GUM is issued in the
// async phase of node down processing and could very well follow the NM evict GUM
// handler. Thus, this GUM handler and associated functions CANNOT call OM to get
// a node object from the node ID string. Thus, this GUM handler and associated
// functions are carefully written to work with a node ID as opposed to a node object.
//
dwNodeId = wcstoul( pszNodeId, NULL, 10 );
//if this update has already been seen after the node down
//ignore this one
if (gFmpNodeArray[dwNodeId].dwNodeDownProcessingInProgress == 0)
{
ClRtlLogPrint(LOG_NOISE,
"[FM] FmpUpdateAssignOwnersToGroups, %1!ws! node down has been processed already\n",
pszNodeId);
goto FnExit;
}
//
// Assign ownership to all groups owned by the dead node
//
dwStatus = FmpAssignOwnersToGroups(pszNodeId, NULL, NULL);
if (dwStatus != ERROR_SUCCESS)
{
ClRtlLogPrint(LOG_CRITICAL,
"[FM] FmpUpdateAssignOwnersToGroups failed %1!d!\n",
dwStatus);
}
//mark that the node down processing has been done
gFmpNodeArray[dwNodeId].dwNodeDownProcessingInProgress = 0;
FnExit:
return(dwStatus);
}
/****
@func DWORD | FmpUpdateApproveJoin| The joining node
makes this update call.
@parm IN BOOL | bSourceNode | set to TRUE, if the update originated at
this
node.
@parm IN LPCWSTR | pszGroupId | The ID of the group that is about
to move.
@parm IN PDWORD | pdwNodeId| A pointer to a DWORD that contains the
ID of the node that is the destination of this move. It is
set to ClusterInvalidNodeId by the destination node when it has
accepted the group.
@comm The purpose of this update is to let all nodes know that a move
is impending. If the source node dies while a move is in progress
,
then preference is given to the target of the move rather than the
node that is chosen by the FmpClaimNodeGroups algorithm.
@rdesc returns ERROR_SUCCESS.
****/
DWORD
FmpUpdateApproveJoin(
IN BOOL SourceNode,
IN LPCWSTR pszNodeId
)
{
PNM_NODE pNode = NULL;
DWORD dwStatus = ERROR_SUCCESS;
//
// Chittur Subbaraman (chitturs) - 4/18/99
//
// If FM groups are not fully initialized or FM is shutting down, then
// don't do anything.
//
if ( !FmpFMGroupsInited || FmpShutdown )
{
return(ERROR_SUCCESS);
}
pNode = OmReferenceObjectById( ObjectTypeNode,
pszNodeId );
if (!pNode)
{
ClRtlLogPrint(LOG_CRITICAL,
"[FM] FmpUpdateAssignOwnersToGroups, %1!ws! node not found\n",
pszNodeId);
//should we return failure here
//is evict of a node synchronized with everything
goto FnExit;
}
if (pNode == NmLocalNode)
{
// SS: can I become the locker now
// If so, there what do I do
//i approve of my own join
goto FnExit;
}
//if a node is trying to join before the processing
//for its last death has been completed, ask it to retry
if (gFmpNodeArray[NmGetNodeId(pNode)].dwNodeDownProcessingInProgress == 1)
{
ClRtlLogPrint(LOG_CRITICAL,
"[FM] FmpUpdateApproveJoin, %1!ws! node down hasnt been processed as yet\n",
pszNodeId);
dwStatus = ERROR_RETRY;
goto FnExit;
}
FnExit:
if (pNode) OmDereferenceObject(pNode);
return(dwStatus);
}
/****
@func DWORD | FmpUpdateCreateGroup | GUM update handler for creating
a group.
@parm IN OUT PGUM_CREATE_GROUP | pGumGroup | Buffer containing group info
@parm IN BOOL | bSourceNode | Indicates whether this call originated
from this node.
@comm This GUM update creates a group and is structured as a local
transaction so that both registry entries and in-memory
structures are updated consistently.
@rdesc Returns ERROR_SUCCESS on success. A Win32 error code otherwise.
****/
DWORD
FmpUpdateCreateGroup(
IN OUT PGUM_CREATE_GROUP pGumGroup,
IN BOOL bSourceNode
)
{
DWORD dwStatus = ERROR_SUCCESS;
HDMKEY hKey = NULL;
DWORD dwDisposition;
HLOCALXSACTION
hXsaction = NULL;
LPCWSTR lpszNodeId = NULL;
PNM_NODE pNode = NULL;
DWORD dwGroupIdLen = 0;
DWORD dwGroupNameLen = 0;
LPWSTR lpszGroupId = NULL;
LPCWSTR lpszGroupName = NULL;
BOOL bLocked = FALSE;
//
// Chittur Subbaraman (chitturs) - 5/27/99
//
// Restructure this GUM update as a local transaction.
//
dwGroupIdLen = pGumGroup->GroupIdLen;
dwGroupNameLen = pGumGroup->GroupNameLen;
lpszGroupId = pGumGroup->GroupId;
lpszGroupName = (PWSTR)((PCHAR)lpszGroupId +
dwGroupIdLen );
ClRtlLogPrint(LOG_NOISE,
"[FM] FmpUpdateCreateGroup: Entry for group %1!ws!...\n",
lpszGroupId);
//
// Start a transaction
//
hXsaction = DmBeginLocalUpdate();
if ( !hXsaction )
{
dwStatus = GetLastError();
ClRtlLogPrint(LOG_UNUSUAL,
"[FM] FmpUpdateCreateGroup, Failed in starting a transaction for group %1!ws!, Status =%2!d!....\n",
lpszGroupId,
dwStatus);
return( dwStatus );
}
//
// Create the new group key.
//
hKey = DmLocalCreateKey( hXsaction,
DmGroupsKey,
lpszGroupId,
0,
KEY_READ | KEY_WRITE,
NULL,
&dwDisposition );
if ( hKey == NULL )
{
dwStatus = GetLastError();
ClRtlLogPrint(LOG_UNUSUAL,
"[FM] FmpUpdateCreateGroup, Failed in creating the group key for group %1!ws!, Status =%2!d!....\n",
lpszGroupId,
dwStatus);
goto FnExit;
}
if ( dwDisposition != REG_CREATED_NEW_KEY )
{
ClRtlLogPrint(LOG_CRITICAL,
"[FM] FmpUpdateCreateGroup used GUID %1!ws! that already existed! This is impossible.\n",
lpszGroupId);
dwStatus = ERROR_ALREADY_EXISTS;
goto FnExit;
}
CL_ASSERT( dwDisposition == REG_CREATED_NEW_KEY );
//
// Set the group name in the registry
//
dwStatus = DmLocalSetValue( hXsaction,
hKey,
CLUSREG_NAME_GRP_NAME,
REG_SZ,
( CONST BYTE * ) lpszGroupName,
( lstrlenW( lpszGroupName ) + 1 ) *
sizeof( WCHAR ) );
if( dwStatus != ERROR_SUCCESS )
{
ClRtlLogPrint(LOG_CRITICAL,
"[FM] FmpUpdateCreateGroup: DmLocalSetValue for group %1!ws! fails, Status = %2!d!...\n",
lpszGroupId,
dwStatus);
goto FnExit;
}
//
// We really shouldn't be acquiring locks here... but
// we'll try anyway. If we fail, we must return an error
// because we have nothing to return.
//
FmpTryAcquireGroupLock( bLocked, 500 );
if ( !bLocked )
{
pGumGroup->Group = NULL;
dwStatus = ERROR_SHARING_VIOLATION;
goto FnExit;
}
pGumGroup->Group = FmpCreateGroup( lpszGroupId, TRUE );
if ( pGumGroup->Group == NULL )
{
dwStatus = GetLastError();
ClRtlLogPrint(LOG_UNUSUAL,
"[FM] FmpUpdateCreateGroup, FmpCreateFroup failed for group %1!ws!, Status =%2!d!....\n",
lpszGroupId,
dwStatus);
goto FnExit;
} else
{
if ( bSourceNode )
{
OmReferenceObject( pGumGroup->Group );
OmReferenceObject( NmLocalNode );
pNode = NmLocalNode;
} else {
lpszNodeId = (PWSTR)((PCHAR)lpszGroupId +
dwGroupIdLen +
dwGroupNameLen );
pNode = OmReferenceObjectById( ObjectTypeNode, lpszNodeId );
if ( pNode == NULL )
{
CL_LOGFAILURE( ERROR_CLUSTER_NODE_NOT_FOUND );
dwStatus = ERROR_CLUSTER_NODE_NOT_FOUND;
ClRtlLogPrint(LOG_UNUSUAL,
"[FM] FmpUpdateCreateGroup, Could not find node for group %1!ws!, Status =%2!d!....\n",
lpszGroupId,
dwStatus);
CsInconsistencyHalt( ERROR_CLUSTER_NODE_NOT_FOUND );
}
}
CL_ASSERT( pGumGroup->Group->OwnerNode == NULL );
if ( !FmpInPreferredList( pGumGroup->Group, pNode , FALSE, NULL) )
{
ClRtlLogPrint(LOG_UNUSUAL,
"[FM] FmpUpdateCreateGroup, node %1!ws! is not in preferred list for group %2!ws!.\n",
OmObjectId( pNode ),
OmObjectId( pGumGroup->Group ));
}
pGumGroup->Group->OwnerNode = pNode;
if ( OmSetObjectName( pGumGroup->Group, lpszGroupName ) != ERROR_SUCCESS )
{
ClRtlLogPrint(LOG_UNUSUAL,
"[FM] FmpUpdateCreateGroup, Cannot set name for group %1!ws!...\n",
OmObjectId( pGumGroup->Group ));
}
ClusterEvent( CLUSTER_EVENT_GROUP_ADDED, pGumGroup->Group );
}
FnExit:
if ( bLocked )
{
FmpReleaseGroupLock( );
}
if ( hKey != NULL )
{
DmCloseKey( hKey );
}
if ( ( dwStatus == ERROR_SUCCESS ) &&
( hXsaction != NULL ) )
{
DmCommitLocalUpdate( hXsaction );
}
else
{
if ( hXsaction ) DmAbortLocalUpdate( hXsaction );
}
ClRtlLogPrint(LOG_NOISE,
"[FM] FmpUpdateCreateGroup: Exit for group %1!ws!, Status=%2!u!...\n",
lpszGroupId,
dwStatus);
return( dwStatus );
}
/****
@func DWORD | FmpUpdateCompleteGroupMove | This update is made when
FmpTakeGroupRequest fails with an RPC error.
@parm IN BOOL | bSourceNode | Set to TRUE, if the update originated at
this node. Not used.
@parm IN LPCWSTR | pszNodeId | The ID of the dead node.
@parm IN LPCWSTR | pszGroupId | The ID of the group which was in the
middle of the move.
@comm The purpose of this update is to let the ownership of the
group which was in the middle of the move determined consistently.
@rdesc Returns ERROR_SUCCESS.
****/
DWORD
FmpUpdateCompleteGroupMove(
IN BOOL bSourceNode,
IN LPCWSTR pszNodeId,
IN LPCWSTR pszGroupId
)
{
PFM_GROUP pGroup = NULL;
DWORD dwStatus = ERROR_SUCCESS;
//
// Chittur Subbaraman (chitturs) - 4/2/2000
//
// If FM groups are not fully initialized, then don't do anything.
// Don't check for shutdown since we need to handle take group
// exceptions for the quorum group even during a shutdown.
//
if ( !FmpFMGroupsInited )
{
return( ERROR_SUCCESS );
}
pGroup = OmReferenceObjectById( ObjectTypeGroup,
pszGroupId );
if ( !pGroup )
{
ClRtlLogPrint(LOG_CRITICAL,
"[FM] FmpUpdateCompleteGroupMove, %1!ws! group not found\n",
pszGroupId);
goto FnExit;
}
//
// Assign ownership to this group which was in the middle of a move
//
dwStatus = FmpAssignOwnersToGroups( pszNodeId, pGroup, NULL );
if ( dwStatus != ERROR_SUCCESS )
{
ClRtlLogPrint(LOG_CRITICAL,
"[FM] FmpUpdateCompleteGroupMove failed with error %1!d!\n",
dwStatus);
}
FnExit:
if ( pGroup ) OmDereferenceObject( pGroup );
return( dwStatus );
}
DWORD
FmpUpdateCheckAndSetGroupOwner(
IN BOOL bSourceNode,
IN LPCWSTR lpszGroupId,
IN LPCWSTR lpszNodeId
)
/*++
Routine Description:
GUM update handler called from FmpTakeGroupRequest for NT5 cluster
to set the group owner ONLY IF its intended owner is the future
owner node.
Arguments:
bSourceNode - Supplies whether or not this node was the source of the update
lpszGroupId - Supplies the id of the resource whose state is changing
lpszNodeId - Supplies the node id of the group owner.
Return Value:
ERROR_SUCCESS if successful
Win32 error code otherwise
--*/
{
PFM_GROUP pGroup = NULL;
DWORD dwStatus = ERROR_SUCCESS;
PNM_NODE pNode = NULL;
PNM_NODE pPrevNode = NULL;
//dont check for shutdown - we cant afford to lose ownership notifications
//while we are shutting down
//since we dont destroy any fm structures - there shouldnt be a problem in
//handling these
if ( !FmpFMGroupsInited )
{
return( ERROR_SUCCESS );
}
ClRtlLogPrint(LOG_NOISE,
"[FM] FmpUpdateCheckAndSetGroupOwner: Entry for Group = <%1!ws!>....\n",
lpszGroupId);
//
// Chittur Subbaraman (chitturs) - 7/27/99
//
// This GUM handler sets the group ownership only if the future owner
// node is the group's intended owner. If the intended owner is NULL,
// it means the node down processing GUM handler has taken charge
// of this group. If the intended owner is not NULL and not the
// future owner node, then it means that the node down processing
// GUM handler has assigned ownership to the group and the group
// started moving to a different target before the FmpTakeGroupRequest
// that issued this GUM due as a part of the first move operation
// got a chance to execute. In both cases, lay your hands off the
// group.
//
pGroup = OmReferenceObjectById( ObjectTypeGroup,
lpszGroupId );
if ( pGroup == NULL )
{
ClRtlLogPrint(LOG_CRITICAL,
"[FM] FmpUpdateCheckAndSetGroupOwner: GroupID = %1!ws! could not be found...\n",
lpszGroupId);
dwStatus = ERROR_GROUP_NOT_FOUND;
goto FnExit;
}
pNode = OmReferenceObjectById( ObjectTypeNode,
lpszNodeId );
if ( pNode == NULL )
{
ClRtlLogPrint(LOG_CRITICAL,
"[FM] FmpUpdateCheckAndSetGroupOwner: NodeID = %1!ws! could not be found, Group = %2!ws!...\n",
lpszNodeId,
lpszGroupId);
dwStatus = ERROR_CLUSTER_NODE_NOT_FOUND;
goto FnExit;
}
if ( pGroup->pIntendedOwner != pNode )
{
ClRtlLogPrint(LOG_CRITICAL,
"[FM] FmpUpdateCheckAndSetGroupOwner: Group = <%1!ws!> intended owner is invalid, not setting group ownership...\n",
lpszGroupId);
dwStatus = ERROR_GROUP_NOT_AVAILABLE;
goto FnExit;
}
pPrevNode = pGroup->OwnerNode;
//
// Set the new owner node, incr ref count
//
OmReferenceObject( pNode );
pGroup->OwnerNode = pNode;
//
// Decrement the ref count on previous owner
//
OmDereferenceObject( pPrevNode );
//
// Generate an event to signify group owner node change
//
ClusterEvent( CLUSTER_EVENT_GROUP_CHANGE, pGroup );
FnExit:
if ( pGroup ) OmDereferenceObject( pGroup );
if ( pNode ) OmDereferenceObject( pNode );
ClRtlLogPrint(LOG_NOISE,
"[FM] FmpUpdateCheckAndSetGroupOwner: Exit for Group = <%1!ws!>, Status=%2!u!....\n",
lpszGroupId,
dwStatus);
return( dwStatus );
}
DWORD
FmpUpdateCreateResourceType(
IN PVOID Buffer
)
/*++
Routine Description:
GUM update handler called for creating a resource type. For
NT5.1 clusters, this GUM handler does both the registry and
in-memory updates as a local transaction.
Arguments:
Buffer - Buffer containing resource type information.
Return Value:
ERROR_SUCCESS if successful
Win32 error code otherwise
--*/
{
PFM_RESTYPE pResType = NULL;
LPWSTR lpszTypeName;
LPWSTR lpszDisplayName;
LPWSTR lpszDllName;
DWORD dwStatus = ERROR_SUCCESS;
DWORD dwLooksAlive;
DWORD dwIsAlive;
DWORD dwDllNameLen;
DWORD dwDisplayNameLen;
DWORD dwTypeNameLen;
DWORD dwClusterHighestVersion;
DWORD dwDisposition;
HLOCALXSACTION hXsaction = NULL;
HDMKEY hTypeKey = NULL;
//
// Chittur Subbaraman (chitturs) - 2/8/2000
//
// Rewrite this GUM handler as a local transaction (for NT5.1 only)
//
lpszTypeName = ( LPWSTR ) Buffer;
ClRtlLogPrint(LOG_NOISE,
"[FM] FmpUpdateCreateResourceType, Entry for resource type %1!ws!...\n",
lpszTypeName);
pResType = OmReferenceObjectById( ObjectTypeResType,
lpszTypeName );
if ( pResType )
{
dwStatus = ERROR_ALREADY_EXISTS;
ClRtlLogPrint(LOG_CRITICAL,
"[FM] FmpUpdateCreateResourceType, Resource type %1!ws! already exists, Status = %2!d!...\n",
lpszTypeName,
dwStatus);
OmDereferenceObject( pResType );
return( dwStatus );
}
dwTypeNameLen = ( lstrlenW( lpszTypeName ) + 1 ) * sizeof( WCHAR );
lpszDisplayName = ( LPWSTR ) ( ( PCHAR ) Buffer + dwTypeNameLen );
dwDisplayNameLen = ( lstrlenW( lpszDisplayName ) + 1 ) * sizeof( WCHAR );
lpszDllName = ( LPWSTR ) ( ( PCHAR ) Buffer +
dwTypeNameLen +
dwDisplayNameLen );
dwDllNameLen = ( lstrlenW( lpszDllName ) + 1 ) * sizeof( WCHAR );
NmGetClusterOperationalVersion( &dwClusterHighestVersion,
NULL,
NULL );
if ( CLUSTER_GET_MAJOR_VERSION( dwClusterHighestVersion ) <
NT51_MAJOR_VERSION )
{
goto skip_registry_updates;
}
dwLooksAlive = *( DWORD UNALIGNED * ) ( ( ( PCHAR ) Buffer +
dwTypeNameLen +
dwDisplayNameLen +
dwDllNameLen ) );
dwIsAlive = *( DWORD UNALIGNED * ) ( ( ( PCHAR ) Buffer +
dwTypeNameLen +
dwDisplayNameLen +
dwDllNameLen +
sizeof( DWORD ) ) );
//
// Start a transaction
//
hXsaction = DmBeginLocalUpdate();
if ( !hXsaction )
{
dwStatus = GetLastError();
ClRtlLogPrint(LOG_CRITICAL,
"[FM] FmpUpdateCreateResourceType, Failed in starting a transaction for resource type %1!ws!, Status =%2!d!....\n",
lpszTypeName,
dwStatus);
return( dwStatus );
}
hTypeKey = DmLocalCreateKey( hXsaction,
DmResourceTypesKey,
lpszTypeName,
0,
KEY_READ | KEY_WRITE,
NULL,
&dwDisposition );
if ( hTypeKey == NULL )
{
dwStatus = GetLastError();
ClRtlLogPrint(LOG_CRITICAL,
"[FM] FmpUpdateCreateResourceType, Failed in creating the resource types key for resource type %1!ws!, Status =%2!d!....\n",
lpszTypeName,
dwStatus);
goto FnExit;
}
if ( dwDisposition != REG_CREATED_NEW_KEY )
{
dwStatus = ERROR_ALREADY_EXISTS;
ClRtlLogPrint(LOG_CRITICAL,
"[FM] FmpUpdateCreateResourceType, Duplicate resource types key exists for resource type %1!ws!, Status =%2!d!....\n",
lpszTypeName,
dwStatus);
goto FnExit;
}
dwStatus = DmLocalSetValue( hXsaction,
hTypeKey,
CLUSREG_NAME_RESTYPE_DLL_NAME,
REG_SZ,
( CONST BYTE * )lpszDllName,
dwDllNameLen );
if ( dwStatus != ERROR_SUCCESS )
{
ClRtlLogPrint(LOG_CRITICAL,
"[FM] FmpUpdateCreateResourceType, Failed in setting the DLL name for resource type %1!ws!, Status =%2!d!....\n",
lpszTypeName,
dwStatus);
goto FnExit;
}
dwStatus = DmLocalSetValue( hXsaction,
hTypeKey,
CLUSREG_NAME_RESTYPE_IS_ALIVE,
REG_DWORD,
( CONST BYTE * )&dwIsAlive,
sizeof( DWORD ) );
if ( dwStatus != ERROR_SUCCESS )
{
ClRtlLogPrint(LOG_CRITICAL,
"[FM] FmpUpdateCreateResourceType, Failed in setting the Is Alive interval for resource type %1!ws!, Status =%2!d!....\n",
lpszTypeName,
dwStatus);
goto FnExit;
}
dwStatus = DmLocalSetValue( hXsaction,
hTypeKey,
CLUSREG_NAME_RESTYPE_LOOKS_ALIVE,
REG_DWORD,
( CONST BYTE * )&dwLooksAlive,
sizeof( DWORD ) );
if ( dwStatus != ERROR_SUCCESS )
{
ClRtlLogPrint(LOG_CRITICAL,
"[FM] FmpUpdateCreateResourceType, Failed in setting the Looks Alive interval for resource type %1!ws!, Status =%2!d!....\n",
lpszTypeName,
dwStatus);
goto FnExit;
}
dwStatus = DmLocalSetValue( hXsaction,
hTypeKey,
CLUSREG_NAME_RESTYPE_NAME,
REG_SZ,
( CONST BYTE * )lpszDisplayName,
dwDisplayNameLen );
if ( dwStatus != ERROR_SUCCESS )
{
ClRtlLogPrint(LOG_CRITICAL,
"[FM] FmpUpdateCreateResourceType, Failed in setting the display name for resource type %1!ws!, Status =%2!d!....\n",
lpszTypeName,
dwStatus);
goto FnExit;
}
skip_registry_updates:
pResType = FmpCreateResType( lpszTypeName );
if ( pResType != NULL )
{
dwStatus = FmpRmLoadResTypeDll( pResType );
if ( dwStatus == ERROR_SUCCESS )
{
pResType->State = RESTYPE_STATE_LOADS;
} else
{
ClRtlLogPrint(LOG_NOISE,
"[FM] FmpUpdateCreateResourceType: Unable to load dll for resource type %1!ws!, Status=%2!u!...\n",
lpszTypeName,
dwStatus);
//
// Some nodes may not support this resource type. So, consider
// the loading failure as success. However, log the error.
//
dwStatus = ERROR_SUCCESS;
}
} else
{
dwStatus = GetLastError();
ClRtlLogPrint(LOG_NOISE,
"[FM] FmpUpdateCreateResourceType: Unable to create resource type %1!ws!, Status=%2!u!...\n",
lpszTypeName,
dwStatus);
}
FnExit:
if ( hTypeKey != NULL )
{
DmCloseKey( hTypeKey );
}
if ( ( dwStatus == ERROR_SUCCESS ) &&
( hXsaction != NULL ) )
{
DmCommitLocalUpdate( hXsaction );
}
else
{
if ( hXsaction ) DmAbortLocalUpdate( hXsaction );
}
ClRtlLogPrint(LOG_NOISE,
"[FM] FmpUpdateCreateResourceType: Exit for resource type %1!ws!, Status=%2!u!...\n",
lpszTypeName,
dwStatus);
return( dwStatus );
}
DWORD
FmpUpdateCreateResource(
IN OUT PGUM_CREATE_RESOURCE pGumResource
)
{
/*++
Routine Description:
GUM update handler called for creating a resource. For
NT5.1 clusters, this GUM handler does both the registry and
in-memory updates as a local transaction.
Arguments:
pGumResource - Structure containing resource information.
Return Value:
ERROR_SUCCESS if successful
Win32 error code otherwise
--*/
DWORD dwStatus = ERROR_SUCCESS;
HDMKEY hResourceKey = NULL;
HDMKEY hGroupKey = NULL;
DWORD dwDisposition;
HLOCALXSACTION
hXsaction = NULL;
DWORD dwClusterHighestVersion;
PGUM_CREATE_RESOURCE GumResource;
LPWSTR lpszResourceId = NULL;
LPWSTR lpszResourceName = NULL;
LPWSTR lpszResourceType = NULL;
PFM_GROUP pGroup = NULL;
PFM_RESTYPE pResType = NULL;
DWORD dwpollIntervals = CLUSTER_RESOURCE_USE_DEFAULT_POLL_INTERVAL;
DWORD dwPersistentState = 0;
DWORD dwResourceTypeLen = 0;
DWORD dwFlags = 0;
HDMKEY hParamKey = NULL;
//
// Chittur Subbaraman (chitturs) - 1/30/2000
//
// Restructure this GUM update as a local transaction.
//
lpszResourceId = (LPWSTR)( (PCHAR) pGumResource->GroupId +
pGumResource->GroupIdLen );
lpszResourceName = (LPWSTR)( (PCHAR) pGumResource->GroupId +
pGumResource->GroupIdLen +
pGumResource->ResourceIdLen );
pGroup = OmReferenceObjectById( ObjectTypeGroup,
pGumResource->GroupId );
if ( pGroup == NULL )
{
CL_LOGFAILURE( ERROR_GROUP_NOT_FOUND );
ClRtlLogPrint(LOG_CRITICAL,
"[FM] FmpUpdateCreateResource: Group for resource %1!ws! not found.\n",
lpszResourceId);
return( ERROR_GROUP_NOT_FOUND );
}
ClRtlLogPrint(LOG_NOISE,
"[FM] FmpUpdateCreateResource: Entry for resource %1!ws!...\n",
lpszResourceId);
//
// If we are dealing with the mixed mode cluster, don't bother to
// do these registry updates since the API layer would do it.
//
NmGetClusterOperationalVersion( &dwClusterHighestVersion,
NULL,
NULL );
if ( CLUSTER_GET_MAJOR_VERSION( dwClusterHighestVersion ) <
NT51_MAJOR_VERSION )
{
ClRtlLogPrint(LOG_NOISE,
"[FM] FmpUpdateCreateResource: Skipping registry updates for resource %1!ws!...\n",
lpszResourceId);
goto skip_registry_updates;
}
dwResourceTypeLen = *( DWORD UNALIGNED * )( (PCHAR) pGumResource->GroupId +
pGumResource->GroupIdLen +
pGumResource->ResourceIdLen +
(lstrlenW(lpszResourceName)+1) * sizeof(WCHAR) );
lpszResourceType = (LPWSTR)( (PCHAR) pGumResource->GroupId +
pGumResource->GroupIdLen +
pGumResource->ResourceIdLen +
(lstrlenW(lpszResourceName)+1) * sizeof(WCHAR) +
sizeof( DWORD ) );
dwFlags = *( DWORD UNALIGNED * )( (PCHAR) pGumResource->GroupId +
pGumResource->GroupIdLen +
pGumResource->ResourceIdLen +
(lstrlenW(lpszResourceName)+1) * sizeof(WCHAR) +
sizeof( DWORD ) +
dwResourceTypeLen );
//
// Start a transaction
//
hXsaction = DmBeginLocalUpdate();
if ( !hXsaction )
{
dwStatus = GetLastError();
ClRtlLogPrint(LOG_UNUSUAL,
"[FM] FmpUpdateCreateResource, Failed in starting a transaction for resource %1!ws!, Status =%2!d!....\n",
lpszResourceId,
dwStatus);
OmDereferenceObject( pGroup );
return( dwStatus );
}
//
// Create the new resources key.
//
hResourceKey = DmLocalCreateKey( hXsaction,
DmResourcesKey,
lpszResourceId,
0,
KEY_READ | KEY_WRITE,
NULL,
&dwDisposition );
if ( hResourceKey == NULL )
{
dwStatus = GetLastError();
ClRtlLogPrint(LOG_UNUSUAL,
"[FM] FmpUpdateCreateResource, Failed in creating the resource key for resource %1!ws!, Status =%2!d!....\n",
lpszResourceId,
dwStatus);
goto FnExit;
}
if ( dwDisposition != REG_CREATED_NEW_KEY )
{
ClRtlLogPrint(LOG_CRITICAL,
"[FM] FmpUpdateCreateResource used GUID %1!ws! that already existed! This is impossible.\n",
lpszResourceId);
dwStatus = ERROR_ALREADY_EXISTS;
goto FnExit;
}
CL_ASSERT( dwDisposition == REG_CREATED_NEW_KEY );
//
// Set the resource name in the registry
//
dwStatus = DmLocalSetValue( hXsaction,
hResourceKey,
CLUSREG_NAME_RES_NAME,
REG_SZ,
(CONST BYTE *)lpszResourceName,
(lstrlenW(lpszResourceName)+1)*sizeof(WCHAR) );
if( dwStatus != ERROR_SUCCESS )
{
ClRtlLogPrint(LOG_CRITICAL,
"[FM] FmpUpdateCreateResource: DmLocalSetValue (resource name) for resource %1!ws! fails, Status = %2!d!...\n",
lpszResourceId,
dwStatus);
goto FnExit;
}
//
// Set the resource's type in the registry
// Note we reference the resource type and use its ID
// so that the case is correct.
//
pResType = OmReferenceObjectById( ObjectTypeResType, lpszResourceType );
CL_ASSERT( pResType != NULL );
dwStatus = DmLocalSetValue( hXsaction,
hResourceKey,
CLUSREG_NAME_RES_TYPE,
REG_SZ,
(CONST BYTE *) OmObjectId( pResType ),
(lstrlenW( lpszResourceType ) + 1 )*sizeof(WCHAR) );
OmDereferenceObject( pResType );
if ( dwStatus != ERROR_SUCCESS )
{
ClRtlLogPrint(LOG_CRITICAL,
"[FM] FmpUpdateCreateResource: DmLocalSetValue (resource type) for resource %1!ws! fails, Status = %2!d!...\n",
lpszResourceId,
dwStatus);
goto FnExit;
}
//
// Set the resource's poll intervals in the registry.
//
dwStatus = DmLocalSetValue( hXsaction,
hResourceKey,
CLUSREG_NAME_RES_LOOKS_ALIVE,
REG_DWORD,
(CONST BYTE *)&dwpollIntervals,
4 );
if ( dwStatus != ERROR_SUCCESS )
{
ClRtlLogPrint(LOG_CRITICAL,
"[FM] FmpUpdateCreateResource: DmLocalSetValue (looks alive) for resource %1!ws! fails, Status = %2!d!...\n",
lpszResourceId,
dwStatus);
goto FnExit;
}
dwStatus = DmLocalSetValue( hXsaction,
hResourceKey,
CLUSREG_NAME_RES_IS_ALIVE,
REG_DWORD,
(CONST BYTE *)&dwpollIntervals,
4);
if ( dwStatus != ERROR_SUCCESS )
{
ClRtlLogPrint(LOG_CRITICAL,
"[FM] FmpUpdateCreateResource: DmLocalSetValue (is alive) for resource %1!ws! fails, Status = %2!d!...\n",
lpszResourceId,
dwStatus);
goto FnExit;
}
//
// If this resource should be started in a separate monitor, set that
// parameter now.
//
if ( dwFlags & CLUSTER_RESOURCE_SEPARATE_MONITOR )
{
DWORD dwSeparateMonitor = 1;
dwStatus = DmLocalSetValue( hXsaction,
hResourceKey,
CLUSREG_NAME_RES_SEPARATE_MONITOR,
REG_DWORD,
(CONST BYTE *)&dwSeparateMonitor,
sizeof( dwSeparateMonitor ) );
if ( dwStatus != ERROR_SUCCESS)
{
ClRtlLogPrint(LOG_CRITICAL,
"[FM] FmpUpdateCreateResource: DmLocalSetValue (separate monitor) for resource %1!ws! fails, Status = %2!d!...\n",
lpszResourceId,
dwStatus);
goto FnExit;
}
}
//
// Create a Parameters key for the resource.
//
hParamKey = DmLocalCreateKey( hXsaction,
hResourceKey,
CLUSREG_KEYNAME_PARAMETERS,
0,
KEY_READ,
NULL,
&dwDisposition );
if ( hParamKey == NULL )
{
dwStatus = GetLastError();
ClRtlLogPrint(LOG_CRITICAL,
"[FM] FmpUpdateCreateResource: DmLocalCreateKey (parameters) for resource %1!ws! fails, Status = %2!d!...\n",
lpszResourceId,
dwStatus);
CL_LOGFAILURE( dwStatus );
goto FnExit;
} else
{
DmCloseKey( hParamKey );
}
hGroupKey = DmOpenKey( DmGroupsKey,
OmObjectId(pGroup),
KEY_READ | KEY_WRITE);
if ( hGroupKey == NULL )
{
dwStatus = GetLastError();
ClRtlLogPrint(LOG_CRITICAL,
"[FM] FmpUpdateCreateResource: DmOpenKey (group key) for resource %1!ws! fails, Status = %2!d!...\n",
lpszResourceId,
dwStatus);
goto FnExit;
}
//
// Chittur Subbaraman (chitturs) - 5/25/99
//
// Make sure you set the persistent state of the resource to
// ClusterResourceOffline before you create the resource. If
// this is not done, if you create a resource in a group which
// is online, the group's persistent state value (i.e., 1 in
// this case) is inherited by the resource in FmpQueryResourceInfo
// (only the memory state is set and not the registry state and
// this was a problem as well) and if you move such a group to
// another node, it will bring the newly created resource online.
//
dwStatus = DmLocalSetValue( hXsaction,
hResourceKey,
CLUSREG_NAME_RES_PERSISTENT_STATE,
REG_DWORD,
( CONST BYTE * )&dwPersistentState,
sizeof( DWORD ) );
if ( dwStatus != ERROR_SUCCESS )
{
ClRtlLogPrint(LOG_CRITICAL,
"[FM] FmpUpdateCreateResource: DmLocalSetValue (persistent state) for resource %1!ws! fails, Status = %2!d!...\n",
lpszResourceId,
dwStatus);
goto FnExit;
}
//
// Add the resource to the Contains value of the specified group.
//
dwStatus = DmLocalAppendToMultiSz( hXsaction,
hGroupKey,
CLUSREG_NAME_GRP_CONTAINS,
lpszResourceId );
if ( dwStatus != ERROR_SUCCESS )
{
ClRtlLogPrint(LOG_CRITICAL,
"[FM] FmpUpdateCreateResource: DmLocalAppendToMultiSz (contains key) for resource %1!ws! fails, Status = %2!d!...\n",
lpszResourceId,
dwStatus);
goto FnExit;
}
skip_registry_updates:
FmpAcquireResourceLock();
pGumResource->Resource = FmpCreateResource( pGroup,
lpszResourceId,
lpszResourceName,
FALSE );
if ( pGumResource->Resource == NULL )
{
dwStatus = GetLastError();
ClRtlLogPrint(LOG_CRITICAL,
"[FM] FmpUpdateCreateResource: FmpCreateResource for resource %1!ws! fails, Status = %2!d!...\n",
lpszResourceId,
dwStatus);
} else
{
ClusterEvent( CLUSTER_EVENT_GROUP_PROPERTY_CHANGE,
pGroup );
ClusterEvent( CLUSTER_EVENT_RESOURCE_ADDED,
pGumResource->Resource );
if ( pGumResource->Resource )
{
OmReferenceObject( pGumResource->Resource );
FmpPostWorkItem( FM_EVENT_RESOURCE_ADDED,
pGumResource->Resource,
0 );
}
}
FmpReleaseResourceLock();
FnExit:
if ( pGroup != NULL )
{
OmDereferenceObject( pGroup );
}
if ( hResourceKey != NULL )
{
DmCloseKey( hResourceKey );
}
if ( hGroupKey != NULL )
{
DmCloseKey( hGroupKey );
}
if ( ( dwStatus == ERROR_SUCCESS ) &&
( hXsaction != NULL ) )
{
DmCommitLocalUpdate( hXsaction );
}
else
{
if ( hXsaction ) DmAbortLocalUpdate( hXsaction );
}
ClRtlLogPrint(LOG_NOISE,
"[FM] FmpUpdateCreateResource: Exit for resource %1!ws!, Status=%2!u!...\n",
lpszResourceId,
dwStatus);
return( dwStatus );
}
DWORD
FmpUpdateDeleteResource(
IN BOOL bSourceNode,
IN LPCWSTR lpszResourceId
)
/*++
Routine Description:
GUM dispatch routine for deleting a resource. For NT5.1 clusters, this is structured as
as local transaction.
Arguments:
bSourceNode - Supplies whether or not this node initiated the GUM update.
Not used.
lpszResourceId - Supplies the resource ID.
Return Value:
ERROR_SUCCESS if successful.
Win32 error code otherwise.
--*/
{
PFM_RESOURCE pResource = NULL;
PFM_GROUP pGroup = NULL;
PLIST_ENTRY pListEntry = NULL;
PDEPENDENCY pDependency = NULL;
PPOSSIBLE_ENTRY pPossibleEntry = NULL;
DWORD dwStatus;
HLOCALXSACTION
hXsaction = NULL;
DWORD dwClusterHighestVersion;
HDMKEY pGroupKey;
//
// Chittur Subbaraman (chitturs) - 9/7/2000
//
// Structure this GUM update as a local transaction.
//
//
// If FM groups are not fully initialized or FM is shutting down, then
// don't do anything.
//
if ( !FmpFMGroupsInited || FmpShutdown )
{
return( ERROR_SUCCESS );
}
pResource = OmReferenceObjectById( ObjectTypeResource, lpszResourceId );
if ( pResource == NULL )
{
ClRtlLogPrint(LOG_UNUSUAL,
"[FM] FmpUpdateDeleteResource: Resource %1!ws! cannot be found....\n",
lpszResourceId );
return( ERROR_RESOURCE_NOT_FOUND );
}
ClRtlLogPrint(LOG_NOISE,
"[FM] FmpUpdateDeleteResource: Delete resource %1!ws!, address %2!lx!....\n",
lpszResourceId,
pResource );
//
// NOTE: It is difficult to include the checkpoint removal in a local transaction, so keep it
// out for now. Also, note that these functions MUST be called BEFORE the Resources key is
// deleted since they enumerate the values under "Resources\RegSync" and "Resources\CryptoSync".
//
if ( pResource->Group->OwnerNode == NmLocalNode )
{
CpckRemoveResourceCheckpoints( pResource );
CpRemoveResourceCheckpoints( pResource );
}
//
// Start a transaction
//
hXsaction = DmBeginLocalUpdate();
if ( !hXsaction )
{
dwStatus = GetLastError();
ClRtlLogPrint(LOG_CRITICAL,
"[FM] FmpUpdateDeleteResource: Failed in starting a transaction for resource %1!ws!, Status =%2!d!....\n",
lpszResourceId,
dwStatus);
goto FnExit;
}
//
// Cannot acquire group lock here to avoid deadlocks with this current design.
//
//
// Remove all registry entries corresponding to the DependsOn list.
//
pListEntry = pResource->DependsOn.Flink;
while ( pListEntry != &pResource->DependsOn )
{
pDependency = CONTAINING_RECORD( pListEntry,
DEPENDENCY,
DependentLinkage );
CL_ASSERT( pDependency->DependentResource == pResource );
pListEntry = pListEntry->Flink;
//
// Note that the removal of registry entries is done as a local transaction.
//
dwStatus = FmpRemoveResourceDependency( hXsaction,
pResource,
pDependency->ProviderResource );
if ( dwStatus != ERROR_SUCCESS )
{
ClRtlLogPrint(LOG_CRITICAL,
"[FM] FmpUpdateDeleteResource: Unable to remove 'DependsOn' registry entries for resource %1!ws!, Status =%2!d!....\n",
lpszResourceId,
dwStatus);
goto FnExit;
}
}
//
// Remove all registry entries corresponding to the ProvidesFor list.
//
pListEntry = pResource->ProvidesFor.Flink;
while ( pListEntry != &pResource->ProvidesFor )
{
pDependency = CONTAINING_RECORD( pListEntry,
DEPENDENCY,
ProviderLinkage );
CL_ASSERT( pDependency->ProviderResource == pResource );
pListEntry = pListEntry->Flink;
//
// Note that the removal of registry entries is done as a local transaction.
//
dwStatus = FmpRemoveResourceDependency( hXsaction,
pDependency->DependentResource,
pResource );
if ( dwStatus != ERROR_SUCCESS )
{
ClRtlLogPrint(LOG_CRITICAL,
"[FM] FmpUpdateDeleteResource: Unable to remove 'ProvidesFor' registry entries for resource %1!ws!, Status=%2!d!....\n",
lpszResourceId,
dwStatus);
goto FnExit;
}
}
//
// If we are dealing with a Whistler-Win2K cluster, don't bother to
// do these registry updates since the API layer would do it.
//
NmGetClusterOperationalVersion( &dwClusterHighestVersion,
NULL,
NULL );
if ( CLUSTER_GET_MAJOR_VERSION( dwClusterHighestVersion ) <
NT51_MAJOR_VERSION )
{
ClRtlLogPrint(LOG_NOISE,
"[FM] FmpUpdateDeleteResource: Skipping registry updates for resource %1!ws!...\n",
lpszResourceId);
goto skip_registry_updates;
}
dwStatus = DmLocalDeleteTree( hXsaction,
DmResourcesKey,
OmObjectId( pResource ) );
if ( ( dwStatus != ERROR_SUCCESS ) &&
( dwStatus != ERROR_FILE_NOT_FOUND ) )
{
ClRtlLogPrint(LOG_CRITICAL,
"[FM] FmpUpdateDeleteResource: Unable to remove 'Resources' tree for resource %1!ws!, Status=%2!d!....\n",
lpszResourceId,
dwStatus);
goto FnExit;
}
pGroupKey = DmOpenKey( DmGroupsKey,
OmObjectId( pResource->Group ),
KEY_READ | KEY_SET_VALUE );
if ( pGroupKey == NULL )
{
dwStatus = GetLastError();
ClRtlLogPrint(LOG_CRITICAL,
"[FM] FmpUpdateDeleteResource: Unable to find 'Groups' key for resource %1!ws!, Status=%2!d!....\n",
lpszResourceId,
dwStatus);
goto FnExit;
}
dwStatus = DmLocalRemoveFromMultiSz( hXsaction,
pGroupKey,
CLUSREG_NAME_GRP_CONTAINS,
OmObjectId( pResource ) );
DmCloseKey( pGroupKey );
if ( dwStatus != ERROR_SUCCESS )
{
ClRtlLogPrint(LOG_CRITICAL,
"[FM] FmpUpdateDeleteResource: Unable to remove contains list for resource %1!ws! in group %2!ws!, Status=%3!d!....\n",
lpszResourceId,
OmObjectId( pResource->Group ),
dwStatus);
goto FnExit;
}
skip_registry_updates:
//
// Remove all list entries corresponding to the DependsOn list.
//
pListEntry = pResource->DependsOn.Flink;
while ( pListEntry != &pResource->DependsOn ) {
pDependency = CONTAINING_RECORD( pListEntry,
DEPENDENCY,
DependentLinkage );
pListEntry = pListEntry->Flink;
RemoveEntryList( &pDependency->ProviderLinkage );
RemoveEntryList( &pDependency->DependentLinkage );
OmDereferenceObject( pDependency->DependentResource );
OmDereferenceObject( pDependency->ProviderResource );
LocalFree( pDependency );
}
//
// Remove all list entries corresponding to the ProvidesFor list.
//
pListEntry = pResource->ProvidesFor.Flink;
while ( pListEntry != &pResource->ProvidesFor ) {
pDependency = CONTAINING_RECORD( pListEntry,
DEPENDENCY,
ProviderLinkage );
pListEntry = pListEntry->Flink;
RemoveEntryList( &pDependency->ProviderLinkage );
RemoveEntryList( &pDependency->DependentLinkage );
OmDereferenceObject( pDependency->DependentResource );
OmDereferenceObject( pDependency->ProviderResource );
LocalFree( pDependency );
}
//
// Remove all entries from the possible owners list.
//
while ( !IsListEmpty( &pResource->PossibleOwners ) )
{
pListEntry = RemoveHeadList( &pResource->PossibleOwners );
pPossibleEntry = CONTAINING_RECORD( pListEntry,
POSSIBLE_ENTRY,
PossibleLinkage );
OmDereferenceObject( pPossibleEntry->PossibleNode );
LocalFree( pPossibleEntry );
}
//
// Remove this resource from the Contains list.
//
RemoveEntryList( &pResource->ContainsLinkage );
OmDereferenceObject( pResource );
//
// Close the resource's registry key.
//
DmRundownList( &pResource->DmRundownList );
if ( pResource->RegistryKey != NULL )
{
DmCloseKey( pResource->RegistryKey );
pResource->RegistryKey = NULL;
}
//
// SS: we do not delete the reference to the resource here
// since we will shortly have to add one before posting a notification
// to the fm worker thread.
//
// Post a work item to close the resource in the resource handler.
// Note that this must be done asynchronously as we cannot call
// the resource monitor from a GUM handler. If we do, resources
// do funny things and make deadlocks.
//
FmpPostWorkItem( FM_EVENT_RESOURCE_DELETED, pResource, 0 );
//
// Decrement resource type reference.
//
if ( pResource->Type != NULL ) {
OmDereferenceObject( pResource->Type );
pResource->Type = NULL;
}
//
// Remove the resource from the resource list.
//
dwStatus = OmRemoveObject( pResource );
ClusterEvent( CLUSTER_EVENT_RESOURCE_DELETED, pResource );
ClusterEvent( CLUSTER_EVENT_GROUP_PROPERTY_CHANGE,
pResource->Group );
//
// Mark the resource as deleted
//
pResource->dwStructState = FM_RESOURCE_STRUCT_MARKED_FOR_DELETE;
FnExit:
OmDereferenceObject( pResource );
if ( ( dwStatus == ERROR_SUCCESS ) &&
( hXsaction != NULL ) )
{
DmCommitLocalUpdate( hXsaction );
}
else
{
if ( hXsaction ) DmAbortLocalUpdate( hXsaction );
}
ClRtlLogPrint(LOG_NOISE,
"[FM] FmpUpdateDeleteResource: Exit for resource %1!ws!, Status=%2!u!...\n",
lpszResourceId,
dwStatus);
return( dwStatus );
} // FmpUpdateDeleteResource
DWORD
FmpUpdateUseRandomizedNodeListForGroups(
IN BOOL SourceNode,
IN LPCWSTR pszNodeId,
IN PFM_GROUP_NODE_LIST pGroupNodeList
)
/*++
Routine Description:
GUM dispatch routine for using a randomized preferred list for group ownership on
node down.
Arguments:
bSourceNode - Supplies whether or not this node initiated the GUM update.
Not used.
pszNodeId - Supplies the ID of the node that is down.
pGroupNodeList - Randomized preferred node list for groups.
Return Value:
ERROR_SUCCESS if successful.
Win32 error code otherwise.
--*/
{
DWORD dwStatus = ERROR_SUCCESS;
DWORD dwNodeId;
//
// Chittur Subbaraman (chitturs) - 4/19/2001
//
// If FM groups are not fully initialized or FM is shutting down, then
// don't do anything.
//
if ( !FmpFMGroupsInited || FmpShutdown )
{
return( ERROR_SUCCESS );
}
//
// In a node evict, the NM GUM handler gets rids of the dead node from the OM list.
// This node down FM GUM handler could follow the NM evict GUM handler since NM
// lets an evict through once *it* declares a node as down. At that time, there is no
// guarantee that the FM node down GUM has executed since that GUM is issued in the
// async phase of node down processing and could very well follow the NM evict GUM
// handler. Thus, this GUM handler and associated functions CANNOT call OM to get
// a node object from the node ID string. Thus, this GUM handler and associated
// functions are carefully written to work with a node ID as opposed to a node object.
//
dwNodeId = wcstoul( pszNodeId, NULL, 10 );
//
// If this update has already been seen after the node down, ignore this one
//
if ( gFmpNodeArray[dwNodeId].dwNodeDownProcessingInProgress == 0 )
{
ClRtlLogPrint(LOG_NOISE,
"[FM] FmpUpdateUseRandomizedNodeListForGroups: %1!ws! node down has been processed already...\n",
pszNodeId);
goto FnExit;
}
//
// Assign ownership to all groups owned by the dead node
//
dwStatus = FmpAssignOwnersToGroups( pszNodeId,
NULL,
pGroupNodeList );
if ( dwStatus != ERROR_SUCCESS )
{
ClRtlLogPrint(LOG_CRITICAL,
"[FM] FmpUpdateUseRandomizedNodeListForGroups: FmpAssignOwnersToGroups failed %1!d!\n",
dwStatus);
}
//
// Mark that the node down processing has been done
//
gFmpNodeArray[dwNodeId].dwNodeDownProcessingInProgress = 0;
FnExit:
return( dwStatus );
}// FmpUpdateUseRandomizedNodeListForGroups