2678 lines
74 KiB
C
2678 lines
74 KiB
C
/*++
|
||
|
||
Copyright (c) 1996-1997 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
fmreg.c
|
||
|
||
Abstract:
|
||
|
||
Object Manager registry query routines for the Failover Manager
|
||
component of the NT Cluster Service.
|
||
|
||
Author:
|
||
|
||
Rod Gamache (rodga) 14-Mar-1996
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
#include "fmp.h"
|
||
#include <stdlib.h>
|
||
#include <search.h>
|
||
|
||
#define LOG_MODULE FMREG
|
||
|
||
//
|
||
// Global data initialized in this module
|
||
//
|
||
ULONG FmpUnknownCount = 0;
|
||
|
||
//
|
||
// Local functions
|
||
//
|
||
|
||
VOID
|
||
FmpGroupChangeCallback(
|
||
IN DWORD_PTR Context1,
|
||
IN DWORD_PTR Context2,
|
||
IN DWORD CompletionFilter,
|
||
IN LPCWSTR RelativeName
|
||
);
|
||
|
||
VOID
|
||
FmpResourceChangeCallback(
|
||
IN DWORD_PTR Context1,
|
||
IN DWORD_PTR Context2,
|
||
IN DWORD CompletionFilter,
|
||
IN LPCWSTR RelativeName
|
||
);
|
||
|
||
|
||
|
||
|
||
/////////////////////////////////////////////////////////////////////////////
|
||
//
|
||
// Configuration Database Access Routines
|
||
//
|
||
/////////////////////////////////////////////////////////////////////////////
|
||
|
||
DWORD
|
||
FmpRegEnumerateKey(
|
||
IN HDMKEY ListKey,
|
||
IN DWORD Index,
|
||
OUT LPWSTR *Name,
|
||
IN OUT LPDWORD NameMaxSize
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Arguments:
|
||
|
||
Returns:
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD status;
|
||
FILETIME fileTime;
|
||
|
||
|
||
status = DmEnumKey( ListKey,
|
||
Index,
|
||
*Name,
|
||
NameMaxSize,
|
||
NULL );
|
||
|
||
if ( status == ERROR_SUCCESS ) {
|
||
return(ERROR_SUCCESS);
|
||
}
|
||
|
||
if ( status == ERROR_MORE_DATA ) {
|
||
PWCHAR nameString = NULL;
|
||
DWORD maxSubkeyNameSize = 0;
|
||
DWORD temp = 0;
|
||
|
||
//
|
||
// The name string isn't big enough. Reallocate it.
|
||
//
|
||
|
||
//
|
||
// Find out the length of the longest subkey name.
|
||
//
|
||
status = DmQueryInfoKey( ListKey,
|
||
&temp,
|
||
&maxSubkeyNameSize,
|
||
&temp,
|
||
&temp,
|
||
&temp,
|
||
NULL,
|
||
&fileTime );
|
||
|
||
if ( (status != ERROR_SUCCESS) &&
|
||
(status != ERROR_MORE_DATA) ) {
|
||
ClRtlLogPrint(LOG_NOISE,"[FM] DmQueryInfoKey returned status %1!u!\n",
|
||
status);
|
||
return(status);
|
||
}
|
||
|
||
CL_ASSERT(maxSubkeyNameSize != 0);
|
||
|
||
//
|
||
// The returned subkey name size does not include the terminating null.
|
||
// It is also an ANSI string count.
|
||
//
|
||
maxSubkeyNameSize *= sizeof(WCHAR);
|
||
maxSubkeyNameSize += sizeof(UNICODE_NULL);
|
||
|
||
nameString = LocalAlloc( LMEM_FIXED,
|
||
maxSubkeyNameSize );
|
||
|
||
if ( nameString == NULL ) {
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] Unable to allocate key name buffer of size %1!u!\n",
|
||
maxSubkeyNameSize
|
||
);
|
||
return(ERROR_NOT_ENOUGH_MEMORY);
|
||
}
|
||
|
||
LocalFree(*Name);
|
||
*Name = nameString;
|
||
*NameMaxSize = maxSubkeyNameSize;
|
||
|
||
status = DmEnumKey( ListKey,
|
||
Index,
|
||
*Name,
|
||
NameMaxSize,
|
||
NULL );
|
||
|
||
CL_ASSERT(status != ERROR_MORE_DATA);
|
||
CL_ASSERT(status != ERROR_NO_MORE_ITEMS);
|
||
}
|
||
|
||
return(status);
|
||
|
||
} // FmpRegEnumerateKey
|
||
|
||
|
||
VOID
|
||
FmpPruneGroupOwners(
|
||
IN PFM_GROUP Group
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Prunes the entire preferred group list based on the possible
|
||
nodes of each resource in the group.
|
||
|
||
Arguments:
|
||
|
||
Group - Supplies the group object to be pruned
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PLIST_ENTRY ListEntry;
|
||
PFM_RESOURCE Resource;
|
||
|
||
ListEntry = Group->Contains.Flink;
|
||
while (ListEntry != &Group->Contains) {
|
||
Resource = CONTAINING_RECORD(ListEntry,
|
||
FM_RESOURCE,
|
||
ContainsLinkage);
|
||
FmpPrunePreferredList(Resource);
|
||
ListEntry = ListEntry->Flink;
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
FmpPrunePreferredList(
|
||
IN PFM_RESOURCE Resource
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Prune out nodes from the preferred owners list, if the resource cannot
|
||
run on that node.
|
||
|
||
Arguments:
|
||
|
||
Resource - Pointer to the resource object with a possible owners list.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PFM_GROUP group;
|
||
PLIST_ENTRY listEntry;
|
||
PLIST_ENTRY entry;
|
||
PPREFERRED_ENTRY preferredEntry;
|
||
PPOSSIBLE_ENTRY possibleEntry;
|
||
DWORD orderedEntry = 0;
|
||
|
||
group = Resource->Group;
|
||
|
||
//
|
||
// For each entry in the Preferred list, it must exist in the possible
|
||
// list.
|
||
//
|
||
|
||
for ( listEntry = group->PreferredOwners.Flink;
|
||
listEntry != &(group->PreferredOwners);
|
||
) {
|
||
|
||
preferredEntry = CONTAINING_RECORD( listEntry,
|
||
PREFERRED_ENTRY,
|
||
PreferredLinkage );
|
||
//
|
||
// Scan the Possible owners list in the resource to make sure that
|
||
// the group can run on all of the preferred owners.
|
||
//
|
||
for ( entry = Resource->PossibleOwners.Flink;
|
||
entry != &(Resource->PossibleOwners);
|
||
entry = entry->Flink ) {
|
||
possibleEntry = CONTAINING_RECORD( entry,
|
||
POSSIBLE_ENTRY,
|
||
PossibleLinkage );
|
||
if ( preferredEntry->PreferredNode == possibleEntry->PossibleNode ) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
listEntry = listEntry->Flink;
|
||
|
||
//
|
||
// If we got to the end of the possible owners list and didn't find
|
||
// an entry, then remove the current preferred entry.
|
||
//
|
||
if ( entry == &(Resource->PossibleOwners) ) {
|
||
ClRtlLogPrint( LOG_NOISE,
|
||
"[FM] Removing preferred node %1!ws! because of resource %2!ws!\n",
|
||
OmObjectId(preferredEntry->PreferredNode),
|
||
OmObjectId(Resource));
|
||
|
||
//
|
||
// If this was an ordered entry, then decrement count.
|
||
//
|
||
if ( orderedEntry < group->OrderedOwners ) {
|
||
--group->OrderedOwners;
|
||
}
|
||
RemoveEntryList( &preferredEntry->PreferredLinkage );
|
||
OmDereferenceObject(preferredEntry->PreferredNode);
|
||
LocalFree(preferredEntry);
|
||
if ( IsListEmpty( &group->PreferredOwners ) ) {
|
||
ClRtlLogPrint( LOG_ERROR,
|
||
"[FM] Preferred owners list is now empty! No place to run group %1!ws!\n",
|
||
OmObjectId(group));
|
||
}
|
||
} else {
|
||
orderedEntry++;
|
||
}
|
||
}
|
||
|
||
} // FmpPrunePreferredList
|
||
|
||
|
||
|
||
BOOL
|
||
FmpAddNodeToPrefList(
|
||
IN PNM_NODE Node,
|
||
IN PFM_GROUP Group
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Node enumeration callback for including all remaining nodes
|
||
in a group's preferred owners list.
|
||
|
||
Arguments:
|
||
|
||
Group - a pointer to the group object to add this node as a preferred owner.
|
||
|
||
Context2 - Not used
|
||
|
||
Node - Supplies the node.
|
||
|
||
Name - Supplies the node's name.
|
||
|
||
Return Value:
|
||
|
||
TRUE - to indicate that the enumeration should continue.
|
||
FALSE - to indicate that the enumeration should not continue.
|
||
|
||
--*/
|
||
|
||
{
|
||
//if it is already in the list FmpSetPrefferedEntry returns ERROR_SUCCESS
|
||
if ( FmpSetPreferredEntry( Group, Node ) != ERROR_SUCCESS ) {
|
||
return(FALSE);
|
||
}
|
||
|
||
return(TRUE);
|
||
|
||
} // FmpAddNodeToPrefList
|
||
|
||
BOOL
|
||
FmpAddNodeToListCb(
|
||
IN OUT PNM_NODE_ENUM2 *ppNmNodeEnum,
|
||
IN LPDWORD pdwAllocatedEntries,
|
||
IN PNM_NODE pNode,
|
||
IN LPCWSTR Id
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Worker callback routine for the enumeration of nodes.
|
||
This routine adds the specified node to the list that is being
|
||
generated.
|
||
|
||
Arguments:
|
||
|
||
ppNmNodeEnum - The node Enumeration list. Can be an output if a new list is
|
||
allocated.
|
||
|
||
EnumData - Supplies the current enumeration data structure.
|
||
|
||
Group - The Group object being enumerated.
|
||
|
||
Id - The Id of the node object being enumerated.
|
||
|
||
Returns:
|
||
|
||
TRUE - to indicate that the enumeration should continue.
|
||
|
||
Side Effects:
|
||
|
||
Makes the quorum group first in the list.
|
||
|
||
--*/
|
||
|
||
{
|
||
PNM_NODE_ENUM2 pNmNodeEnum;
|
||
PNM_NODE_ENUM2 pNewNmNodeEnum;
|
||
DWORD dwNewAllocated;
|
||
DWORD dwStatus;
|
||
|
||
|
||
|
||
pNmNodeEnum = *ppNmNodeEnum;
|
||
|
||
if ( pNmNodeEnum->NodeCount >= *pdwAllocatedEntries )
|
||
{
|
||
//
|
||
// Time to grow the GROUP_ENUM
|
||
//
|
||
|
||
dwNewAllocated = *pdwAllocatedEntries + ENUM_GROW_SIZE;
|
||
pNewNmNodeEnum = LocalAlloc(LMEM_FIXED, NODE_SIZE(dwNewAllocated));
|
||
if ( pNewNmNodeEnum == NULL )
|
||
{
|
||
dwStatus = ERROR_NOT_ENOUGH_MEMORY;
|
||
CL_UNEXPECTED_ERROR(dwStatus);
|
||
return(FALSE);
|
||
}
|
||
|
||
CopyMemory(pNewNmNodeEnum, pNmNodeEnum, NODE_SIZE(*pdwAllocatedEntries));
|
||
*pdwAllocatedEntries = dwNewAllocated;
|
||
*ppNmNodeEnum = pNewNmNodeEnum;
|
||
LocalFree(pNmNodeEnum);
|
||
pNmNodeEnum = pNewNmNodeEnum;
|
||
}
|
||
|
||
//dont copy more that the sixe
|
||
lstrcpyW(pNmNodeEnum->NodeList[pNmNodeEnum->NodeCount].NodeId, Id );
|
||
++pNmNodeEnum->NodeCount;
|
||
|
||
|
||
return(TRUE);
|
||
|
||
} // FmpAddNodeToListCb
|
||
|
||
int
|
||
__cdecl
|
||
SortNodesInAscending(
|
||
const PVOID Elem1,
|
||
const PVOID Elem2
|
||
)
|
||
{
|
||
PNM_NODE_INFO2 El1 = (PNM_NODE_INFO2)Elem1;
|
||
PNM_NODE_INFO2 El2 = (PNM_NODE_INFO2)Elem2;
|
||
|
||
return(lstrcmpiW( El1->NodeId, El2->NodeId ));
|
||
|
||
}// SortNodesInAsceding
|
||
|
||
|
||
DWORD
|
||
FmpEnumNodesById(
|
||
IN DWORD dwOptions,
|
||
OUT PNM_NODE_ENUM2 *ppNodeEnum
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Enumerates and sorts the list of Groups.
|
||
|
||
Arguments:
|
||
|
||
*ppNodeEnum - Returns the requested objects.
|
||
|
||
dwOptions -
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS if successful.
|
||
|
||
Win32 error code on error.
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD dwStatus;
|
||
PNM_NODE_ENUM2 pNmNodeEnum = NULL;
|
||
DWORD dwAllocatedEntries;
|
||
|
||
//
|
||
// initialize output params to NULL
|
||
//
|
||
*ppNodeEnum = NULL;
|
||
|
||
dwAllocatedEntries = ENUM_GROW_SIZE;
|
||
|
||
pNmNodeEnum = LocalAlloc( LMEM_FIXED, NODE_SIZE(ENUM_GROW_SIZE) );
|
||
if ( pNmNodeEnum == NULL ) {
|
||
dwStatus = ERROR_NOT_ENOUGH_MEMORY;
|
||
goto error_exit;
|
||
}
|
||
|
||
pNmNodeEnum->NodeCount = 0;
|
||
|
||
//
|
||
// Enumerate all nodes
|
||
//
|
||
|
||
OmEnumObjects( ObjectTypeNode,
|
||
FmpAddNodeToListCb,
|
||
&pNmNodeEnum,
|
||
&dwAllocatedEntries );
|
||
|
||
CL_ASSERT( pNmNodeEnum->NodeCount != 0 );
|
||
//
|
||
// Sort the groups by their collating sequence number.
|
||
//
|
||
|
||
qsort( (PVOID)(&pNmNodeEnum->NodeList[0]),
|
||
(size_t)pNmNodeEnum->NodeCount,
|
||
sizeof(NM_NODE_INFO2),
|
||
(int (__cdecl *)(const void*, const void*)) SortNodesInAscending
|
||
);
|
||
|
||
*ppNodeEnum = pNmNodeEnum;
|
||
return( ERROR_SUCCESS );
|
||
|
||
error_exit:
|
||
if ( pNmNodeEnum != NULL ) {
|
||
LocalFree( pNmNodeEnum );
|
||
}
|
||
|
||
return( dwStatus );
|
||
|
||
} // FmpEnumNodesById
|
||
|
||
|
||
BOOL
|
||
FmpEnumAddAllOwners(
|
||
IN PFM_RESOURCE Resource,
|
||
IN PVOID Context2,
|
||
IN PNM_NODE Node,
|
||
IN LPCWSTR Name
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Node enumeration callback for adding all nodes to a resource's
|
||
list of possible nodes.
|
||
|
||
Arguments:
|
||
|
||
Resource - a pointer to the resource object to add this node as a possible owner.
|
||
|
||
Context2 - Not used
|
||
|
||
Node - Supplies the node.
|
||
|
||
Name - Supplies the node's name.
|
||
|
||
Return Value:
|
||
|
||
TRUE - to indicate that the enumeration should continue.
|
||
FALSE - to indicate that the enumeration should not continue.
|
||
|
||
--*/
|
||
|
||
{
|
||
if ( !Resource->PossibleList ) {
|
||
FmpAddPossibleEntry(Resource, Node);
|
||
}
|
||
return(TRUE);
|
||
|
||
} // FmpEnumAddAllOwners
|
||
|
||
|
||
DWORD
|
||
FmpQueryGroupNodes(
|
||
IN PFM_GROUP Group,
|
||
IN HDMKEY hGroupKey
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Rebuilds and orders the list of preferred nodes associated with
|
||
a group.
|
||
|
||
Arguments:
|
||
|
||
Group - Supplies the group whose list of preferred nodes should
|
||
be rebuilt.
|
||
|
||
hGroupKey - Supplies a handle to the group's registry key
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS if successful
|
||
|
||
Win32 error code otherwise
|
||
|
||
--*/
|
||
|
||
{
|
||
LPWSTR preferredOwnersString = NULL;
|
||
DWORD preferredOwnersStringSize = 0;
|
||
DWORD preferredOwnersStringMaxSize = 0;
|
||
DWORD mszStringIndex;
|
||
PPREFERRED_ENTRY preferredEntry;
|
||
DWORD status;
|
||
PLIST_ENTRY listEntry;
|
||
PNM_NODE_ENUM2 pNmNodeEnum = NULL;
|
||
PNM_NODE pNmNode;
|
||
DWORD i;
|
||
//
|
||
// First, delete the old list.
|
||
//
|
||
while ( !IsListEmpty(&Group->PreferredOwners) ) {
|
||
listEntry = Group->PreferredOwners.Flink;
|
||
preferredEntry = CONTAINING_RECORD( listEntry,
|
||
PREFERRED_ENTRY,
|
||
PreferredLinkage );
|
||
RemoveEntryList( &preferredEntry->PreferredLinkage );
|
||
OmDereferenceObject( preferredEntry->PreferredNode );
|
||
LocalFree( preferredEntry );
|
||
}
|
||
Group->OrderedOwners = 0;
|
||
|
||
CL_ASSERT ( IsListEmpty(&Group->PreferredOwners) );
|
||
|
||
status = DmQueryMultiSz( hGroupKey,
|
||
CLUSREG_NAME_GRP_PREFERRED_OWNERS,
|
||
&preferredOwnersString,
|
||
&preferredOwnersStringMaxSize,
|
||
&preferredOwnersStringSize );
|
||
|
||
if ( status == NO_ERROR ) {
|
||
|
||
//
|
||
// Now Create the Preferred Owners list.
|
||
//
|
||
|
||
for ( mszStringIndex = 0; ; mszStringIndex++ ) {
|
||
LPCWSTR nameString;
|
||
PNM_NODE preferredNode;
|
||
|
||
nameString = ClRtlMultiSzEnum( preferredOwnersString,
|
||
preferredOwnersStringSize/sizeof(WCHAR),
|
||
mszStringIndex );
|
||
|
||
if ( nameString == NULL ) {
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Create the Preferred Owners List entry
|
||
//
|
||
|
||
preferredEntry = LocalAlloc( LMEM_FIXED, sizeof(PREFERRED_ENTRY) );
|
||
|
||
if ( preferredEntry == NULL ) {
|
||
status = ERROR_NOT_ENOUGH_MEMORY;
|
||
return(status);
|
||
}
|
||
|
||
//
|
||
// Create the preferred owners. This will implicitly create
|
||
// additional reference required for the preferred owner nodes.
|
||
//
|
||
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] Group %1!ws! preferred owner %2!ws!.\n",
|
||
OmObjectId(Group),
|
||
nameString);
|
||
|
||
preferredNode = OmReferenceObjectById( ObjectTypeNode,
|
||
nameString );
|
||
|
||
if ( preferredNode == NULL ) {
|
||
LocalFree(preferredEntry);
|
||
status = GetLastError();
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] Failed to find node %1!ws! for Group %2!ws!\n",
|
||
nameString,
|
||
OmObjectId(Group));
|
||
} else {
|
||
Group->OrderedOwners++;
|
||
preferredEntry->PreferredNode = preferredNode;
|
||
InsertTailList( &Group->PreferredOwners,
|
||
&preferredEntry->PreferredLinkage );
|
||
}
|
||
|
||
}
|
||
LocalFree( preferredOwnersString );
|
||
}
|
||
|
||
//
|
||
// We now include all remaining nodes in the preferred owners list.
|
||
//
|
||
// Every node must maintain the same ordering for the preferred list
|
||
// for the multi-node cluster to work
|
||
//
|
||
status = FmpEnumNodesById( 0, &pNmNodeEnum );
|
||
|
||
if ( status != ERROR_SUCCESS )
|
||
{
|
||
CL_UNEXPECTED_ERROR( status );
|
||
ClRtlLogPrint(LOG_UNUSUAL,
|
||
"[FM] FmpQueryGroupNodes: FmpEnumNodesById failed, status = %1!u!\r\n",
|
||
status);
|
||
// return error
|
||
}
|
||
|
||
for ( i=0; i<pNmNodeEnum->NodeCount; i++ )
|
||
{
|
||
pNmNode = OmReferenceObjectById( ObjectTypeNode,
|
||
pNmNodeEnum->NodeList[i].NodeId );
|
||
CL_ASSERT( pNmNode != NULL );
|
||
FmpAddNodeToPrefList( pNmNode, Group );
|
||
OmDereferenceObject( pNmNode );
|
||
}
|
||
|
||
//
|
||
// Now prune out all the unreachable nodes.
|
||
//
|
||
FmpPruneGroupOwners( Group );
|
||
|
||
//
|
||
// Chittur Subbaraman (chitturs) - 12/11/98
|
||
//
|
||
// Free the memory allocated for pNmNodeEnum.
|
||
// (Fix memory leak)
|
||
//
|
||
LocalFree( pNmNodeEnum );
|
||
|
||
return( ERROR_SUCCESS );
|
||
|
||
} // FmpQueryGroupNodes
|
||
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
FmpQueryGroupInfo(
|
||
IN PVOID Object,
|
||
IN BOOL Initialize
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Queries Group info from the registry when creating a Group Object.
|
||
|
||
Arguments:
|
||
|
||
Object - A pointer to the Group object being created.
|
||
|
||
Initialize - TRUE if the resource objects should be initialized. FALSE
|
||
otherwise.
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS if successful.
|
||
|
||
Win32 error code otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
PFM_GROUP Group = (PFM_GROUP)Object;
|
||
PFM_RESOURCE Resource;
|
||
DWORD status;
|
||
LPWSTR containsString = NULL;
|
||
DWORD containsStringSize = 0;
|
||
DWORD containsStringMaxSize = 0;
|
||
DWORD temp;
|
||
DWORD mszStringIndex;
|
||
DWORD failoverThreshold = CLUSTER_GROUP_DEFAULT_FAILOVER_THRESHOLD;
|
||
DWORD failoverPeriod = CLUSTER_GROUP_DEFAULT_FAILOVER_PERIOD;
|
||
DWORD autoFailbackType = CLUSTER_GROUP_DEFAULT_AUTO_FAILBACK_TYPE;
|
||
DWORD zero = 0;
|
||
PLIST_ENTRY listEntry;
|
||
HDMKEY groupKey;
|
||
DWORD groupNameStringMaxSize = 0;
|
||
DWORD groupNameStringSize = 0;
|
||
LPWSTR groupName;
|
||
PPREFERRED_ENTRY preferredEntry;
|
||
DWORD dwBufferSize = 0;
|
||
DWORD dwStringSize;
|
||
|
||
|
||
//
|
||
// Initialize the Group object from the registry info.
|
||
//
|
||
if ( Group->Initialized ) {
|
||
return(ERROR_SUCCESS);
|
||
}
|
||
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] Initializing group %1!ws! from the registry.\n",
|
||
OmObjectId(Group));
|
||
|
||
//
|
||
// Open the group key.
|
||
//
|
||
groupKey = DmOpenKey( DmGroupsKey,
|
||
OmObjectId(Group),
|
||
MAXIMUM_ALLOWED );
|
||
|
||
if ( groupKey == NULL ) {
|
||
status = GetLastError();
|
||
ClRtlLogPrint(LOG_UNUSUAL,
|
||
"[FM] Unable to open group key %1!ws!, %2!u!\n",
|
||
OmObjectId(Group),
|
||
status);
|
||
|
||
return(status);
|
||
}
|
||
|
||
//
|
||
// Read the required group values. The strings will be allocated
|
||
// by the DmQuery* functions.
|
||
//
|
||
|
||
//
|
||
// Get the Name.
|
||
//
|
||
status = DmQuerySz( groupKey,
|
||
CLUSREG_NAME_GRP_NAME,
|
||
&groupName,
|
||
&groupNameStringMaxSize,
|
||
&groupNameStringSize );
|
||
|
||
if (status != ERROR_SUCCESS) {
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] Unable to read name for Group %1!ws!\n",
|
||
OmObjectId(Group));
|
||
goto error_exit;
|
||
}
|
||
|
||
status = OmSetObjectName( Group, groupName );
|
||
|
||
if ( status != ERROR_SUCCESS ) {
|
||
ClRtlLogPrint(LOG_ERROR,
|
||
"[FM] Unable to set name %1!ws! for group %2!ws!, error %3!u!.\n",
|
||
groupName,
|
||
OmObjectId(Group),
|
||
status );
|
||
goto error_exit;
|
||
}
|
||
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] Name for Group %1!ws! is '%2!ws!'.\n",
|
||
OmObjectId(Group),
|
||
groupName);
|
||
|
||
LocalFree(groupName);
|
||
//
|
||
// Get the PersistentState.
|
||
//
|
||
status = DmQueryDword( groupKey,
|
||
CLUSREG_NAME_GRP_PERSISTENT_STATE,
|
||
&temp,
|
||
&zero );
|
||
|
||
//
|
||
// If the group state is non-zero then we go online.
|
||
//
|
||
if ( temp ) {
|
||
Group->PersistentState = ClusterGroupOnline;
|
||
} else {
|
||
Group->PersistentState = ClusterGroupOffline;
|
||
}
|
||
|
||
//
|
||
// Get the OPTIONAL PreferredOwners list.
|
||
// *** NOTE *** This MUST be done before processing the contains list!
|
||
//
|
||
status = FmpQueryGroupNodes(Group, groupKey);
|
||
if (status != ERROR_SUCCESS) {
|
||
ClRtlLogPrint(LOG_UNUSUAL,"[FM] Error %1!d! creating preferred owners list\n",status);
|
||
goto error_exit;
|
||
}
|
||
|
||
|
||
//
|
||
// Get the Contains string.
|
||
//
|
||
status = DmQueryMultiSz( groupKey,
|
||
CLUSREG_NAME_GRP_CONTAINS,
|
||
&containsString,
|
||
&containsStringMaxSize,
|
||
&containsStringSize );
|
||
|
||
if ( status != NO_ERROR ) {
|
||
if ( status != ERROR_FILE_NOT_FOUND ) {
|
||
ClRtlLogPrint(LOG_UNUSUAL,
|
||
"[FM] Unable to read Contains for group %1!ws!\n",
|
||
OmObjectId(Group));
|
||
}
|
||
} else {
|
||
//
|
||
// Now Create the Contains list.
|
||
//
|
||
|
||
for ( mszStringIndex = 0; ; mszStringIndex++ ) {
|
||
LPCWSTR nameString;
|
||
PFM_RESOURCE containedResource;
|
||
|
||
nameString = ClRtlMultiSzEnum( containsString,
|
||
containsStringSize/sizeof(WCHAR),
|
||
mszStringIndex );
|
||
|
||
if ( nameString == NULL ) {
|
||
break;
|
||
}
|
||
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] Group %1!ws! contains Resource %2!ws!.\n",
|
||
OmObjectId(Group),
|
||
nameString);
|
||
|
||
//
|
||
// Try to create the object.
|
||
//
|
||
FmpAcquireResourceLock();
|
||
FmpAcquireLocalGroupLock( Group );
|
||
|
||
containedResource = FmpCreateResource( Group,
|
||
nameString,
|
||
NULL,
|
||
Initialize );
|
||
FmpReleaseLocalGroupLock( Group );
|
||
FmpReleaseResourceLock();
|
||
|
||
//
|
||
// Check if we got a resource.
|
||
//
|
||
if ( containedResource == NULL ) {
|
||
//
|
||
// This group claims to contain a non-existent resource.
|
||
// Log an error, but keep going. This should not tank the
|
||
// whole group. Also, let the arbitration code know about
|
||
// the failure of a resource.
|
||
//
|
||
Group->InitFailed = TRUE;
|
||
ClRtlLogPrint(LOG_UNUSUAL,
|
||
"[FM] Failed to find resource %1!ws! for Group %2!ws!\n",
|
||
nameString,
|
||
OmObjectId(Group));
|
||
}
|
||
}
|
||
LocalFree(containsString);
|
||
|
||
}
|
||
|
||
//
|
||
// Get the AutoFailbackType.
|
||
//
|
||
|
||
status = DmQueryDword( groupKey,
|
||
CLUSREG_NAME_GRP_FAILBACK_TYPE,
|
||
&temp,
|
||
&autoFailbackType );
|
||
|
||
//
|
||
// Verify that AutoFailbackType is okay.
|
||
//
|
||
|
||
if ( temp >= FailbackMaximum ) {
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] Illegal value for AutoFailbackType on %1!ws!, setting to default\n",
|
||
OmObjectId(Group));
|
||
temp = autoFailbackType;
|
||
}
|
||
|
||
Group->FailbackType = (UCHAR)temp;
|
||
|
||
//
|
||
// Get the FailbackWindowStart.
|
||
//
|
||
status = DmQueryDword( groupKey,
|
||
CLUSREG_NAME_GRP_FAILBACK_WIN_START,
|
||
&temp,
|
||
&zero );
|
||
|
||
//
|
||
// Verify that FailbackWindowStart is okay.
|
||
//
|
||
if ( temp > 24 ) {
|
||
if ( temp != CLUSTER_GROUP_DEFAULT_FAILBACK_WINDOW_START ) {
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] Illegal value for FailbackWindowStart on %1!ws!,setting to default\n",
|
||
OmObjectId(Group));
|
||
temp = zero;
|
||
}
|
||
}
|
||
Group->FailbackWindowStart = (UCHAR)temp;
|
||
|
||
//
|
||
// Get the FailbackWindowEnd.
|
||
//
|
||
status = DmQueryDword( groupKey,
|
||
CLUSREG_NAME_GRP_FAILBACK_WIN_END,
|
||
&temp,
|
||
&zero );
|
||
|
||
//
|
||
// Verify that FailbackWindowEnd is okay.
|
||
//
|
||
|
||
if ( temp > 24 ) {
|
||
if ( temp != CLUSTER_GROUP_DEFAULT_FAILBACK_WINDOW_END ) {
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] Illegal value for FailbackWindowEnd on %1!ws!, setting to default\n",
|
||
OmObjectId(Group));
|
||
temp = zero;
|
||
}
|
||
}
|
||
Group->FailbackWindowEnd = (UCHAR)temp;
|
||
|
||
//
|
||
// Get the FailoverPeriod.
|
||
//
|
||
status = DmQueryDword( groupKey,
|
||
CLUSREG_NAME_GRP_FAILOVER_PERIOD,
|
||
&temp,
|
||
&failoverPeriod );
|
||
|
||
//
|
||
// Verify that FailoverPeriod is okay. Take any value up to UCHAR max.
|
||
// In theory we could take any value... but in practice we have to convert
|
||
// this time to milliseconds (currently). That means that 1193 hours can
|
||
// fit in a DWORD - so that is the maximum we can take. (We are limited
|
||
// because we use GetTickCount, which returns a DWORD in milliseconds.)
|
||
//
|
||
|
||
if ( temp > CLUSTER_GROUP_MAXIMUM_FAILOVER_PERIOD ) { // Keep it positive?
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] Illegal value for FailolverPeriod on %1!ws!. Max is 1193\n",
|
||
OmObjectId(Group));
|
||
temp = failoverPeriod;
|
||
}
|
||
|
||
Group->FailoverPeriod = (UCHAR)temp;
|
||
|
||
|
||
//
|
||
// Get the FailoverThreshold.
|
||
//
|
||
status = DmQueryDword( groupKey,
|
||
CLUSREG_NAME_GRP_FAILOVER_THRESHOLD,
|
||
&(Group->FailoverThreshold),
|
||
&failoverThreshold );
|
||
|
||
//
|
||
// Verify that FailoverThreshold is okay. Take any value.
|
||
//
|
||
|
||
//
|
||
// Get the AntiAffinityClassName property if present.
|
||
//
|
||
status = DmQueryMultiSz( groupKey,
|
||
CLUSREG_NAME_GRP_ANTI_AFFINITY_CLASS_NAME,
|
||
&Group->lpszAntiAffinityClassName,
|
||
&dwBufferSize,
|
||
&dwStringSize );
|
||
|
||
//
|
||
// Handle the case in which the string is empty.
|
||
//
|
||
if ( ( status == ERROR_SUCCESS ) &&
|
||
( Group->lpszAntiAffinityClassName != NULL ) &&
|
||
( Group->lpszAntiAffinityClassName[0] == L'\0' ) )
|
||
{
|
||
LocalFree( Group->lpszAntiAffinityClassName );
|
||
Group->lpszAntiAffinityClassName = NULL;
|
||
}
|
||
|
||
//
|
||
// We're done. We should only get here if Group->Initialized is FALSE.
|
||
//
|
||
CL_ASSERT( Group->Initialized == FALSE );
|
||
Group->Initialized = TRUE;
|
||
Group->RegistryKey = groupKey;
|
||
|
||
//
|
||
// Now register for any changes to the resource key.
|
||
//
|
||
|
||
status = DmNotifyChangeKey(
|
||
groupKey,
|
||
(DWORD) CLUSTER_CHANGE_ALL,
|
||
FALSE, // Only watch the top of the tree
|
||
&Group->DmRundownList,
|
||
FmpGroupChangeCallback,
|
||
(DWORD_PTR)Group,
|
||
0 );
|
||
|
||
if ( status != ERROR_SUCCESS) {
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] Error registering for DM change notify on group %1!ws!, error %2!u!.\n",
|
||
OmObjectId(Group),
|
||
status);
|
||
goto error_exit;
|
||
}
|
||
|
||
return(ERROR_SUCCESS);
|
||
|
||
|
||
error_exit:
|
||
|
||
Group->Initialized = FALSE;
|
||
Group->RegistryKey = NULL;
|
||
|
||
DmCloseKey(groupKey);
|
||
|
||
//
|
||
// Cleanup any contained resources
|
||
//
|
||
while ( !IsListEmpty(&Group->Contains) ) {
|
||
listEntry = RemoveHeadList(&Group->Contains);
|
||
Resource = CONTAINING_RECORD(listEntry, FM_RESOURCE, ContainsLinkage);
|
||
OmDereferenceObject(Resource);
|
||
}
|
||
|
||
//
|
||
// Cleanup any preferred nodes
|
||
//
|
||
while ( !IsListEmpty(&Group->PreferredOwners) ) {
|
||
listEntry = RemoveHeadList(&Group->PreferredOwners);
|
||
preferredEntry = CONTAINING_RECORD(listEntry, PREFERRED_ENTRY, PreferredLinkage);
|
||
OmDereferenceObject(preferredEntry->PreferredNode);
|
||
LocalFree(preferredEntry);
|
||
}
|
||
|
||
return(status);
|
||
|
||
} // FmpQueryGroupInfo
|
||
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
FmpFixupGroupInfo(
|
||
IN PFM_GROUP Group
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Re-queries Group info from the registry to fixup items that may have
|
||
changed since the quorum resource (and the Group that it is in) was
|
||
first created.
|
||
|
||
This routine exists because we may have created the quorum resource
|
||
(and its Group) early in the 'life' of the cluster, before all the node
|
||
objects (for example) were created. We then would have failed generating
|
||
the list of possible owners for the resource. This in turn would have
|
||
caused some entries from the preferred list to get pruned. We need to
|
||
redo this operation again here.
|
||
|
||
Arguments:
|
||
|
||
Group - A pointer to the Group object to fix up.
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS if successful.
|
||
|
||
Win32 error code otherwise.
|
||
|
||
Notes:
|
||
|
||
It is assumed that the quorum resource fixup already has happened.
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD status;
|
||
|
||
status = FmpQueryGroupNodes(Group, Group->RegistryKey);
|
||
|
||
return(status);
|
||
|
||
} // FmpFixupGroupInfo
|
||
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
FmpQueryResourceInfo(
|
||
IN PVOID Object,
|
||
IN BOOL Initialize
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Queries Resource info from the registry when creating a Resource Object.
|
||
|
||
Arguments:
|
||
|
||
Object - A pointer to the Resource object being created.
|
||
|
||
Initialize - TRUE if the resource should be fully initialized.
|
||
FALSE otherwise.
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS if successful.
|
||
|
||
Win32 error code otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
PFM_RESOURCE Resource = (PFM_RESOURCE)Object;
|
||
DWORD status;
|
||
DWORD dllNameStringSize = 0;
|
||
DWORD dllNameStringMaxSize = 0;
|
||
LPWSTR resourceTypeString = NULL;
|
||
DWORD resourceTypeStringMaxSize = 0;
|
||
DWORD resourceTypeStringSize = 0;
|
||
DWORD dependenciesStringMaxSize = 0;
|
||
DWORD restartThreshold = CLUSTER_RESOURCE_DEFAULT_RESTART_THRESHOLD;
|
||
DWORD restartPeriod = CLUSTER_RESOURCE_DEFAULT_RESTART_PERIOD;
|
||
DWORD pendingTimeout = CLUSTER_RESOURCE_DEFAULT_PENDING_TIMEOUT;
|
||
DWORD RetryPeriodOnFailure = CLUSTER_RESOURCE_DEFAULT_RETRY_PERIOD_ON_FAILURE;
|
||
DWORD defaultRestartAction = RestartGroup;
|
||
DWORD DefaultExFlags = 0;
|
||
DWORD zero = 0;
|
||
DWORD temp;
|
||
DWORD separateMonitor;
|
||
HDMKEY resourceKey;
|
||
DWORD resourceNameStringMaxSize = 0;
|
||
DWORD resourceNameStringSize = 0;
|
||
LPWSTR resourceName = NULL;
|
||
LPWSTR possibleOwnersString = NULL;
|
||
DWORD possibleOwnersStringSize = 0;
|
||
DWORD possibleOwnersStringMaxSize = 0;
|
||
DWORD mszStringIndex;
|
||
PPOSSIBLE_ENTRY possibleEntry;
|
||
PLIST_ENTRY listEntry;
|
||
WCHAR unknownName[] = L"_Unknown9999";
|
||
DWORD nameSize = 0;
|
||
DWORD stringSize;
|
||
|
||
//if the key is non null, this resource has already been initialized
|
||
if (Resource->RegistryKey != NULL)
|
||
return(ERROR_SUCCESS);
|
||
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] Initializing resource %1!ws! from the registry.\n",
|
||
OmObjectId(Resource));
|
||
|
||
//
|
||
// Begin initializing the resource from the registry.
|
||
//
|
||
//
|
||
// Open the resource key.
|
||
//
|
||
resourceKey = DmOpenKey( DmResourcesKey,
|
||
OmObjectId(Resource),
|
||
MAXIMUM_ALLOWED );
|
||
|
||
if ( resourceKey == NULL ) {
|
||
status = GetLastError();
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] Unable to open resource key %1!ws!, %2!u!\n",
|
||
OmObjectId(Resource),
|
||
status);
|
||
return(ERROR_INVALID_NAME);
|
||
}
|
||
|
||
//
|
||
// Read the required resource values. The strings will be allocated
|
||
// by the DmQuery* functions.
|
||
//
|
||
|
||
//
|
||
// Get the Name.
|
||
//
|
||
status = DmQuerySz( resourceKey,
|
||
CLUSREG_NAME_RES_NAME,
|
||
&resourceName,
|
||
&resourceNameStringMaxSize,
|
||
&resourceNameStringSize );
|
||
|
||
if (status != ERROR_SUCCESS) {
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] Unable to read name for resource %1!ws!\n",
|
||
OmObjectId(Resource));
|
||
if ( OmObjectName( Resource ) == NULL ) {
|
||
wsprintf( unknownName,
|
||
L"_Unknown%u",
|
||
InterlockedIncrement( &FmpUnknownCount ));
|
||
status = OmSetObjectName( Resource, unknownName );
|
||
} else {
|
||
status = ERROR_SUCCESS;
|
||
}
|
||
} else {
|
||
status = OmSetObjectName( Resource, resourceName );
|
||
}
|
||
if ( status != ERROR_SUCCESS ) {
|
||
ClRtlLogPrint(LOG_ERROR,
|
||
"[FM] Unable to set name %1!ws! for resource %2!ws!, error %3!u!.\n",
|
||
resourceName,
|
||
OmObjectId(Resource),
|
||
status );
|
||
LocalFree(resourceName);
|
||
status = ERROR_INVALID_NAME;
|
||
goto error_exit;
|
||
}
|
||
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] Name for Resource %1!ws! is '%2!ws!'.\n",
|
||
OmObjectId(Resource),
|
||
resourceName);
|
||
|
||
LocalFree(resourceName);
|
||
|
||
//
|
||
// Get the dependencies list.
|
||
//
|
||
|
||
status = DmQueryMultiSz( resourceKey,
|
||
CLUSREG_NAME_RES_DEPENDS_ON,
|
||
&(Resource->Dependencies),
|
||
&dependenciesStringMaxSize,
|
||
&(Resource->DependenciesSize) );
|
||
|
||
if (status != NO_ERROR) {
|
||
if ( status != ERROR_FILE_NOT_FOUND ) {
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] Unable to read Dependencies for resource %1!ws!\n",
|
||
OmObjectId(Resource));
|
||
}
|
||
}
|
||
|
||
//
|
||
// Get the OPTIONAL PossibleOwners list.
|
||
//
|
||
// We do this here, because we must have a possible owners list for the
|
||
// CluAdmin to start the resource.
|
||
//
|
||
|
||
status = DmQueryMultiSz( resourceKey,
|
||
CLUSREG_NAME_RES_POSSIBLE_OWNERS,
|
||
&possibleOwnersString,
|
||
&possibleOwnersStringMaxSize,
|
||
&possibleOwnersStringSize );
|
||
|
||
if ( status == NO_ERROR ) {
|
||
|
||
//
|
||
// Now Create the Possible Owners list.
|
||
//
|
||
|
||
for ( mszStringIndex = 0; ; mszStringIndex++ ) {
|
||
LPCWSTR nameString;
|
||
PNM_NODE possibleNode;
|
||
|
||
nameString = ClRtlMultiSzEnum( possibleOwnersString,
|
||
possibleOwnersStringSize/sizeof(WCHAR),
|
||
mszStringIndex );
|
||
|
||
if ( nameString == NULL ) {
|
||
break;
|
||
}
|
||
possibleNode = OmReferenceObjectById( ObjectTypeNode,
|
||
nameString );
|
||
|
||
if ( possibleNode == NULL ) {
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] Warning, failed to find node %1!ws! for Resource %2!ws!\n",
|
||
nameString,
|
||
OmObjectId(Resource));
|
||
} else {
|
||
Resource->PossibleList = TRUE;
|
||
status = FmpAddPossibleEntry(Resource, possibleNode);
|
||
OmDereferenceObject(possibleNode);
|
||
if (status != ERROR_SUCCESS) {
|
||
goto error_exit;
|
||
}
|
||
}
|
||
}
|
||
LocalFree(possibleOwnersString);
|
||
|
||
//
|
||
// Now prune out unusable nodes from the preferred owners list.
|
||
//
|
||
FmpPrunePreferredList( Resource );
|
||
|
||
} else {
|
||
//
|
||
// No possible owners value was specified. Add all the nodes
|
||
// to the possible owners list. Note there is no point in pruning
|
||
// the preferred list after this since this resource can run
|
||
// anywhere.
|
||
//
|
||
OmEnumObjects( ObjectTypeNode,
|
||
FmpEnumAddAllOwners,
|
||
Resource,
|
||
NULL );
|
||
}
|
||
|
||
//
|
||
// Get the resource type.
|
||
//
|
||
status = DmQuerySz( resourceKey,
|
||
CLUSREG_NAME_RES_TYPE,
|
||
&resourceTypeString,
|
||
&resourceTypeStringMaxSize,
|
||
&resourceTypeStringSize );
|
||
|
||
if (status != ERROR_SUCCESS) {
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] Unable to read ResourceType for resource %1!ws!\n",
|
||
OmObjectId(Resource));
|
||
goto error_exit;
|
||
}
|
||
|
||
//
|
||
// Open (and reference) the resource type.
|
||
//
|
||
if (Resource->Type == NULL)
|
||
{
|
||
Resource->Type = OmReferenceObjectById( ObjectTypeResType,
|
||
resourceTypeString );
|
||
}
|
||
if (Resource->Type == NULL) {
|
||
|
||
PFM_RESTYPE pResType;
|
||
//
|
||
// If we can't find a resource type, then try to create it.
|
||
//
|
||
pResType = FmpCreateResType(resourceTypeString );
|
||
|
||
if (pResType == NULL) {
|
||
status = ERROR_INVALID_PARAMETER;
|
||
LocalFree(resourceTypeString);
|
||
goto error_exit;
|
||
}
|
||
|
||
//bump the ref count before saving a pointer to it in the
|
||
//resource structure.
|
||
OmReferenceObject(pResType);
|
||
Resource->Type = pResType;
|
||
}
|
||
|
||
LocalFree(resourceTypeString);
|
||
|
||
if ( !Initialize ) {
|
||
//
|
||
// We're not supposed to fully initialize the resource. This is
|
||
// when we're early in the init process. We need to keep the registry
|
||
// key closed when leaving.
|
||
//
|
||
DmCloseKey(resourceKey);
|
||
return(ERROR_SUCCESS);
|
||
}
|
||
|
||
|
||
//
|
||
// Get the IsAlive poll interval
|
||
//
|
||
CL_ASSERT( Resource->Type->IsAlivePollInterval != 0 );
|
||
status = DmQueryDword( resourceKey,
|
||
CLUSREG_NAME_RES_IS_ALIVE,
|
||
&Resource->IsAlivePollInterval,
|
||
&Resource->Type->IsAlivePollInterval );
|
||
|
||
if ( status != NO_ERROR ) {
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] Unable to read IsAlivePollInterval for resource %1!ws!. Error %2!u!\n",
|
||
OmObjectId(Resource),
|
||
status);
|
||
|
||
goto error_exit;
|
||
}
|
||
|
||
if ( Resource->IsAlivePollInterval == CLUSTER_RESOURCE_USE_DEFAULT_POLL_INTERVAL ) {
|
||
Resource->IsAlivePollInterval = Resource->Type->IsAlivePollInterval;
|
||
}
|
||
|
||
//
|
||
// Get the LooksAlive poll interval
|
||
//
|
||
CL_ASSERT( Resource->Type->LooksAlivePollInterval != 0 );
|
||
status = DmQueryDword( resourceKey,
|
||
CLUSREG_NAME_RES_LOOKS_ALIVE,
|
||
&Resource->LooksAlivePollInterval,
|
||
&Resource->Type->LooksAlivePollInterval );
|
||
|
||
if ( status != NO_ERROR ) {
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] Unable to read LooksAlivePollInterval for resource %1!ws!. Error %2!u!\n",
|
||
OmObjectId(Resource),
|
||
status);
|
||
goto error_exit;
|
||
}
|
||
|
||
if ( Resource->LooksAlivePollInterval == CLUSTER_RESOURCE_USE_DEFAULT_POLL_INTERVAL ) {
|
||
Resource->LooksAlivePollInterval = Resource->Type->LooksAlivePollInterval;
|
||
}
|
||
|
||
//
|
||
// Get the current persistent state of the resource.
|
||
//
|
||
status = DmQueryDword( resourceKey,
|
||
CLUSREG_NAME_RES_PERSISTENT_STATE,
|
||
&temp,
|
||
NULL );
|
||
|
||
//
|
||
// Save the current resource state.
|
||
//
|
||
|
||
if ( ( status == ERROR_FILE_NOT_FOUND ) ||
|
||
( ( status == ERROR_SUCCESS ) && ( temp == CLUSTER_RESOURCE_DEFAULT_PERSISTENT_STATE ) ) ) {
|
||
switch ( Resource->Group->PersistentState ) {
|
||
case ClusterGroupOnline:
|
||
Resource->PersistentState = ClusterResourceOnline;
|
||
break;
|
||
case ClusterGroupOffline:
|
||
Resource->PersistentState = ClusterResourceOffline;
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
} else if ( status != NO_ERROR ) {
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] Unable to read PersistentState for resource %1!ws!. Error %2!u!\n",
|
||
OmObjectId(Resource),
|
||
status);
|
||
goto error_exit;
|
||
} else if ( temp ) {
|
||
Resource->PersistentState = ClusterResourceOnline;
|
||
} else {
|
||
Resource->PersistentState = ClusterResourceOffline;
|
||
}
|
||
|
||
//
|
||
// Determine the monitor to run this in.
|
||
//
|
||
status = DmQueryDword( resourceKey,
|
||
CLUSREG_NAME_RES_SEPARATE_MONITOR,
|
||
&separateMonitor,
|
||
&zero );
|
||
if ( separateMonitor ) {
|
||
Resource->Flags |= RESOURCE_SEPARATE_MONITOR;
|
||
}
|
||
|
||
//
|
||
// Get the RestartThreshold.
|
||
//
|
||
|
||
status = DmQueryDword( resourceKey,
|
||
CLUSREG_NAME_RES_RESTART_THRESHOLD,
|
||
&Resource->RestartThreshold,
|
||
&restartThreshold );
|
||
|
||
// Verify the RestartThreshold. Take any value.
|
||
|
||
//
|
||
// Get the RestartPeriod.
|
||
//
|
||
|
||
status = DmQueryDword( resourceKey,
|
||
CLUSREG_NAME_RES_RESTART_PERIOD,
|
||
&Resource->RestartPeriod,
|
||
&restartPeriod );
|
||
|
||
// Verify the RestartPeriod. Take any value.
|
||
|
||
//
|
||
// Get the RestartAction.
|
||
//
|
||
|
||
status = DmQueryDword( resourceKey,
|
||
CLUSREG_NAME_RES_RESTART_ACTION,
|
||
&Resource->RestartAction,
|
||
&defaultRestartAction );
|
||
|
||
// Verify the RestartAction.
|
||
|
||
if ( Resource->RestartAction >= RestartMaximum ) {
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] Illegal RestartAction for resource %1!ws!\n",
|
||
OmObjectId(Resource));
|
||
goto error_exit;
|
||
}
|
||
|
||
status = DmQueryDword( resourceKey,
|
||
CLUSREG_NAME_RES_RETRY_PERIOD_ON_FAILURE,
|
||
&Resource->RetryPeriodOnFailure,
|
||
&RetryPeriodOnFailure );
|
||
|
||
// make sure that RetryPeriodOnFailure >= RestartPeriod
|
||
if (Resource->RetryPeriodOnFailure < Resource->RestartPeriod)
|
||
{
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] Specified RetryPeriodOnFailure value is less than RestartPeriod value - setting RetryPeriodOnFailure equal to RestartPeriod \n");
|
||
Resource->RetryPeriodOnFailure = Resource->RestartPeriod;
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// Get the extrinsic Flags
|
||
//
|
||
DefaultExFlags = 0;
|
||
status = DmQueryDword( resourceKey,
|
||
CLUSREG_NAME_FLAGS,
|
||
&Resource->ExFlags,
|
||
&DefaultExFlags );
|
||
|
||
if ( status != NO_ERROR ) {
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] Unable to read Extrinsic Flags for resource %1!ws!. Error %2!u!\n",
|
||
OmObjectId(Resource),
|
||
status);
|
||
|
||
goto error_exit;
|
||
}
|
||
|
||
//
|
||
// Get the PendingTimeout value.
|
||
//
|
||
|
||
status = DmQueryDword( resourceKey,
|
||
CLUSREG_NAME_RES_PENDING_TIMEOUT,
|
||
&Resource->PendingTimeout,
|
||
&pendingTimeout );
|
||
|
||
// Verify the PendingTimeout. Take any value.
|
||
|
||
//
|
||
// Now register for any changes to the resource key.
|
||
//
|
||
|
||
if (IsListEmpty(&Resource->DmRundownList))
|
||
{
|
||
status = DmNotifyChangeKey(
|
||
resourceKey,
|
||
(DWORD) CLUSTER_CHANGE_ALL,
|
||
FALSE, // Only watch the top of the tree
|
||
&Resource->DmRundownList,
|
||
FmpResourceChangeCallback,
|
||
(DWORD_PTR)Resource,
|
||
0 );
|
||
|
||
if ( status != ERROR_SUCCESS) {
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] Error registering for DM change notify on resource %1!ws!, error %2!u!.\n",
|
||
OmObjectId(Resource),
|
||
status);
|
||
goto error_exit;
|
||
}
|
||
}
|
||
//
|
||
// Get the DebugPrefix string... this is on the resource type.
|
||
//
|
||
status = DmQuerySz( resourceKey,
|
||
CLUSREG_NAME_RES_DEBUG_PREFIX,
|
||
&Resource->DebugPrefix,
|
||
&nameSize,
|
||
&stringSize );
|
||
|
||
//
|
||
// Finally save the resource key for registry updates of the
|
||
// PersistentState.
|
||
//
|
||
Resource->RegistryKey = resourceKey;
|
||
|
||
return(ERROR_SUCCESS);
|
||
|
||
|
||
error_exit:
|
||
|
||
DmCloseKey(resourceKey);
|
||
|
||
if ( Resource->Type != NULL ) {
|
||
OmDereferenceObject(Resource->Type);
|
||
}
|
||
|
||
//
|
||
// Cleanup any dependencies
|
||
//
|
||
if ( Resource->Dependencies != NULL ) {
|
||
LocalFree(Resource->Dependencies);
|
||
Resource->Dependencies = NULL;
|
||
}
|
||
|
||
//
|
||
// Cleanup any possible nodes
|
||
//
|
||
while ( !IsListEmpty(&Resource->PossibleOwners) ) {
|
||
listEntry = RemoveHeadList(&Resource->PossibleOwners);
|
||
possibleEntry = CONTAINING_RECORD(listEntry, POSSIBLE_ENTRY, PossibleLinkage);
|
||
OmDereferenceObject(possibleEntry->PossibleNode);
|
||
LocalFree(possibleEntry);
|
||
}
|
||
|
||
return(status);
|
||
|
||
} // FmpQueryResourceInfo
|
||
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
FmpFixupResourceInfo(
|
||
IN PFM_RESOURCE Resource
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Re-queries Resource info from the registry to fixup items that may have
|
||
changed since the quorum resource was first created.
|
||
|
||
This routine exists because we may have created the quorum resource early
|
||
in the 'life' of the cluster, before all the node objects (for example)
|
||
were created. We then would have failed generating the list of possible
|
||
owners for the resource. In FmpQueryResourceInfo, we treat failures to
|
||
find node objects as non-fatal errors, which we will now cleanup.
|
||
|
||
Arguments:
|
||
|
||
Resource - A pointer to the Resource object to fix up.
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS if successful.
|
||
|
||
Win32 error code otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
LPWSTR possibleOwnersString = NULL;
|
||
DWORD possibleOwnersStringSize = 0;
|
||
DWORD possibleOwnersStringMaxSize = 0;
|
||
DWORD mszStringIndex;
|
||
DWORD status;
|
||
|
||
|
||
if ( Resource->RegistryKey == NULL ) {
|
||
return(ERROR_NOT_READY);
|
||
}
|
||
|
||
//
|
||
// Get the OPTIONAL PossibleOwners list.
|
||
//
|
||
|
||
status = DmQueryMultiSz( Resource->RegistryKey,
|
||
CLUSREG_NAME_RES_POSSIBLE_OWNERS,
|
||
&possibleOwnersString,
|
||
&possibleOwnersStringMaxSize,
|
||
&possibleOwnersStringSize );
|
||
|
||
if ( status == NO_ERROR ) {
|
||
|
||
//
|
||
// Now Create the Possible Owners list.
|
||
//
|
||
|
||
for ( mszStringIndex = 0; ; mszStringIndex++ ) {
|
||
LPCWSTR nameString;
|
||
PNM_NODE possibleNode;
|
||
|
||
nameString = ClRtlMultiSzEnum( possibleOwnersString,
|
||
possibleOwnersStringSize/sizeof(WCHAR),
|
||
mszStringIndex );
|
||
|
||
if ( nameString == NULL ) {
|
||
break;
|
||
}
|
||
possibleNode = OmReferenceObjectById( ObjectTypeNode,
|
||
nameString );
|
||
|
||
if ( possibleNode == NULL ) {
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] Warning, failed to find node %1!ws! for Resource %2!ws!\n",
|
||
nameString,
|
||
OmObjectId(Resource));
|
||
} else {
|
||
Resource->PossibleList = TRUE;
|
||
status = FmpAddPossibleEntry(Resource, possibleNode);
|
||
OmDereferenceObject(possibleNode);
|
||
if (status != ERROR_SUCCESS) {
|
||
return(status);
|
||
}
|
||
}
|
||
}
|
||
LocalFree(possibleOwnersString);
|
||
|
||
//
|
||
// Now prune out unusable nodes from the preferred owners list.
|
||
//
|
||
FmpPrunePreferredList( Resource );
|
||
|
||
} else {
|
||
//
|
||
// No possible owners value was specified. Add all the nodes
|
||
// to the possible owners list. Note there is no point in pruning
|
||
// the preferred list after this since this resource can run
|
||
// anywhere.
|
||
//
|
||
OmEnumObjects( ObjectTypeNode,
|
||
FmpEnumAddAllOwners,
|
||
Resource,
|
||
NULL );
|
||
|
||
}
|
||
|
||
return(ERROR_SUCCESS);
|
||
|
||
} // FmpFixupQuorumResourceInfo
|
||
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
FmpQueryResTypeInfo(
|
||
IN PVOID Object
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Queries Resource Type info from the registry when creating a ResType Object.
|
||
|
||
Arguments:
|
||
|
||
Object - A pointer to the Resource Type object being created.
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS if successful.
|
||
|
||
Win32 error code otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
PFM_RESTYPE resType = (PFM_RESTYPE)Object;
|
||
DWORD status;
|
||
DWORD dwSize = 0;
|
||
DWORD stringSize;
|
||
HDMKEY resTypeKey;
|
||
DWORD temp;
|
||
LPWSTR pmszPossibleNodes = NULL;
|
||
|
||
//
|
||
// Open the resource type key.
|
||
//
|
||
resTypeKey = DmOpenKey( DmResourceTypesKey,
|
||
OmObjectId(resType),
|
||
MAXIMUM_ALLOWED );
|
||
|
||
if ( resTypeKey == NULL ) {
|
||
status = GetLastError();
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] Unable to open resource type key %1!ws!, %2!u!\n",
|
||
OmObjectId(resType),
|
||
status);
|
||
|
||
return(status);
|
||
}
|
||
|
||
//
|
||
// Read the required resource type DLL name. The strings will be allocated
|
||
// by the DmQuery* functions.
|
||
//
|
||
|
||
status = DmQuerySz( resTypeKey,
|
||
CLUSREG_NAME_RESTYPE_DLL_NAME,
|
||
&resType->DllName,
|
||
&dwSize,
|
||
&stringSize );
|
||
if ( status != NO_ERROR ) {
|
||
if ( status == ERROR_FILE_NOT_FOUND ) {
|
||
ClRtlLogPrint(LOG_CRITICAL,
|
||
"[FM] The DllName value for the %1!ws! resource type does not exist. "
|
||
"Resources of this type will not be monitored.\n",
|
||
OmObjectId(resType));
|
||
}
|
||
else {
|
||
ClRtlLogPrint(LOG_CRITICAL,
|
||
"[FM] The DllName value for the %1!ws! resource type could not be read "
|
||
"from the registry. Resources of this type will not be monitored. "
|
||
"The error was %2!d!.\n",
|
||
OmObjectId(resType),
|
||
status);
|
||
}
|
||
|
||
goto error_exit;
|
||
}
|
||
|
||
|
||
//
|
||
// Get the optional LooksAlive poll interval
|
||
//
|
||
status = DmQueryDword( resTypeKey,
|
||
CLUSREG_NAME_RESTYPE_LOOKS_ALIVE,
|
||
&resType->LooksAlivePollInterval,
|
||
NULL );
|
||
|
||
if ( status != NO_ERROR ) {
|
||
if ( status == ERROR_FILE_NOT_FOUND ) {
|
||
resType->LooksAlivePollInterval = CLUSTER_RESTYPE_DEFAULT_LOOKS_ALIVE;
|
||
} else {
|
||
ClRtlLogPrint(LOG_CRITICAL,
|
||
"[FM] The LooksAlive poll interval for the %1!ws! resource type could "
|
||
"not be read from the registry. Resources of this type will not be "
|
||
"monitored. The error was %2!d!.\n",
|
||
OmObjectId(resType),
|
||
status);
|
||
goto error_exit;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Get the optional IsAlive poll interval
|
||
//
|
||
status = DmQueryDword( resTypeKey,
|
||
CLUSREG_NAME_RESTYPE_IS_ALIVE,
|
||
&resType->IsAlivePollInterval,
|
||
NULL );
|
||
|
||
if ( status != NO_ERROR ) {
|
||
if ( status == ERROR_FILE_NOT_FOUND ) {
|
||
resType->IsAlivePollInterval = CLUSTER_RESTYPE_DEFAULT_IS_ALIVE;
|
||
} else {
|
||
ClRtlLogPrint(LOG_CRITICAL,
|
||
"[FM] The IsAlive poll interval for the %1!ws! resource type "
|
||
"could not be read from the registry. Resources of this type "
|
||
"will not be monitored. The error was %2!d!.\n",
|
||
OmObjectId(resType),
|
||
status);
|
||
goto error_exit;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Get the optional DebugPrefix string... this is on the resource type.
|
||
//
|
||
dwSize = 0;
|
||
status = DmQuerySz( resTypeKey,
|
||
CLUSREG_NAME_RESTYPE_DEBUG_PREFIX,
|
||
&resType->DebugPrefix,
|
||
&dwSize,
|
||
&stringSize );
|
||
|
||
//
|
||
// Get the optional DebugControlFunctions registry value
|
||
//
|
||
resType->Flags &= ~RESTYPE_DEBUG_CONTROL_FUNC;
|
||
temp = 0;
|
||
status = DmQueryDword( resTypeKey,
|
||
CLUSREG_NAME_RESTYPE_DEBUG_CTRLFUNC,
|
||
&temp,
|
||
NULL );
|
||
|
||
if ( status != NO_ERROR ) {
|
||
if ( status != ERROR_FILE_NOT_FOUND ) {
|
||
ClRtlLogPrint(LOG_CRITICAL,
|
||
"[FM] The Debug control functions for the %1!ws! resource type "
|
||
"could not be read from the registry. Resources of this type "
|
||
"will not be monitored. The error was %2!d!.\n",
|
||
OmObjectId(resType),
|
||
status);
|
||
goto error_exit;
|
||
}
|
||
}
|
||
|
||
if ( temp ) {
|
||
resType->Flags |= RESTYPE_DEBUG_CONTROL_FUNC;
|
||
}
|
||
|
||
|
||
//ss: bug make sure you free the old memory
|
||
InitializeListHead(&(resType->PossibleNodeList));
|
||
|
||
//
|
||
// Get the Possible Nodes
|
||
//
|
||
dwSize = 0;
|
||
status = DmQueryMultiSz( resTypeKey,
|
||
CLUSREG_NAME_RESTYPE_POSSIBLE_NODES,
|
||
&pmszPossibleNodes,
|
||
&dwSize,
|
||
&stringSize);
|
||
|
||
|
||
if ( status != NO_ERROR )
|
||
{
|
||
//if the possible node list is not found this is ok
|
||
if ( status != ERROR_FILE_NOT_FOUND )
|
||
{
|
||
ClRtlLogPrint(LOG_CRITICAL,
|
||
"[FM] The Possible nodes list for the %1!ws! resource type "
|
||
"could not be read from the registry. Resources of this type "
|
||
"will not be monitored. The error was %2!d!.\n",
|
||
OmObjectId(resType),
|
||
status);
|
||
goto error_exit;
|
||
}
|
||
}
|
||
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] FmpQueryResTypeInfo: Calling FmpAddPossibleNodeToList for restype %1!ws!\r\n",
|
||
OmObjectId(resType));
|
||
|
||
status = FmpAddPossibleNodeToList(pmszPossibleNodes, stringSize,
|
||
&resType->PossibleNodeList);
|
||
if ( status != ERROR_SUCCESS )
|
||
{
|
||
ClRtlLogPrint(LOG_CRITICAL,
|
||
"[FM] FmpCreateResType: FmpAddPossibleNodeToList() failed, status=%1!u!\r\n",
|
||
status);
|
||
goto error_exit;
|
||
}
|
||
|
||
|
||
error_exit:
|
||
if (pmszPossibleNodes) LocalFree(pmszPossibleNodes);
|
||
DmCloseKey(resTypeKey);
|
||
|
||
return(status);
|
||
|
||
} // FmpQueryResTypeInfo
|
||
|
||
|
||
|
||
VOID
|
||
FmpGroupChangeCallback(
|
||
IN DWORD_PTR Context1,
|
||
IN DWORD_PTR Context2,
|
||
IN DWORD CompletionFilter,
|
||
IN LPCWSTR RelativeName
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine basically flushes our cached data for the given group.
|
||
|
||
Arguments:
|
||
|
||
Context1 - A pointer to the Group object that was modified.
|
||
|
||
Context2 - Not used.
|
||
|
||
CompletionFilter - Not used.
|
||
|
||
RelativeName - The registry path relative to the entry that was modified.
|
||
Not used.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PFM_GROUP Group = (PFM_GROUP)Context1;
|
||
HDMKEY groupKey;
|
||
DWORD status;
|
||
DWORD temp;
|
||
BOOL notify = FALSE;
|
||
DWORD dwBufferSize = 0;
|
||
DWORD dwStringSize;
|
||
|
||
groupKey = Group->RegistryKey;
|
||
if ( groupKey == NULL ) {
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Re-fetch all of the data for the group.
|
||
//
|
||
// Name changes are managed elsewhere.
|
||
// The Contains list is managed elsewhere.
|
||
//
|
||
|
||
//
|
||
// Get the OPTIONAL PreferredOwners list.
|
||
// *** NOTE *** This MUST be done before processing the contains list!
|
||
//
|
||
status = FmpQueryGroupNodes(Group, groupKey);
|
||
if (status != ERROR_SUCCESS) {
|
||
ClRtlLogPrint(LOG_UNUSUAL,"[FM] Error %1!d! refreshing preferred owners list\n",status);
|
||
}
|
||
|
||
//
|
||
// Get the AutoFailbackType.
|
||
//
|
||
temp = Group->FailbackType;
|
||
status = DmQueryDword( groupKey,
|
||
CLUSREG_NAME_GRP_FAILBACK_TYPE,
|
||
&temp,
|
||
&temp );
|
||
|
||
//
|
||
// Verify that AutoFailbackType is okay.
|
||
//
|
||
|
||
if ( temp >= FailbackMaximum ) {
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] Illegal refresh value for AutoFailbackType on %1!ws!\n",
|
||
OmObjectId(Group));
|
||
} else {
|
||
if ( (UCHAR)temp != Group->FailbackType ) {
|
||
notify = TRUE;
|
||
}
|
||
Group->FailbackType = (UCHAR)temp;
|
||
}
|
||
|
||
//
|
||
// Get the FailbackWindowStart.
|
||
//
|
||
temp = Group->FailbackWindowStart;
|
||
status = DmQueryDword( groupKey,
|
||
CLUSREG_NAME_GRP_FAILBACK_WIN_START,
|
||
&temp,
|
||
&temp );
|
||
|
||
//
|
||
// Verify that FailbackWindowStart is okay.
|
||
//
|
||
|
||
if ( temp > 24 ) {
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] Illegal refresh value for FailbackWindowStart on %1!ws!\n",
|
||
OmObjectId(Group));
|
||
} else {
|
||
if ( (UCHAR)temp != Group->FailbackWindowStart ) {
|
||
notify = TRUE;
|
||
}
|
||
Group->FailbackWindowStart = (UCHAR)temp;
|
||
}
|
||
|
||
//
|
||
// Get the FailbackWindowEnd.
|
||
//
|
||
temp = Group->FailbackWindowEnd;
|
||
status = DmQueryDword( groupKey,
|
||
CLUSREG_NAME_GRP_FAILBACK_WIN_END,
|
||
&temp,
|
||
&temp );
|
||
|
||
//
|
||
// Verify that FailbackWindowEnd is okay.
|
||
//
|
||
|
||
if ( temp > 24 ) {
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] Illegal refresh value for FailbackWindowEnd on %1!ws!\n",
|
||
OmObjectId(Group));
|
||
} else {
|
||
if ( (UCHAR)temp != Group->FailbackWindowEnd ) {
|
||
notify = TRUE;
|
||
}
|
||
Group->FailbackWindowEnd = (UCHAR)temp;
|
||
}
|
||
|
||
//
|
||
// Get the FailoverPeriod.
|
||
//
|
||
temp = Group->FailoverPeriod;
|
||
status = DmQueryDword( groupKey,
|
||
CLUSREG_NAME_GRP_FAILOVER_PERIOD,
|
||
&temp,
|
||
&temp );
|
||
|
||
//
|
||
// Verify that FailoverPeriod is okay. Take any value up to UCHAR max.
|
||
// In theory we could take any value... but in practice we have to convert
|
||
// this time to milliseconds (currently). That means that 1193 hours can
|
||
// fit in a DWORD - so that is the maximum we can take. (We are limited
|
||
// because we use GetTickCount, which returns a DWORD in milliseconds.)
|
||
//
|
||
|
||
if ( temp > (1193) ) { // we dont bother Keeping it positive?
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] Illegal refresh value for FailolverPeriod on %1!ws!. Max is 596\n",
|
||
OmObjectId(Group));
|
||
} else {
|
||
if ( (UCHAR)temp != Group->FailoverPeriod ) {
|
||
notify = TRUE;
|
||
}
|
||
Group->FailoverPeriod = (UCHAR)temp;
|
||
}
|
||
|
||
//
|
||
// Get the FailoverThreshold.
|
||
//
|
||
status = DmQueryDword( groupKey,
|
||
CLUSREG_NAME_GRP_FAILOVER_THRESHOLD,
|
||
&(Group->FailoverThreshold),
|
||
&(Group->FailoverThreshold) );
|
||
|
||
//
|
||
// Verify that FailoverThreshold is okay. Take any value.
|
||
//
|
||
|
||
//
|
||
// Get the current persistent state of the group.
|
||
//
|
||
if ( Group->PersistentState == ClusterGroupOnline ) {
|
||
temp = 1;
|
||
} else {
|
||
temp = 0;
|
||
}
|
||
status = DmQueryDword( groupKey,
|
||
CLUSREG_NAME_GRP_PERSISTENT_STATE,
|
||
&temp,
|
||
&temp );
|
||
//
|
||
// If the group state is non-zero then we go online.
|
||
//
|
||
// Don't bother with change notifications... they should happen elsewhere.
|
||
//
|
||
if ( temp ) {
|
||
if ( ClusterGroupOnline != Group->PersistentState ) {
|
||
//notify = TRUE;
|
||
}
|
||
Group->PersistentState = ClusterGroupOnline;
|
||
} else {
|
||
if ( ClusterGroupOffline != Group->PersistentState ) {
|
||
//notify = TRUE;
|
||
}
|
||
Group->PersistentState = ClusterGroupOffline;
|
||
}
|
||
|
||
//
|
||
// Get the AntiAffinityClassName property if present.
|
||
//
|
||
LocalFree( Group->lpszAntiAffinityClassName );
|
||
Group->lpszAntiAffinityClassName = NULL;
|
||
status = DmQueryMultiSz( groupKey,
|
||
CLUSREG_NAME_GRP_ANTI_AFFINITY_CLASS_NAME,
|
||
&Group->lpszAntiAffinityClassName,
|
||
&dwBufferSize,
|
||
&dwStringSize );
|
||
|
||
//
|
||
// Handle the case in which the string is empty.
|
||
//
|
||
if ( ( status == ERROR_SUCCESS ) &&
|
||
( Group->lpszAntiAffinityClassName != NULL ) &&
|
||
( Group->lpszAntiAffinityClassName[0] == L'\0' ) )
|
||
{
|
||
LocalFree( Group->lpszAntiAffinityClassName );
|
||
Group->lpszAntiAffinityClassName = NULL;
|
||
}
|
||
|
||
// We're done!
|
||
if ( !FmpShutdown &&
|
||
notify ) {
|
||
ClusterEvent( CLUSTER_EVENT_GROUP_PROPERTY_CHANGE, Group );
|
||
}
|
||
|
||
return;
|
||
|
||
} // FmpGroupChangeCallback
|
||
|
||
|
||
|
||
VOID
|
||
FmpResourceChangeCallback(
|
||
IN DWORD_PTR Context1,
|
||
IN DWORD_PTR Context2,
|
||
IN DWORD CompletionFilter,
|
||
IN LPCWSTR RelativeName
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine basically flushes our cached data for the given resource.
|
||
|
||
Arguments:
|
||
|
||
Context1 - A pointer to the resource object that was modified.
|
||
|
||
Context2 - Not used.
|
||
|
||
CompletionFilter - Not used.
|
||
|
||
RelativeName - The registry path relative to the entry that was modified.
|
||
Not used.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PFM_RESOURCE Resource = (PFM_RESOURCE)Context1;
|
||
HDMKEY resourceKey;
|
||
DWORD status;
|
||
DWORD separateMonitor;
|
||
DWORD zero = 0;
|
||
DWORD temp;
|
||
BOOL notify = FALSE;
|
||
DWORD dwDefault;
|
||
|
||
resourceKey = Resource->RegistryKey;
|
||
if ( resourceKey == NULL ) {
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Re-fetch all of the data for the resource.
|
||
//
|
||
// Name changes are managed elsewhere.
|
||
// The dependency list is managed elsewhere.
|
||
//
|
||
// We can't change the resource type here!
|
||
// We can't stop the resource to start it in a separate monitor either.
|
||
//
|
||
|
||
#if 0
|
||
//
|
||
// Get the Name.
|
||
//
|
||
status = DmQuerySz( resourceKey,
|
||
CLUSREG_NAME_RES_NAME,
|
||
&resourceName,
|
||
&resourceNameStringMaxSize,
|
||
&resourceNameStringSize );
|
||
|
||
if (status != ERROR_SUCCESS) {
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] Unable to read name for resource %1!ws!\n",
|
||
OmObjectId(Resource));
|
||
if ( OmObjectName( Resource ) == NULL ) {
|
||
wsprintf( unknownName,
|
||
L"_Unknown%u",
|
||
InterlockedIncrement( &FmpUnknownCount ));
|
||
status = OmSetObjectName( Resource, unknownName );
|
||
} else {
|
||
status = ERROR_SUCCESS;
|
||
}
|
||
} else {
|
||
status = OmSetObjectName( Resource, resourceName );
|
||
}
|
||
|
||
if ( status != ERROR_SUCCESS ) {
|
||
ClRtlLogPrint(LOG_ERROR,
|
||
"[FM] Unable to set name %1!ws! for resource %2!ws!, error %3!u!.\n",
|
||
resourceName,
|
||
OmObjectId(Resource),
|
||
status );
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// Get the IsAlive poll interval
|
||
//
|
||
temp = Resource->IsAlivePollInterval;
|
||
dwDefault = CLUSTER_RESOURCE_DEFAULT_IS_ALIVE;
|
||
status = DmQueryDword( resourceKey,
|
||
CLUSREG_NAME_RES_IS_ALIVE,
|
||
&Resource->IsAlivePollInterval,
|
||
&dwDefault );
|
||
|
||
if ( status != NO_ERROR ) {
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] Unable to refresh IsAlivePollInterval for resource %1!ws!. Error %2!u!\n",
|
||
OmObjectId(Resource),
|
||
status);
|
||
|
||
} else {
|
||
CL_ASSERT( Resource->Type->IsAlivePollInterval != 0 );
|
||
if ( temp != Resource->IsAlivePollInterval ) {
|
||
notify = TRUE;
|
||
}
|
||
if ( Resource->IsAlivePollInterval == CLUSTER_RESOURCE_USE_DEFAULT_POLL_INTERVAL ) {
|
||
Resource->IsAlivePollInterval = Resource->Type->IsAlivePollInterval;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Get the LooksAlive poll interval
|
||
//
|
||
temp = Resource->LooksAlivePollInterval;
|
||
dwDefault = CLUSTER_RESOURCE_DEFAULT_LOOKS_ALIVE;
|
||
status = DmQueryDword( resourceKey,
|
||
CLUSREG_NAME_RES_LOOKS_ALIVE,
|
||
&Resource->LooksAlivePollInterval,
|
||
&dwDefault );
|
||
|
||
if ( status != NO_ERROR ) {
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] Unable to refresh LooksAlivePollInterval for resource %1!ws!. Error %2!u!\n",
|
||
OmObjectId(Resource),
|
||
status);
|
||
} else {
|
||
CL_ASSERT( Resource->Type->IsAlivePollInterval != 0 );
|
||
if ( temp != Resource->LooksAlivePollInterval ) {
|
||
notify = TRUE;
|
||
}
|
||
if ( Resource->LooksAlivePollInterval == CLUSTER_RESOURCE_USE_DEFAULT_POLL_INTERVAL ) {
|
||
Resource->LooksAlivePollInterval = Resource->Type->LooksAlivePollInterval;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Get the RestartThreshold.
|
||
//
|
||
temp = Resource->RestartThreshold;
|
||
dwDefault = CLUSTER_RESOURCE_DEFAULT_RESTART_THRESHOLD;
|
||
status = DmQueryDword( resourceKey,
|
||
CLUSREG_NAME_RES_RESTART_THRESHOLD,
|
||
&Resource->RestartThreshold,
|
||
&dwDefault);
|
||
|
||
// Verify the RestartThreshold. Take any value.
|
||
if ( (status == NO_ERROR) &&
|
||
(temp != Resource->RestartThreshold) ) {
|
||
notify = TRUE;
|
||
}
|
||
|
||
//
|
||
// Get the RestartPeriod.
|
||
//
|
||
temp = Resource->RestartPeriod;
|
||
dwDefault = CLUSTER_RESOURCE_DEFAULT_RESTART_PERIOD;
|
||
status = DmQueryDword( resourceKey,
|
||
CLUSREG_NAME_RES_RESTART_PERIOD,
|
||
&Resource->RestartPeriod,
|
||
&dwDefault );
|
||
|
||
if ( (status == NO_ERROR) &&
|
||
(temp != Resource->RestartPeriod) ) {
|
||
notify = TRUE;
|
||
}
|
||
|
||
// Verify the RestartPeriod. Take any value.
|
||
|
||
//
|
||
// Get the RestartAction.
|
||
//
|
||
temp = Resource->RestartAction;
|
||
dwDefault = CLUSTER_RESOURCE_DEFAULT_RESTART_ACTION;
|
||
status = DmQueryDword( resourceKey,
|
||
CLUSREG_NAME_RES_RESTART_ACTION,
|
||
&Resource->RestartAction,
|
||
&dwDefault);
|
||
|
||
// Verify the RestartAction.
|
||
|
||
if ( status == NO_ERROR ) {
|
||
if ( temp != Resource->RestartAction ) {
|
||
notify = TRUE;
|
||
}
|
||
if ( Resource->RestartAction >= RestartMaximum ) {
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] Illegal RestartAction refresh for resource %1!ws!\n",
|
||
OmObjectId(Resource));
|
||
}
|
||
}
|
||
|
||
temp = Resource->RetryPeriodOnFailure;
|
||
dwDefault = CLUSTER_RESOURCE_DEFAULT_RETRY_PERIOD_ON_FAILURE;
|
||
status = DmQueryDword( resourceKey,
|
||
CLUSREG_NAME_RES_RETRY_PERIOD_ON_FAILURE,
|
||
&Resource->RetryPeriodOnFailure,
|
||
&dwDefault );
|
||
|
||
// make sure that RetryPeriodOnFailure >= RestartPeriod
|
||
if (Resource->RetryPeriodOnFailure < Resource->RestartPeriod)
|
||
{
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] Specified RetryPeriodOnFailure value is less than RestartPeriod value - setting RetryPeriodOnFailure equal to RestartPeriod \n");
|
||
Resource->RetryPeriodOnFailure = Resource->RestartPeriod;
|
||
|
||
}
|
||
if( temp != Resource->RetryPeriodOnFailure)
|
||
notify = TRUE;
|
||
|
||
//
|
||
// Get the PendingTimeout value.
|
||
//
|
||
temp = Resource->PendingTimeout;
|
||
dwDefault = CLUSTER_RESOURCE_DEFAULT_PENDING_TIMEOUT;
|
||
status = DmQueryDword( resourceKey,
|
||
CLUSREG_NAME_RES_PENDING_TIMEOUT,
|
||
&Resource->PendingTimeout,
|
||
&dwDefault);
|
||
|
||
// Verify the PendingTimeout. Take any value.
|
||
|
||
if ( (status == NO_ERROR) &&
|
||
(temp != Resource->PendingTimeout) ) {
|
||
notify = TRUE;
|
||
}
|
||
|
||
|
||
//
|
||
// Get the current persistent state of the resource.
|
||
//
|
||
// Don't bother with change notifications... they should happen elsewhere.
|
||
//
|
||
status = DmQueryDword( resourceKey,
|
||
CLUSREG_NAME_RES_PERSISTENT_STATE,
|
||
&temp,
|
||
NULL );
|
||
|
||
//
|
||
// Save the current resource state.
|
||
//
|
||
|
||
if ( ( status == ERROR_FILE_NOT_FOUND ) ||
|
||
( ( status == ERROR_SUCCESS ) && ( temp == CLUSTER_RESOURCE_DEFAULT_PERSISTENT_STATE ) ) ) {
|
||
switch ( Resource->Group->PersistentState ) {
|
||
case ClusterGroupOnline:
|
||
Resource->PersistentState = ClusterResourceOnline;
|
||
break;
|
||
case ClusterGroupOffline:
|
||
Resource->PersistentState = ClusterResourceOffline;
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
} else if ( status != NO_ERROR ) {
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] Unable to read PersistentState for resource %1!ws!. Error %2!u!\n",
|
||
OmObjectId(Resource),
|
||
status);
|
||
return;
|
||
} else if ( temp ) {
|
||
Resource->PersistentState = ClusterResourceOnline;
|
||
} else {
|
||
Resource->PersistentState = ClusterResourceOffline;
|
||
}
|
||
|
||
#if 0 // Do this work when bringing the resource online!
|
||
//
|
||
// Determine the monitor to run this in. This is only updated from
|
||
// the node that owns the resource.
|
||
//
|
||
status = DmQueryDword( resourceKey,
|
||
CLUSREG_NAME_RES_SEPARATE_MONITOR,
|
||
&separateMonitor,
|
||
&zero );
|
||
//
|
||
// Only do work if the flag changes.
|
||
//
|
||
if ( (!separateMonitor &&
|
||
(Resource->Flags & RESOURCE_SEPARATE_MONITOR)) ||
|
||
(separateMonitor &&
|
||
((Resource->Flags & RESOURCE_SEPARATE_MONITOR) == 0)) ) {
|
||
|
||
//
|
||
// If the resource is not offline or the quorum resource, then
|
||
// we'll have to wait until the cluster service restarts.
|
||
//
|
||
//
|
||
// The separate monitor flag has changed... tell ResMon to close
|
||
// the resource and then create it again.
|
||
//
|
||
if ( (Resource->State != ClusterResourceOffline) ||
|
||
Resource->QuorumResource ) {
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] Can't change separate monitor flag on the fly... we'll pick this up on next cluster start\n",
|
||
OmObjectId(Resource) );
|
||
// Now fall through to post the other changes.
|
||
} else {
|
||
if ( FmpPostNotification( (RM_NOTIFY_KEY)Resource,
|
||
RmRestartResource,
|
||
Resource->PersistentState ) ) {
|
||
return;
|
||
} else {
|
||
ClRtlLogPrint(LOG_UNUSUAL,
|
||
"[FM] Failed to post notification to restart resource '%1!ws!'.\n",
|
||
OmObjectId(Resource) );
|
||
}
|
||
}
|
||
}
|
||
#endif
|
||
|
||
if ( !FmpShutdown &&
|
||
notify ) {
|
||
//
|
||
// Comments from sunitas: Tell the resource monitor about the
|
||
// changes but do this from the worker thread. Originally, this
|
||
// used to be a post notification to the FmpRmWorkerThread
|
||
// which posts resmon notifications to clussvc.
|
||
//
|
||
OmReferenceObject(Resource);
|
||
FmpPostWorkItem(FM_EVENT_INTERNAL_RESOURCE_CHANGE_PARAMS,
|
||
Resource,
|
||
0);
|
||
}
|
||
|
||
#if 0 // The post notification above handles the event notification
|
||
if ( !FmpShutdown &&
|
||
notify ) {
|
||
ClusterEvent( CLUSTER_EVENT_RESOURCE_PROPERTY_CHANGE, Resource );
|
||
}
|
||
#endif
|
||
|
||
return;
|
||
|
||
} // FmpResourceChangeCallback
|
||
|
||
|
||
|
||
DWORD
|
||
FmpChangeResourceMonitor(
|
||
IN PFM_RESOURCE Resource,
|
||
IN DWORD SeparateMonitor
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine switches the resource from one resource monitor to another.
|
||
|
||
Arguments:
|
||
|
||
Resource - pointer to the resource that was modified.
|
||
|
||
SeparateMonitor - flag to indicate whether to run in a separate monitor;
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS if successful.
|
||
A Win32 error code on failure.
|
||
|
||
Notes:
|
||
|
||
The resource should be offline.
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD status = ERROR_SUCCESS;
|
||
DWORD separateMonitor;
|
||
DWORD zero = 0;
|
||
|
||
if ( Resource->RegistryKey == NULL ) {
|
||
return(ERROR_INVALID_STATE);
|
||
}
|
||
|
||
if ( (Resource->State != ClusterResourceOffline) &&
|
||
(Resource->State != ClusterResourceFailed) ) {
|
||
return(ERROR_INVALID_STATE);
|
||
}
|
||
|
||
//
|
||
// Determine the monitor to run this in. This is only updated from
|
||
// the node that owns the resource.
|
||
//
|
||
if ( (!SeparateMonitor &&
|
||
(Resource->Flags & RESOURCE_SEPARATE_MONITOR)) ||
|
||
(SeparateMonitor &&
|
||
((Resource->Flags & RESOURCE_SEPARATE_MONITOR) == 0)) ) {
|
||
|
||
//
|
||
// The separate monitor flag has changed... tell ResMon to close
|
||
// the resource and then create it again.
|
||
//
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] Changing Separate Resource Monitor state\n");
|
||
|
||
status = FmpRmCloseResource( Resource );
|
||
if ( status == ERROR_SUCCESS ) {
|
||
if ( Resource->Flags & RESOURCE_SEPARATE_MONITOR ) {
|
||
Resource->Flags &= ~RESOURCE_SEPARATE_MONITOR;
|
||
} else {
|
||
Resource->Flags |= RESOURCE_SEPARATE_MONITOR;
|
||
}
|
||
status = FmpRmCreateResource( Resource );
|
||
if ( status != ERROR_SUCCESS ) {
|
||
ClRtlLogPrint(LOG_UNUSUAL,
|
||
"[FM] Separate resource monitor changed for '%1!ws!', but failed to re-open the resource, error %2!u!.\n",
|
||
OmObjectId(Resource),
|
||
status );
|
||
}
|
||
} else {
|
||
ClRtlLogPrint(LOG_UNUSUAL,
|
||
"[FM] Separate resource monitor changed for '%1!ws!', but failed to close the resource, error %2!u!.\n",
|
||
OmObjectId(Resource),
|
||
status );
|
||
return(status);
|
||
}
|
||
}
|
||
|
||
return(status);
|
||
|
||
} // FmpChangeResourceMonitor
|
||
|
||
|