3035 lines
83 KiB
C
3035 lines
83 KiB
C
/*++
|
||
|
||
Copyright (c) 1996 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
resource.c
|
||
|
||
Abstract:
|
||
|
||
Cluster resource management routines.
|
||
|
||
Author:
|
||
|
||
Mike Massa (mikemas) 1-Jan-1996
|
||
|
||
|
||
Notes:
|
||
|
||
WARNING: All of the routines in this file assume that the resource
|
||
lock is held when they are called.
|
||
|
||
Revision History:
|
||
|
||
|
||
--*/
|
||
|
||
#include "fmp.h"
|
||
|
||
//globals
|
||
|
||
#define LOG_MODULE RESOURCE
|
||
|
||
|
||
//
|
||
// Global Data
|
||
//
|
||
CRITICAL_SECTION FmpResourceLock;
|
||
|
||
//
|
||
// Local Data
|
||
//
|
||
|
||
typedef struct PENDING_CONTEXT {
|
||
PFM_RESOURCE Resource;
|
||
BOOL ForceOnline;
|
||
} PENDING_CONTEXT, *PPENDING_CONTEXT;
|
||
|
||
|
||
//
|
||
// Local function prototypes
|
||
//
|
||
|
||
|
||
|
||
/////////////////////////////////////////////////////////////////////////////
|
||
//
|
||
// Resource List Maintenance Routines
|
||
//
|
||
/////////////////////////////////////////////////////////////////////////////
|
||
|
||
BOOL
|
||
FmpFindResourceByNotifyKeyWorker(
|
||
IN RM_NOTIFY_KEY NotifyKey,
|
||
IN PFM_RESOURCE *FoundResource,
|
||
IN PFM_RESOURCE Resource,
|
||
IN LPCWSTR Name
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Enumeration callback routine for finding a resource by notify key
|
||
|
||
Arguments:
|
||
|
||
FoundResource - Returns the found resource.
|
||
|
||
Resource - Supplies the current resource.
|
||
|
||
Name - Supplies the current resource's name.
|
||
|
||
Return Value:
|
||
|
||
TRUE - to continue searching
|
||
|
||
FALSE - to stop the search. The matching resource is returned in
|
||
*FoundResource
|
||
|
||
--*/
|
||
|
||
{
|
||
if ((RM_NOTIFY_KEY)Resource == NotifyKey) {
|
||
*FoundResource = Resource;
|
||
return(FALSE);
|
||
}
|
||
return(TRUE);
|
||
|
||
} // FmpFindResourceByNotifyKeyWorker
|
||
|
||
|
||
|
||
PFM_RESOURCE
|
||
FmpFindResourceByNotifyKey(
|
||
RM_NOTIFY_KEY NotifyKey
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Arguments:
|
||
|
||
Returns:
|
||
|
||
--*/
|
||
|
||
{
|
||
PFM_RESOURCE resource = NULL;
|
||
|
||
OmEnumObjects(ObjectTypeResource,
|
||
(OM_ENUM_OBJECT_ROUTINE)FmpFindResourceByNotifyKeyWorker,
|
||
(PVOID)NotifyKey,
|
||
&resource);
|
||
return(resource);
|
||
|
||
} // FmpFindResourceByNotifyKey
|
||
|
||
|
||
//////////////////////////////////////////////////////////////
|
||
//
|
||
// Interfaces for managing resources.
|
||
//
|
||
/////////////////////////////////////////////////////////////
|
||
|
||
|
||
PFM_RESOURCE
|
||
FmpCreateResource(
|
||
IN PFM_GROUP Group,
|
||
IN LPCWSTR ResourceId,
|
||
IN LPCWSTR ResourceName,
|
||
IN BOOL Initialize
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Create a resource in our list of resources.
|
||
|
||
Arguments:
|
||
|
||
Group - The Group in which this resource belongs.
|
||
|
||
ResourceId - The id of the resource being created.
|
||
|
||
ResourceName - The name of the resource being created.
|
||
|
||
Initialize - TRUE if the resource should be initialized from the registry.
|
||
FALSE if the resource should be left un-initialized.
|
||
|
||
Returns:
|
||
|
||
A pointer to the resource that was created or NULL.
|
||
|
||
Notes:
|
||
|
||
1) The resource lock must be held when this routine is called.
|
||
|
||
2) If the resource was created, then the reference count on the resource
|
||
is 2 when this routine returns. If the resource was not created, then
|
||
the reference count on the resource is not incremented. That way, if
|
||
the caller needs extra references on the resource, then it must place
|
||
them itself. This can be done later, since the resource lock is held.
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD mszStringIndex;
|
||
PFM_RESOURCE resource = NULL;
|
||
DWORD status;
|
||
PDEPENDENCY dependency = NULL;
|
||
DWORD resourceNameSize=
|
||
(wcslen(ResourceId) * sizeof(WCHAR)) +
|
||
sizeof(UNICODE_NULL);
|
||
BOOL created;
|
||
LPWSTR quorumId = NULL;
|
||
DWORD quorumIdSize = 0;
|
||
DWORD quorumIdMaxSize = 0;
|
||
|
||
//
|
||
// Open an existing resource or create a new one.
|
||
//
|
||
resource = OmCreateObject( ObjectTypeResource,
|
||
ResourceId,
|
||
ResourceName,
|
||
&created);
|
||
if ( resource == NULL ) {
|
||
return(NULL);
|
||
}
|
||
|
||
//
|
||
// If we did not create a new resource, then make sure the Groups match.
|
||
//
|
||
if ( !created )
|
||
{
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] CreateResource, Opened existing resource %1!ws!\n",
|
||
ResourceId);
|
||
OmDereferenceObject( resource );
|
||
//the quorum group may be created again recursively before phase 1
|
||
//in this case we dont want to do the special processing for putting
|
||
//it on the quorum group contains list
|
||
if ( resource->Group == Group )
|
||
{
|
||
return(resource);
|
||
}
|
||
//the quorum group is destroyed at initialization but not the quorum resource
|
||
if (!FmpFMOnline)
|
||
{
|
||
//the quorum group is being recreated for the second time
|
||
//the quorum group state needs to be refigured
|
||
// this is done after all groups are created in FmFormNewClusterPhase2()
|
||
|
||
if (resource->QuorumResource)
|
||
{
|
||
ClRtlLogPrint(LOG_NOISE,"[FM] ReCreating quorum resource %1!ws!\n", ResourceId);
|
||
Group->OwnerNode = NmLocalNode;
|
||
InsertTailList( &Group->Contains,
|
||
&resource->ContainsLinkage );
|
||
//add a referenece to the resource object for being on the contains list
|
||
OmReferenceObject( resource );
|
||
resource->Group = Group;
|
||
OmReferenceObject(Group);
|
||
//SS: for now we dont use resource locks, so dont create it and leak it !
|
||
//InitializeCriticalSection( &resource->Lock );
|
||
FmpSetPreferredEntry( Group, NmLocalNode );
|
||
return(resource);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
SetLastError(ERROR_RESOURCE_NOT_AVAILABLE);
|
||
return(NULL);
|
||
}
|
||
|
||
}
|
||
|
||
ClRtlLogPrint(LOG_NOISE,"[FM] Creating resource %1!ws!\n", ResourceId);
|
||
|
||
resource->dwStructState = FM_RESOURCE_STRUCT_CREATED;
|
||
|
||
//
|
||
// Initialize the resource.
|
||
//
|
||
resource->Group = Group;
|
||
resource->State = ClusterResourceOffline; // Initial value for state.
|
||
// resource->Flags = 0; // Structure zeroed at creation
|
||
// resource->Id = 0;
|
||
// resource->FailureTime = 0;
|
||
// resource->QuorumResource = FALSE;
|
||
// resource->PossibleList = FALSE;
|
||
//SS: for now we dont use resource locks, so dont create it and leak it !
|
||
//InitializeCriticalSection( &resource->Lock );
|
||
resource->hTimer=NULL;
|
||
InitializeListHead( &(resource->ProvidesFor) );
|
||
InitializeListHead( &(resource->DependsOn) );
|
||
InitializeListHead( &(resource->PossibleOwners) );
|
||
InitializeListHead( &(resource->DmRundownList) );
|
||
|
||
//
|
||
// Insert the new resource onto the Group's contains list.
|
||
//
|
||
InsertTailList( &Group->Contains,
|
||
&resource->ContainsLinkage );
|
||
//SS: there is already a reference to the object for being on the resource
|
||
//list
|
||
//add a referenece to the resource object for being on the contains list
|
||
OmReferenceObject( resource );
|
||
|
||
//add a reference to the group because the resource has a pointer to it
|
||
OmReferenceObject(Group);
|
||
|
||
//
|
||
// Complete initialization if we were told to do so.
|
||
//
|
||
status = FmpInitializeResource( resource, Initialize );
|
||
|
||
//
|
||
// Check for distinguished error code to delete this resource.
|
||
//
|
||
if ( status == ERROR_INVALID_NAME ) {
|
||
goto error_exit;
|
||
}
|
||
//
|
||
// On other failures, we must be sure to come back through init later...
|
||
//
|
||
if ( Initialize &&
|
||
(status != ERROR_SUCCESS) ) {
|
||
CL_ASSERT( resource->Monitor == NULL );
|
||
}
|
||
|
||
//
|
||
// Now insert this object into the tree... before the dependency
|
||
// list is processed. That way, if there are circular dependencies
|
||
// this will not loop forever. If we can be assured that there are
|
||
// no circular dependencies, then we can do the insert after creating
|
||
// the dependency tree.
|
||
//
|
||
// if this is being called during initialization, and the resource is
|
||
// is already created it belonged to the group to which the quorum
|
||
// resource belongs to and doesnt need to be inserted into the resource list
|
||
if (FmpFMOnline || (created))
|
||
{
|
||
status = OmInsertObject( resource );
|
||
|
||
if ( status != ERROR_SUCCESS )
|
||
goto error_exit;
|
||
}
|
||
|
||
//
|
||
// Check if this is the Quorum Resource.
|
||
//
|
||
status = DmQuerySz( DmQuorumKey,
|
||
CLUSREG_NAME_QUORUM_RESOURCE,
|
||
&quorumId,
|
||
&quorumIdMaxSize,
|
||
&quorumIdSize );
|
||
|
||
if ( status != ERROR_SUCCESS) {
|
||
ClRtlLogPrint(LOG_ERROR,
|
||
"[FM] Failed to read quorum resource, error %1!u!.\n",
|
||
status);
|
||
}
|
||
|
||
//
|
||
// If we're creating the quorum resource, then indicate it.
|
||
//
|
||
if ( (quorumId != NULL) &&
|
||
(lstrcmpiW( quorumId, ResourceId ) == 0) ) {
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] Found the quorum resource %1!ws!.\n",
|
||
ResourceId);
|
||
resource->QuorumResource = TRUE;
|
||
}
|
||
|
||
LocalFree(quorumId);
|
||
//
|
||
// Create Any Dependencies
|
||
//
|
||
for (mszStringIndex = 0; ; mszStringIndex++) {
|
||
LPCWSTR nameString;
|
||
PFM_RESOURCE childResource;
|
||
|
||
|
||
nameString = ClRtlMultiSzEnum(
|
||
resource->Dependencies,
|
||
resource->DependenciesSize/sizeof(WCHAR),
|
||
mszStringIndex
|
||
);
|
||
|
||
if (nameString == NULL) {
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Create the dependency.
|
||
//
|
||
dependency = LocalAlloc(LMEM_FIXED, sizeof(DEPENDENCY));
|
||
if (dependency == NULL) {
|
||
status = ERROR_NOT_ENOUGH_MEMORY;
|
||
goto error_exit;
|
||
}
|
||
|
||
//
|
||
// Create the child resource recursively. We must also add an
|
||
// additional reference required for the dependency relationship.
|
||
//
|
||
ClRtlLogPrint(LOG_NOISE,"[FM] Resource %1!ws! depends on %2!ws!. Creating...\n",
|
||
ResourceId,
|
||
nameString);
|
||
|
||
childResource = FmpCreateResource( resource->Group,
|
||
nameString,
|
||
NULL,
|
||
Initialize );
|
||
if (childResource == NULL) {
|
||
status = GetLastError();
|
||
ClRtlLogPrint(LOG_NOISE,"[FM] Failed to create dep %1!ws! for resource %2!ws!\n",
|
||
nameString,
|
||
ResourceId);
|
||
|
||
goto error_exit;
|
||
} else {
|
||
//
|
||
// Add a reference to each resource to reflect the
|
||
// dependency.
|
||
//
|
||
OmReferenceObject( childResource );
|
||
OmReferenceObject( resource );
|
||
dependency->DependentResource = resource;
|
||
dependency->ProviderResource = childResource;
|
||
InsertTailList(&childResource->ProvidesFor,
|
||
&dependency->ProviderLinkage);
|
||
InsertTailList(&resource->DependsOn,
|
||
&dependency->DependentLinkage);
|
||
}
|
||
|
||
}
|
||
|
||
resource->dwStructState |= FM_RESOURCE_STRUCT_INITIALIZED;
|
||
|
||
ClRtlLogPrint(LOG_NOISE,"[FM] All dependencies for resource %1!ws! created.\n",
|
||
ResourceId);
|
||
|
||
return(resource);
|
||
|
||
error_exit:
|
||
|
||
FmpAcquireLocalResourceLock( resource );
|
||
|
||
RemoveEntryList( &resource->ContainsLinkage );
|
||
//dereference the resource object for being removed from the contains linkage
|
||
OmDereferenceObject( resource );
|
||
|
||
FmpDestroyResource( resource, FALSE );
|
||
|
||
//dereference the resource object, for being removed from the resource list.
|
||
//OmDereferenceObject( resource );
|
||
//delete the extra reference that was added to the group
|
||
//OmDereferenceObject(Group);
|
||
|
||
SetLastError(status);
|
||
return(NULL);
|
||
|
||
} // FmpCreateResource
|
||
|
||
|
||
DWORD
|
||
FmpInitializeResource(
|
||
IN PFM_RESOURCE Resource,
|
||
IN BOOL Initialize
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Initializes a resource from the registry and tells the Resource
|
||
Monitor about the new resource (if the local system can host the
|
||
resource).
|
||
|
||
Arguments:
|
||
|
||
Resource - Supplies the resource to be initialized.
|
||
|
||
Initialize - TRUE if the resource should be fully initialized.
|
||
FALSE otherwise.
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS if successful.
|
||
|
||
A Win32 error code on failure.
|
||
|
||
Notes:
|
||
|
||
It is assumed that the resource lock is held.
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD status;
|
||
|
||
if ( Resource->Monitor != NULL ) {
|
||
return(ERROR_ALREADY_INITIALIZED);
|
||
}
|
||
|
||
status = FmpQueryResourceInfo( Resource, Initialize );
|
||
if ( status != ERROR_SUCCESS ) {
|
||
return(status);
|
||
}
|
||
|
||
//
|
||
// If we didn't fully initialize the resource, then leave now.
|
||
//
|
||
if ( !Initialize ) {
|
||
return(ERROR_SUCCESS);
|
||
}
|
||
|
||
//
|
||
// This is a newly initialized resource. Tell the Resource Monitor to
|
||
// create it.
|
||
//
|
||
// TODO - we don't want to instantiate resources in the resource
|
||
// monitor that we cannot execute. We must check possible owners list
|
||
// before making this call. We must also make sure the registry
|
||
// parameters are read. We use the Monitor field as a surrogate for
|
||
// determining whether the registry parameters have been read.
|
||
//
|
||
return(FmpRmCreateResource(Resource));
|
||
|
||
} // FmpInitializeResource
|
||
|
||
|
||
|
||
DWORD
|
||
FmpOnlineResource(
|
||
IN PFM_RESOURCE Resource,
|
||
IN BOOL ForceOnline
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Brings a resource and all its dependencies online. If ERROR_IO_PENDING is
|
||
returned, then no thread is started to complete the online request.
|
||
|
||
Arguments:
|
||
|
||
Resource - Supplies the resource to be brought online
|
||
|
||
ForceOnline - TRUE if the resource should be forced online.
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS if successful.
|
||
ERROR_IO_PENDING if the request is pending.
|
||
Win32 error code otherwise.
|
||
|
||
--*/
|
||
{
|
||
PLIST_ENTRY entry;
|
||
PDEPENDENCY dependency;
|
||
DWORD status;
|
||
BOOL waitingResource = FALSE;
|
||
DWORD separateMonitor;
|
||
DWORD onlinestatus;
|
||
|
||
FmpAcquireLocalResourceLock( Resource );
|
||
|
||
//
|
||
// If the resource is not owned by this system, then return error.
|
||
//
|
||
CL_ASSERT( Resource->Group != NULL );
|
||
if (Resource->Group->OwnerNode != NmLocalNode)
|
||
{
|
||
FmpReleaseLocalResourceLock( Resource );
|
||
return(ERROR_HOST_NODE_NOT_RESOURCE_OWNER);
|
||
}
|
||
//if it is the quorum resource dont check for the node
|
||
//being in the preferred list, we should be able to
|
||
//bring the quorum resource online on any node
|
||
if (!(Resource->QuorumResource) &&
|
||
!FmpInPreferredList( Resource->Group, Resource->Group->OwnerNode, TRUE, Resource ))
|
||
{
|
||
FmpReleaseLocalResourceLock( Resource );
|
||
return(ERROR_NODE_CANT_HOST_RESOURCE);
|
||
}
|
||
|
||
//
|
||
// If the resource is already online, then return immediately.
|
||
//
|
||
if (Resource->State == ClusterResourceOnline) {
|
||
FmpReleaseLocalResourceLock( Resource );
|
||
return(ERROR_SUCCESS);
|
||
}
|
||
|
||
//
|
||
// If the resource is in online pending state, then return immediately.
|
||
//
|
||
if ( Resource->State == ClusterResourceOnlinePending ) {
|
||
FmpReleaseLocalResourceLock( Resource );
|
||
return(ERROR_IO_PENDING);
|
||
}
|
||
|
||
//
|
||
// If the resource is in offline pending state, then return immediately.
|
||
//
|
||
if ( Resource->State == ClusterResourceOfflinePending ) {
|
||
FmpReleaseLocalResourceLock( Resource );
|
||
return(ERROR_INVALID_STATE);
|
||
}
|
||
|
||
//
|
||
// If the resource is not initialized, then initialize it now.
|
||
//
|
||
if ( Resource->Monitor == NULL ) {
|
||
status = FmpInitializeResource( Resource, TRUE );
|
||
if ( status != ERROR_SUCCESS ) {
|
||
FmpReleaseLocalResourceLock( Resource );
|
||
return(status);
|
||
}
|
||
} else {
|
||
//
|
||
// If the separate monitor flag has changed, then close down old
|
||
// resource in resmon, and re-create it.
|
||
//
|
||
separateMonitor = (Resource->Flags & RESOURCE_SEPARATE_MONITOR) ? 1 : 0;
|
||
status = DmQueryDword( Resource->RegistryKey,
|
||
CLUSREG_NAME_RES_SEPARATE_MONITOR,
|
||
&separateMonitor,
|
||
&separateMonitor );
|
||
|
||
if ( (!separateMonitor &&
|
||
(Resource->Flags & RESOURCE_SEPARATE_MONITOR)) ||
|
||
(separateMonitor &&
|
||
((Resource->Flags & RESOURCE_SEPARATE_MONITOR) == 0)) ) {
|
||
|
||
status = FmpChangeResourceMonitor( Resource, separateMonitor );
|
||
if ( status != ERROR_SUCCESS ) {
|
||
FmpReleaseLocalResourceLock( Resource );
|
||
return(status);
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// If this resource is supposed to be left offline, then make it so.
|
||
//
|
||
if ( !ForceOnline && (Resource->PersistentState == ClusterResourceOffline) ) {
|
||
FmpReleaseLocalResourceLock( Resource );
|
||
return(ERROR_RESOURCE_NOT_ONLINE);
|
||
}
|
||
|
||
//
|
||
// Next, make sure there are no resources down the tree that are waiting.
|
||
// This prevents a deadlock where the top of the tree is trying to go
|
||
// offline, while the bottom of the tree is trying to go online.
|
||
//
|
||
for ( entry = Resource->DependsOn.Flink;
|
||
entry != &(Resource->DependsOn);
|
||
entry = entry->Flink
|
||
)
|
||
{
|
||
dependency = CONTAINING_RECORD(entry, DEPENDENCY, DependentLinkage);
|
||
|
||
if ( (dependency->ProviderResource->State == ClusterResourceOfflinePending) &&
|
||
(dependency->ProviderResource->Flags & RESOURCE_WAITING) ) {
|
||
waitingResource= TRUE;
|
||
break;
|
||
}
|
||
}
|
||
if ( waitingResource ) {
|
||
Resource->Flags |= RESOURCE_WAITING;
|
||
FmpReleaseLocalResourceLock( Resource );
|
||
return(ERROR_RESOURCE_NOT_AVAILABLE);
|
||
}
|
||
|
||
//
|
||
// If the PersistentState is Offline, then reset the current state.
|
||
//
|
||
if ( Resource->PersistentState == ClusterResourceOffline ) {
|
||
FmpSetResourcePersistentState( Resource, ClusterResourceOnline );
|
||
}
|
||
|
||
//
|
||
// Make sure the Waiting flag is clear.
|
||
//
|
||
Resource->Flags &= ~RESOURCE_WAITING;
|
||
|
||
//
|
||
// If this resource has any dependencies, bring them online first.
|
||
//
|
||
for ( entry = Resource->DependsOn.Flink;
|
||
entry != &(Resource->DependsOn);
|
||
entry = entry->Flink
|
||
)
|
||
{
|
||
dependency = CONTAINING_RECORD(entry, DEPENDENCY, DependentLinkage);
|
||
|
||
//
|
||
// Recursively bring the provider resource online.
|
||
//
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] OnlineResource: %1!ws! depends on %2!ws!. Bring online first.\n",
|
||
OmObjectId(Resource),
|
||
OmObjectId(dependency->ProviderResource));
|
||
onlinestatus = FmpDoOnlineResource( dependency->ProviderResource,
|
||
ForceOnline );
|
||
|
||
if ( onlinestatus != ERROR_SUCCESS ) {
|
||
if ( onlinestatus != ERROR_IO_PENDING ) {
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] OnlineResource: dependency %1!ws! failed %2!d!\n",
|
||
OmObjectId(dependency->ProviderResource),
|
||
status);
|
||
FmpReleaseLocalResourceLock( Resource );
|
||
status = onlinestatus;
|
||
return(status);
|
||
} else {
|
||
FmpCallResourceNotifyCb(Resource, ClusterResourceOnlinePending);
|
||
FmpPropagateResourceState( Resource,
|
||
ClusterResourceOnlinePending );
|
||
Resource->Flags |= RESOURCE_WAITING;
|
||
if (status == ERROR_SUCCESS)
|
||
status = onlinestatus;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// Tell the resource monitor to bring this resource online.
|
||
//
|
||
if ( !(Resource->Flags & RESOURCE_WAITING) ) {
|
||
status = FmpRmOnlineResource( Resource );
|
||
}
|
||
#ifdef CLUSTER_TESTPOINT
|
||
TESTPT(TpFailOnlineResource) {
|
||
FmpRmFailResource( Resource );
|
||
}
|
||
#endif
|
||
|
||
FmpReleaseLocalResourceLock( Resource );
|
||
return(status);
|
||
|
||
} // FmpOnlineResource
|
||
|
||
|
||
|
||
DWORD
|
||
FmpTerminateResource(
|
||
IN PFM_RESOURCE Resource
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine takes a resource (and all of the resources that it provides
|
||
for) offline - the hard way.
|
||
|
||
Arguments:
|
||
|
||
Resource - A pointer to the resource to take offline the hard way.
|
||
|
||
Returns:
|
||
|
||
ERROR_SUCCESS - if the request is successful.
|
||
A Win32 error if the request fails.
|
||
|
||
--*/
|
||
|
||
{
|
||
PLIST_ENTRY entry;
|
||
PDEPENDENCY dependency;
|
||
DWORD status;
|
||
|
||
|
||
FmpAcquireLocalResourceLock( Resource );
|
||
|
||
//
|
||
// If the resource is already offline, then return immediately.
|
||
//
|
||
// We should not have to check if a resource has been initialized,
|
||
// since if it hasn't been initialized we will return because the
|
||
// pre-initialized state of a resource is Offline.
|
||
//
|
||
if ( Resource->State == ClusterResourceOffline ) {
|
||
FmpReleaseLocalResourceLock( Resource );
|
||
return(ERROR_SUCCESS);
|
||
}
|
||
|
||
Resource->Flags &= ~RESOURCE_WAITING;
|
||
|
||
//
|
||
// If this resource has any dependents, terminate them first.
|
||
//
|
||
for ( entry = Resource->ProvidesFor.Flink;
|
||
entry != &(Resource->ProvidesFor);
|
||
entry = entry->Flink
|
||
)
|
||
{
|
||
dependency = CONTAINING_RECORD(entry, DEPENDENCY, ProviderLinkage);
|
||
|
||
//
|
||
// Recursively terminate the dependent resource
|
||
//
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] TerminateResource: %1!ws! depends on %2!ws!. Terminating first\n",
|
||
OmObjectId(dependency->DependentResource),
|
||
OmObjectId(Resource));
|
||
|
||
//
|
||
// First stop any pending threads.
|
||
//
|
||
|
||
if ( dependency->DependentResource->PendingEvent ) {
|
||
SetEvent( dependency->DependentResource->PendingEvent );
|
||
}
|
||
|
||
status = FmpTerminateResource(dependency->DependentResource);
|
||
|
||
CL_ASSERT( status != ERROR_IO_PENDING );
|
||
if (status != ERROR_SUCCESS) {
|
||
FmpReleaseLocalResourceLock( Resource );
|
||
return(status);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Tell the resource monitor to terminate this resource.
|
||
//
|
||
FmpRmTerminateResource(Resource);
|
||
|
||
FmpReleaseLocalResourceLock( Resource );
|
||
|
||
return(ERROR_SUCCESS);
|
||
|
||
} // FmpTerminateResource
|
||
|
||
|
||
|
||
DWORD
|
||
FmpOfflineResource(
|
||
IN PFM_RESOURCE Resource,
|
||
IN BOOL bForceOffline
|
||
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine takes a resource (and all of the resources that it provides
|
||
for) offline. If ERROR_IO_PENDING is returned, then no thread is started
|
||
to complete the offline request.
|
||
|
||
Arguments:
|
||
|
||
Resource - A pointer to the resource to take offline.
|
||
|
||
bForceOffline - Indicates whether the persistent state is to be set.
|
||
|
||
Returns:
|
||
|
||
ERROR_SUCCESS if the request is successful.
|
||
ERROR_IO_PENDING if the request is pending.
|
||
A Win32 error code if the request fails.
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD status = ERROR_SUCCESS;
|
||
PLIST_ENTRY entry;
|
||
PDEPENDENCY dependency;
|
||
BOOL waitingResource = FALSE;
|
||
DWORD offlinestatus;
|
||
|
||
FmpAcquireLocalResourceLock( Resource );
|
||
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] FmpOfflineResource: Offline resource <%1!ws!> <%2!ws!>\n",
|
||
OmObjectName(Resource),
|
||
OmObjectId(Resource) );
|
||
//
|
||
// If we own the Group and we are not a possible owner, then the resource
|
||
// better be offline!
|
||
//
|
||
if ( (Resource->Group->OwnerNode != NmLocalNode) ||
|
||
(!FmpInPreferredList( Resource->Group, Resource->Group->OwnerNode , FALSE, NULL) &&
|
||
(Resource->Group != gpQuoResource->Group) &&
|
||
(Resource->State != ClusterResourceOffline)) ) {
|
||
FmpReleaseLocalResourceLock( Resource );
|
||
return(ERROR_INVALID_STATE);
|
||
}
|
||
|
||
//
|
||
// If the resource is already offline, then return immediately.
|
||
//
|
||
// We should not have to check if a resource has been initialized,
|
||
// since if it hasn't, then we will return because the pre-initialized
|
||
// state of a resource is Offline.
|
||
//
|
||
if ( Resource->State == ClusterResourceOffline ) {
|
||
//
|
||
// If this is the quorum resource, make sure any reservation
|
||
// threads are stopped!
|
||
//
|
||
if ( Resource->QuorumResource ) {
|
||
FmpRmTerminateResource( Resource );
|
||
}
|
||
FmpReleaseLocalResourceLock( Resource );
|
||
return(ERROR_SUCCESS);
|
||
} else if ( Resource->State == ClusterResourceFailed ) {
|
||
//
|
||
// Chittur Subbaraman (chitturs) - 4/8/99
|
||
//
|
||
// If the resource has already failed, then don't do anything.
|
||
// You could run into some funny cases of a resource switching
|
||
// between offline pending and failed states for ever if you
|
||
// attempt to offline a failed resource.
|
||
//
|
||
FmpReleaseLocalResourceLock( Resource );
|
||
return(ERROR_SUCCESS);
|
||
}
|
||
|
||
//
|
||
// If this system is not the owner, then return an error. Forwarding
|
||
// should have been done at a higher layer.
|
||
//
|
||
CL_ASSERT( Resource->Group != NULL );
|
||
if ( (Resource->Group->OwnerNode != NmLocalNode) ||
|
||
((Resource->Group != gpQuoResource->Group) &&
|
||
!FmpInPreferredList( Resource->Group, Resource->Group->OwnerNode, FALSE, NULL)) ) {
|
||
FmpReleaseLocalResourceLock( Resource );
|
||
return(ERROR_HOST_NODE_NOT_RESOURCE_OWNER);
|
||
}
|
||
|
||
if (Resource->State == ClusterResourceOnlinePending ) {
|
||
FmpReleaseLocalResourceLock( Resource );
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] FmpOfflineResource: Offline resource <%1!ws!> is in online pending state\n",
|
||
OmObjectName(Resource) );
|
||
if (FmpShutdown)
|
||
{
|
||
FmpRmTerminateResource( Resource );
|
||
return(ERROR_SUCCESS);
|
||
}
|
||
else
|
||
return(ERROR_INVALID_STATE);
|
||
}
|
||
|
||
//
|
||
// If the resource is in a pending state, then return immediately.
|
||
//
|
||
if (Resource->State == ClusterResourceOfflinePending ) {
|
||
FmpReleaseLocalResourceLock( Resource );
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] FmpOfflineResource: Offline resource <%1!ws!> returned pending\n",
|
||
OmObjectName(Resource) );
|
||
return(ERROR_IO_PENDING);
|
||
}
|
||
|
||
//
|
||
// Next, make sure there are no resources up the tree that are waiting.
|
||
// This prevents a deadlock where the top of the tree is trying to go
|
||
// offline, while the bottom of the tree is trying to go online.
|
||
//
|
||
for ( entry = Resource->ProvidesFor.Flink;
|
||
entry != &(Resource->ProvidesFor);
|
||
entry = entry->Flink
|
||
)
|
||
{
|
||
dependency = CONTAINING_RECORD(entry, DEPENDENCY, ProviderLinkage);
|
||
|
||
if ( (dependency->DependentResource->State == ClusterResourceOnlinePending) &&
|
||
(dependency->DependentResource->Flags & RESOURCE_WAITING) ) {
|
||
waitingResource = TRUE;
|
||
break;
|
||
}
|
||
}
|
||
if ( waitingResource ) {
|
||
Resource->Flags |= RESOURCE_WAITING;
|
||
FmpReleaseLocalResourceLock( Resource );
|
||
return(ERROR_RESOURCE_NOT_AVAILABLE);
|
||
}
|
||
|
||
//
|
||
// Make sure the Waiting flag is clear.
|
||
//
|
||
Resource->Flags &= ~RESOURCE_WAITING;
|
||
|
||
//
|
||
// If this resource has any dependents, shut them down first.
|
||
//
|
||
for ( entry = Resource->ProvidesFor.Flink;
|
||
entry != &(Resource->ProvidesFor);
|
||
entry = entry->Flink
|
||
)
|
||
{
|
||
dependency = CONTAINING_RECORD(entry, DEPENDENCY, ProviderLinkage);
|
||
|
||
//
|
||
// Recursively shutdown the dependent resource.
|
||
//
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] FmpOfflineResource: %1!ws! depends on %2!ws!. Shut down first.\n",
|
||
OmObjectName(dependency->DependentResource),
|
||
OmObjectName(Resource));
|
||
|
||
offlinestatus = FmpDoOfflineResource(dependency->DependentResource,
|
||
bForceOffline);
|
||
|
||
if ( offlinestatus != ERROR_SUCCESS ) {
|
||
if ( offlinestatus != ERROR_IO_PENDING ) {
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] FmpOfflineResource for %1!ws!, bad status returned %2!u!.\n",
|
||
OmObjectName(dependency->DependentResource),
|
||
offlinestatus);
|
||
FmpTerminateResource( dependency->DependentResource );
|
||
FmpReleaseLocalResourceLock( Resource );
|
||
return(offlinestatus);
|
||
} else {
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] FmpOfflineResource for %1!ws! marked as waiting.\n",
|
||
OmObjectName(Resource));
|
||
FmpCallResourceNotifyCb(Resource, ClusterResourceOfflinePending);
|
||
FmpPropagateResourceState( Resource,
|
||
ClusterResourceOfflinePending );
|
||
Resource->Flags |= RESOURCE_WAITING;
|
||
if (status == ERROR_SUCCESS)
|
||
status = offlinestatus;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// Tell the resource monitor to shut this resource down.
|
||
// The state gets updated by the return status in FmpRmOfflineResource.
|
||
//
|
||
if ( !(Resource->Flags & RESOURCE_WAITING) ) {
|
||
status = FmpRmOfflineResource( Resource );
|
||
//
|
||
// Chittur Subbaraman (chitturs) - 3/2/2000
|
||
//
|
||
// If the resource could not be made offline since the quorum
|
||
// resource online operation failed or for other reasons, then
|
||
// make sure the resource is terminated after you declare the
|
||
// state of the resource as failed. This is necessary since
|
||
// otherwise the FM will consider the resource as having failed
|
||
// while the resource itself is actually online. This will
|
||
// lead to disastrous cases whereby the FM will allow the online
|
||
// entry point of a resource to be called while the resource is
|
||
// actually online !
|
||
//
|
||
if( ( status != ERROR_SUCCESS ) &&
|
||
( status != ERROR_IO_PENDING ) &&
|
||
( status != ERROR_RETRY ) ) {
|
||
FmpRmTerminateResource( Resource );
|
||
}
|
||
}
|
||
FmpReleaseLocalResourceLock( Resource );
|
||
|
||
return(status);
|
||
|
||
} // FmpOfflineResource
|
||
|
||
|
||
|
||
DWORD
|
||
FmpDoOnlineResource(
|
||
IN PFM_RESOURCE Resource,
|
||
IN BOOL ForceOnline
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine brings a resource online. If ERROR_IO_PENDING is returned,
|
||
then a thread is started to complete the Online request.
|
||
|
||
|
||
Arguments:
|
||
|
||
Resource - A pointer to the resource to bring online.
|
||
|
||
ForceOnline - TRUE if the resource should be forced online.
|
||
|
||
Returns:
|
||
|
||
ERROR_SUCCESS if the request is successful.
|
||
ERROR_IO_PENDING if the request is pending.
|
||
A Win32 error code if the request fails.
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD status;
|
||
|
||
FmpAcquireLocalResourceLock( Resource );
|
||
|
||
//
|
||
// If the resource is already online, then return immediately.
|
||
//
|
||
if ( Resource->State == ClusterResourceOnline ) {
|
||
FmpReleaseLocalResourceLock( Resource );
|
||
return(ERROR_SUCCESS);
|
||
}
|
||
|
||
//
|
||
// If the resource is in a pending state, then return immediately.
|
||
// FmpOnlineResource checks for offlinepending state and returns
|
||
// ERROR_INVALID_STATE if so.
|
||
//
|
||
if ( Resource->State == ClusterResourceOnlinePending ) {
|
||
FmpReleaseLocalResourceLock( Resource );
|
||
return(ERROR_IO_PENDING);
|
||
}
|
||
|
||
//
|
||
// If this node is paused, return failure.
|
||
//
|
||
if (NmGetNodeState(NmLocalNode) == ClusterNodePaused) {
|
||
FmpReleaseLocalResourceLock( Resource );
|
||
return(ERROR_SHARING_PAUSED);
|
||
}
|
||
|
||
//
|
||
// Try to bring the resource online.
|
||
//
|
||
status = FmpOnlineResource( Resource, ForceOnline );
|
||
|
||
//
|
||
// Write the persistent state if it is forced online.
|
||
//
|
||
if ( ForceOnline &&
|
||
((status == ERROR_SUCCESS)||
|
||
(status == ERROR_IO_PENDING))) {
|
||
FmpSetResourcePersistentState( Resource, ClusterResourceOnline );
|
||
}
|
||
|
||
FmpReleaseLocalResourceLock( Resource );
|
||
|
||
return(status);
|
||
|
||
} // FmpDoOnlineResource
|
||
|
||
|
||
|
||
DWORD
|
||
FmpDoOfflineResource(
|
||
IN PFM_RESOURCE Resource,
|
||
IN BOOL bForceOffline
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine takes a resource offline. If ERROR_IO_PENDING is returned,
|
||
then a thread is started to complete the Offline request.
|
||
|
||
|
||
Arguments:
|
||
|
||
Resource - A pointer to the resource to take offline.
|
||
|
||
bForceOffline - Indicates whether the persistent state must be changed.
|
||
|
||
Returns:
|
||
|
||
ERROR_SUCCESS if the request is successful.
|
||
ERROR_IO_PENDING if the request is pending.
|
||
A Win32 error code if the request fails.
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD status;
|
||
|
||
FmpAcquireLocalResourceLock( Resource );
|
||
|
||
//
|
||
// If the resource is already offline, then return immediately.
|
||
//
|
||
if (Resource->State == ClusterResourceOffline) {
|
||
//
|
||
// If this is the quorum resource, make sure any reservation
|
||
// threads are stopped!
|
||
//
|
||
if ( Resource->QuorumResource ) {
|
||
FmpRmTerminateResource( Resource );
|
||
}
|
||
FmpReleaseLocalResourceLock( Resource );
|
||
return(ERROR_SUCCESS);
|
||
}
|
||
|
||
//
|
||
// If the resource is in a pending state, then return immediately.
|
||
// FmpOffline resource checks to see if it is in OnlinePending state
|
||
// and returns invalid state
|
||
//
|
||
if (Resource->State == ClusterResourceOfflinePending ) {
|
||
FmpReleaseLocalResourceLock( Resource );
|
||
return(ERROR_IO_PENDING);
|
||
}
|
||
|
||
//
|
||
// Try to take the resource offline.
|
||
//
|
||
status = FmpOfflineResource( Resource, bForceOffline );
|
||
|
||
//
|
||
// Write the persistent state if it is forced offline
|
||
//
|
||
if ( bForceOffline &&
|
||
((status == ERROR_SUCCESS)||
|
||
(status == ERROR_IO_PENDING))) {
|
||
FmpSetResourcePersistentState( Resource, ClusterResourceOffline );
|
||
}
|
||
|
||
FmpReleaseLocalResourceLock( Resource );
|
||
|
||
return(status);
|
||
|
||
} // FmpDoOfflineResource
|
||
|
||
|
||
|
||
VOID
|
||
FmpSetResourcePersistentState(
|
||
IN PFM_RESOURCE Resource,
|
||
IN CLUSTER_RESOURCE_STATE State
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Set the persistent state of a Resource in the registry and set the
|
||
PersistentState for the volatile (in-memory) resource. It is assumed
|
||
that the dynamic state gets changed elsewhere depending on whether
|
||
the resource online request succeeds or fails.
|
||
|
||
Arguments:
|
||
|
||
Resource - The resource to set the state.
|
||
State - The new state for the Resource.
|
||
|
||
Returns:
|
||
|
||
ERROR_SUCCESS if successful.
|
||
|
||
A Win32 error code on failure.
|
||
|
||
|
||
Notes:
|
||
|
||
The LocalResourceLock must be held.
|
||
|
||
--*/
|
||
|
||
{
|
||
CLUSTER_RESOURCE_STATE persistentState;
|
||
LPWSTR persistentStateName = CLUSREG_NAME_RES_PERSISTENT_STATE;
|
||
|
||
if (!gbIsQuoResEnoughSpace)
|
||
return;
|
||
|
||
//
|
||
// If the current state has changed, then do the work. Otherwise,
|
||
// skip the effort.
|
||
//
|
||
if ( Resource->PersistentState != State ) {
|
||
Resource->PersistentState = State;
|
||
CL_ASSERT( Resource->RegistryKey != NULL );
|
||
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] FmpSetResourcePersistentState: Setting persistent state for resource %1!ws!...\r\n",
|
||
OmObjectId(Resource));
|
||
|
||
//
|
||
// Set the new value, but only if it is online or offline.
|
||
//
|
||
if ( State == ClusterResourceOffline ) {
|
||
persistentState = 0;
|
||
DmSetValue( Resource->RegistryKey,
|
||
persistentStateName,
|
||
REG_DWORD,
|
||
(LPBYTE)&persistentState,
|
||
sizeof(DWORD) );
|
||
} else if ( State == ClusterResourceOnline ) {
|
||
persistentState = 1;
|
||
DmSetValue( Resource->RegistryKey,
|
||
persistentStateName,
|
||
REG_DWORD,
|
||
(LPBYTE)&persistentState,
|
||
sizeof(DWORD) );
|
||
}
|
||
}
|
||
|
||
} // FmpSetResourcePersistentState
|
||
|
||
void FmpCallResourceNotifyCb(
|
||
IN PFM_RESOURCE Resource,
|
||
IN CLUSTER_RESOURCE_STATE State
|
||
)
|
||
{
|
||
|
||
switch ( State ) {
|
||
case ClusterResourceOnline:
|
||
OmNotifyCb(Resource, NOTIFY_RESOURCE_POSTONLINE);
|
||
break;
|
||
case ClusterResourceOffline:
|
||
OmNotifyCb(Resource, NOTIFY_RESOURCE_POSTOFFLINE);
|
||
break;
|
||
case ClusterResourceFailed:
|
||
OmNotifyCb(Resource, NOTIFY_RESOURCE_FAILED);
|
||
break;
|
||
case ClusterResourceOnlinePending:
|
||
OmNotifyCb(Resource, NOTIFY_RESOURCE_ONLINEPENDING);
|
||
break;
|
||
case ClusterResourceOfflinePending:
|
||
OmNotifyCb(Resource, NOTIFY_RESOURCE_OFFLINEPENDING);
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
return;
|
||
|
||
}
|
||
|
||
|
||
DWORD
|
||
FmpPropagateResourceState(
|
||
IN PFM_RESOURCE Resource,
|
||
IN CLUSTER_RESOURCE_STATE State
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Propagates the state of the resource to other systems in the cluster.
|
||
Ideally the gQuoCritSec lock should be held when this routine is called.
|
||
This is because this routine checks the quorumresource field of the
|
||
resource
|
||
|
||
Arguments:
|
||
|
||
Resource - The resource to propagate state.
|
||
|
||
State - The new state for the resource.
|
||
|
||
Returns:
|
||
|
||
ERROR_SUCCESS if successful.
|
||
|
||
A Win32 error code on failure.
|
||
|
||
--*/
|
||
|
||
{
|
||
GUM_RESOURCE_STATE resourceState;
|
||
LPCWSTR resourceId;
|
||
DWORD resourceStateSize;
|
||
DWORD status= ERROR_SUCCESS;
|
||
BOOL bAcquiredQuoLock = FALSE;
|
||
|
||
//for quorum resource use the quorum lock for state changes
|
||
// for others use the group locks
|
||
if (Resource->QuorumResource)
|
||
{
|
||
ACQUIRE_EXCLUSIVE_LOCK(gQuoLock);
|
||
bAcquiredQuoLock = TRUE;
|
||
}
|
||
else
|
||
FmpAcquireLocalResourceLock( Resource );
|
||
|
||
++Resource->StateSequence;
|
||
|
||
if (! FmpFMFormPhaseProcessing )
|
||
{
|
||
//
|
||
// If this is the same state, or we don't own the group
|
||
// then don't bother propagating.
|
||
//
|
||
|
||
if ( (State == Resource->State) ||
|
||
(Resource->Group->OwnerNode != NmLocalNode) ) {
|
||
goto ReleaseResourceLock;
|
||
}
|
||
|
||
//if the previous state is the online pending and this
|
||
// is called for the quorum resource, wake up all resources
|
||
// make sure that if this is called while the form phase
|
||
//processing is going on(when the quorum group is destroyed
|
||
//and recreated), that the group is not referenced
|
||
if ((Resource->QuorumResource) &&
|
||
(Resource->State==ClusterResourceOnlinePending) &&
|
||
(Resource->Group->OwnerNode == NmLocalNode) )
|
||
{
|
||
//set the state and signal
|
||
Resource->State = State;
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] FmpPropagateResourceState: signalling the ghQuoOnlineEvent\r\n");
|
||
SetEvent(ghQuoOnlineEvent);
|
||
}
|
||
|
||
|
||
}
|
||
|
||
Resource->State = State;
|
||
|
||
//
|
||
// Prepare to notify other systems.
|
||
//
|
||
resourceId = OmObjectId( Resource );
|
||
resourceState.State = State;
|
||
resourceState.PersistentState = Resource->PersistentState;
|
||
resourceState.StateSequence = Resource->StateSequence;
|
||
|
||
status = GumSendUpdateEx(GumUpdateFailoverManager,
|
||
FmUpdateResourceState,
|
||
2,
|
||
(lstrlenW(resourceId)+1)*sizeof(WCHAR),
|
||
resourceId,
|
||
sizeof(resourceState),
|
||
&resourceState);
|
||
|
||
|
||
//
|
||
// Signal change notify event.
|
||
//
|
||
switch ( State ) {
|
||
case ClusterResourceOnline:
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] FmpPropagateResourceState: resource %1!ws! online event.\n",
|
||
OmObjectId(Resource) );
|
||
ClusterEvent(CLUSTER_EVENT_RESOURCE_ONLINE, Resource);
|
||
break;
|
||
case ClusterResourceOffline:
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] FmpPropagateResourceState: resource %1!ws! offline event.\n",
|
||
OmObjectId(Resource) );
|
||
ClusterEvent(CLUSTER_EVENT_RESOURCE_OFFLINE, Resource);
|
||
break;
|
||
case ClusterResourceFailed:
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] FmpPropagateResourceState: resource %1!ws! failed event.\n",
|
||
OmObjectId(Resource) );
|
||
ClusterEvent(CLUSTER_EVENT_RESOURCE_FAILED, Resource);
|
||
break;
|
||
case ClusterResourceOnlinePending:
|
||
case ClusterResourceOfflinePending:
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] FmpPropagateResourceState: resource %1!ws! pending event.\n",
|
||
OmObjectId(Resource) );
|
||
ClusterEvent(CLUSTER_EVENT_RESOURCE_CHANGE, Resource);
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
|
||
ReleaseResourceLock:
|
||
if (bAcquiredQuoLock)
|
||
RELEASE_LOCK(gQuoLock);
|
||
else
|
||
FmpReleaseLocalResourceLock( Resource );
|
||
|
||
|
||
if ( status != ERROR_SUCCESS )
|
||
{
|
||
ClRtlLogPrint(LOG_UNUSUAL,
|
||
"[FM] Propagation of resource %1!ws! state %2!u! failed. Error %3!u!\n",
|
||
OmObjectId(Resource),
|
||
State,
|
||
status);
|
||
goto FnExit;
|
||
}
|
||
//if fm is not completely online we dont want to propagate group state
|
||
// This is because the quorum group is destroyed in FmFormNewClusterPhase1
|
||
// and then recreated again in FmFormNewClusterPhase2.
|
||
if (FmpFMOnline)
|
||
{
|
||
OmReferenceObject(Resource->Group);
|
||
//FmpPropagateGroupState( Resource->Group );
|
||
FmpPostWorkItem(FM_EVENT_INTERNAL_PROP_GROUP_STATE, Resource->Group, 0);
|
||
}
|
||
FnExit:
|
||
return(status);
|
||
|
||
} // FmpPropagateResourceState
|
||
|
||
|
||
|
||
VOID
|
||
FmpDestroyResource(
|
||
IN PFM_RESOURCE Resource,
|
||
IN BOOL bDeleteObjOnly
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Destroys a resource. This basically snips a resource out of the
|
||
dependency tree.
|
||
|
||
First, any resources that depend on the specified
|
||
resource are recursively destroyed. This removes any dependencies
|
||
that other resources may have on the specified resource (i.e. all
|
||
resources "higher" in the dependency tree are destroyed).
|
||
|
||
Second, all the dependencies that the specified resource has are
|
||
removed. This entails dereferencing the provider resource (to remove
|
||
the reference that was added when the dependency was created) removing
|
||
the DEPENDENCY structure from its provider and dependent lists, and
|
||
finally freeing the DEPENDENCY storage.
|
||
|
||
If the resource is online, it is terminated. The resource monitor is
|
||
signalled to clean up and close the specified resource id.
|
||
|
||
Arguments:
|
||
|
||
FoundResource - Returns the found resource.
|
||
|
||
Resource - Supplies the current resource.
|
||
|
||
Name - Supplies the current resource's name.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
Notes:
|
||
|
||
The LocalResourceLock MUST be held! The lock is released before exit!
|
||
|
||
--*/
|
||
{
|
||
DWORD status;
|
||
DWORD i;
|
||
PLIST_ENTRY ListEntry;
|
||
PDEPENDENCY Dependency;
|
||
PPOSSIBLE_ENTRY possibleEntry;
|
||
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] DestroyResource: destroying %1!ws!\n",
|
||
OmObjectId(Resource));
|
||
|
||
//
|
||
// First, unlink this resource from the resource list.
|
||
//
|
||
//
|
||
// if the resource belongs to the quorum group, it is destroyed
|
||
// after the quorum logs are created so that it can be recreated
|
||
// dont remove it from the list then
|
||
if ((!bDeleteObjOnly))
|
||
status = OmRemoveObject( Resource );
|
||
|
||
//
|
||
// If anyone depends on this resource, destroy them first.
|
||
//
|
||
while (!IsListEmpty(&Resource->ProvidesFor)) {
|
||
|
||
ListEntry = Resource->ProvidesFor.Flink;
|
||
Dependency = CONTAINING_RECORD(ListEntry, DEPENDENCY, ProviderLinkage);
|
||
CL_ASSERT(Dependency->ProviderResource == Resource);
|
||
|
||
#if 0
|
||
FmpRemoveResourceDependency( Dependency->DependentResource,
|
||
Resource );
|
||
#endif
|
||
RemoveEntryList(&Dependency->ProviderLinkage);
|
||
RemoveEntryList(&Dependency->DependentLinkage);
|
||
|
||
//
|
||
// Dereference provider/dependent resource and free dependency storage
|
||
//
|
||
OmDereferenceObject(Dependency->ProviderResource);
|
||
OmDereferenceObject(Dependency->DependentResource);
|
||
LocalFree(Dependency);
|
||
}
|
||
|
||
//
|
||
// Remove our resource dependencies.
|
||
//
|
||
while (!IsListEmpty(&Resource->DependsOn)) {
|
||
|
||
ListEntry = RemoveHeadList(&Resource->DependsOn);
|
||
Dependency = CONTAINING_RECORD(ListEntry, DEPENDENCY, DependentLinkage);
|
||
CL_ASSERT(Dependency->DependentResource == Resource);
|
||
|
||
#if 0
|
||
FmpRemoveResourceDependency( Resource,
|
||
Dependency->ProviderResource );
|
||
#endif
|
||
RemoveEntryList(&Dependency->ProviderLinkage);
|
||
RemoveEntryList(&Dependency->DependentLinkage);
|
||
|
||
//
|
||
// Dereference provider/dependent resource and free dependency storage
|
||
//
|
||
OmDereferenceObject(Dependency->DependentResource);
|
||
OmDereferenceObject(Dependency->ProviderResource);
|
||
LocalFree(Dependency);
|
||
}
|
||
|
||
//
|
||
// Remove all entries from the possible owners list.
|
||
//
|
||
while ( !IsListEmpty(&Resource->PossibleOwners) ) {
|
||
ListEntry = RemoveHeadList(&Resource->PossibleOwners);
|
||
possibleEntry = CONTAINING_RECORD( ListEntry,
|
||
POSSIBLE_ENTRY,
|
||
PossibleLinkage );
|
||
OmDereferenceObject( possibleEntry->PossibleNode );
|
||
LocalFree(possibleEntry);
|
||
}
|
||
|
||
if (!bDeleteObjOnly)
|
||
{
|
||
//
|
||
// Close the resource's registry key.
|
||
//
|
||
|
||
DmRundownList( &Resource->DmRundownList );
|
||
|
||
if (Resource->RegistryKey != NULL) {
|
||
DmCloseKey( Resource->RegistryKey );
|
||
Resource->RegistryKey = NULL;
|
||
}
|
||
|
||
//
|
||
// Decrement resource type reference.
|
||
//
|
||
if ( Resource->Type != NULL ) {
|
||
OmDereferenceObject( Resource->Type );
|
||
Resource->Type = NULL;
|
||
}
|
||
|
||
// Let the worker thread peform the 'last' dereference
|
||
FmpPostWorkItem(FM_EVENT_RESOURCE_DELETED, Resource, 0);
|
||
FmpReleaseLocalResourceLock( Resource );
|
||
|
||
}
|
||
else
|
||
{
|
||
//the resource being destroyed is from the quorum group
|
||
//This is done at initialization
|
||
FmpReleaseLocalResourceLock( Resource );
|
||
|
||
|
||
// make sure that all resources except the quorum resource
|
||
// are created completely in the second phase of initialization
|
||
//decrement its ref count here, this is the last ref
|
||
//SS:: we dont use FM_EVENT_RESOURCE_DELETED here
|
||
//since we want this done synchronously before phase 2 is
|
||
//complete
|
||
OmDereferenceObject(Resource);
|
||
|
||
}
|
||
|
||
//ss: for now we dont use it, so dont delete it
|
||
//DeleteCriticalSection(&Resource->Lock);
|
||
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] FmpDestroyResource Exit.\n");
|
||
|
||
return;
|
||
|
||
} // FmpDestroyResource
|
||
|
||
|
||
|
||
///////////////////////////////////////////////////////////////////////////
|
||
//
|
||
// Initialization Routines
|
||
//
|
||
///////////////////////////////////////////////////////////////////////////
|
||
|
||
|
||
BOOL
|
||
FmDependentResource(
|
||
IN PFM_RESOURCE Resource,
|
||
IN PFM_RESOURCE DependentResource,
|
||
IN BOOL ImmediateOnly
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Returns indication of whether a resource is a dependent of another
|
||
resource.
|
||
|
||
Arguments:
|
||
|
||
Resource - The resource to scan if it depends on the dependent resource.
|
||
|
||
DependentResource - The dependent resource to check on.
|
||
|
||
ImmediateOnly - Specifies whether only immediate dependencies should be
|
||
checked. If this is FALSE, this routine recursively checks all
|
||
dependents.
|
||
|
||
Returns:
|
||
|
||
TRUE - The the resource does depend on the dependent resource.
|
||
|
||
FALSE - The resource does not depend on the dependent resource.
|
||
|
||
--*/
|
||
|
||
{
|
||
PLIST_ENTRY listEntry;
|
||
PDEPENDENCY dependency;
|
||
BOOL result = FALSE;
|
||
|
||
FmpAcquireLocalResourceLock( Resource );
|
||
|
||
listEntry = Resource->DependsOn.Flink;
|
||
while ( listEntry != &Resource->DependsOn ) {
|
||
dependency = CONTAINING_RECORD( listEntry,
|
||
DEPENDENCY,
|
||
DependentLinkage );
|
||
if ( dependency->ProviderResource == DependentResource ) {
|
||
result = TRUE;
|
||
break;
|
||
} else {
|
||
if (!ImmediateOnly) {
|
||
if (FmDependentResource(dependency->ProviderResource,
|
||
DependentResource,
|
||
FALSE)) {
|
||
result = TRUE;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
listEntry = listEntry->Flink;
|
||
|
||
} // while
|
||
|
||
FmpReleaseLocalResourceLock( Resource );
|
||
|
||
return(result);
|
||
|
||
} // FmpDependentResource
|
||
|
||
|
||
DWORD
|
||
FmpAddPossibleEntry(
|
||
IN PFM_RESOURCE Resource,
|
||
IN PNM_NODE Node
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Creates a new possible node entry and adds it to a resource's list.
|
||
|
||
If the node is already in the resource's list, it will not be added
|
||
again.
|
||
|
||
Arguments:
|
||
|
||
Resource - Supplies the resource whose node list is to be updated.
|
||
|
||
Node - Supplies the node to be added to the resource's list.
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS if successful.
|
||
|
||
Win32 error otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
PLIST_ENTRY ListEntry;
|
||
PPOSSIBLE_ENTRY PossibleEntry;
|
||
|
||
//
|
||
// First check to see if the node is already in the possible owners list.
|
||
//
|
||
ListEntry = Resource->PossibleOwners.Flink;
|
||
while (ListEntry != &Resource->PossibleOwners) {
|
||
PossibleEntry = CONTAINING_RECORD( ListEntry,
|
||
POSSIBLE_ENTRY,
|
||
PossibleLinkage );
|
||
if (PossibleEntry->PossibleNode == Node) {
|
||
//
|
||
// Found a match, it's already here, so return
|
||
// success.
|
||
//
|
||
return(ERROR_SUCCESS);
|
||
}
|
||
ListEntry = ListEntry->Flink;
|
||
}
|
||
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] FmpAddPossibleEntry: adding node %1!ws! as possible host for resource %2!ws!.\n",
|
||
OmObjectId(Node),
|
||
OmObjectId(Resource));
|
||
|
||
PossibleEntry = LocalAlloc(LMEM_FIXED, sizeof(POSSIBLE_ENTRY));
|
||
if (PossibleEntry == NULL) {
|
||
ClRtlLogPrint(LOG_CRITICAL,
|
||
"[FM] FmpAddPossibleEntry failed to allocated PossibleEntry\n");
|
||
return(ERROR_NOT_ENOUGH_MEMORY);
|
||
}
|
||
OmReferenceObject(Node);
|
||
PossibleEntry->PossibleNode = Node;
|
||
InsertTailList( &Resource->PossibleOwners,
|
||
&PossibleEntry->PossibleLinkage );
|
||
|
||
return(ERROR_SUCCESS);
|
||
|
||
}
|
||
|
||
|
||
DWORD
|
||
FmpAddPossibleNode(
|
||
IN PFM_RESOURCE Resource,
|
||
IN PNM_NODE Node
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Adds a node to the resource's list of possible nodes.
|
||
|
||
The resource lock must be held.
|
||
|
||
Arguments:
|
||
|
||
Resource - Supplies the resource whose list of nodes is to be updated
|
||
|
||
Node - Supplies the node to add to the specified resource's list.
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS if successful
|
||
|
||
Win32 error otherwise
|
||
|
||
--*/
|
||
|
||
{
|
||
HDMKEY hGroup;
|
||
DWORD Status;
|
||
|
||
|
||
//
|
||
// Allocate the new possible node entry and add it to the list.
|
||
//
|
||
Status = FmpAddPossibleEntry(Resource, Node);
|
||
if (Status != ERROR_SUCCESS) {
|
||
return(Status);
|
||
}
|
||
|
||
//
|
||
// Need to check the group list to see if the specified node
|
||
// can be added to the preferred list now. The easiest way
|
||
// to do that is to simply recreate the entire preferred list,
|
||
// then reprune.
|
||
//
|
||
hGroup = DmOpenKey( DmGroupsKey,
|
||
OmObjectId(Resource->Group),
|
||
KEY_READ );
|
||
if (hGroup == NULL) {
|
||
Status = GetLastError();
|
||
ClRtlLogPrint(LOG_UNUSUAL,
|
||
"[FM] FmpAddPossibleNode failed to open group %1!ws! status %2!d!\n",
|
||
OmObjectId(Resource->Group),
|
||
Status);
|
||
|
||
return(Status);
|
||
}
|
||
Status = FmpQueryGroupNodes(Resource->Group,
|
||
hGroup);
|
||
CL_ASSERT(Status == ERROR_SUCCESS);
|
||
if (Status == ERROR_SUCCESS) {
|
||
FmpPruneGroupOwners(Resource->Group);
|
||
}
|
||
DmCloseKey(hGroup);
|
||
|
||
|
||
return(Status);
|
||
|
||
} // FmpAddPossibleNode
|
||
|
||
|
||
DWORD
|
||
FmpRemovePossibleNode(
|
||
IN PFM_RESOURCE Resource,
|
||
IN PNM_NODE Node,
|
||
IN BOOL RemoveQuorum
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Removes a node from the resource's list of possible nodes.
|
||
|
||
The resource lock must be held.
|
||
|
||
Arguments:
|
||
|
||
Resource - Supplies the resource whose list of nodes is to be updated
|
||
|
||
Node - Supplies the node to be removed from the specified resource's list.
|
||
|
||
RemoveQuorum - TRUE if we can remove node from quorum device.
|
||
FALSE otherwise.
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS if successful
|
||
|
||
Win32 error otherwise
|
||
|
||
--*/
|
||
|
||
{
|
||
PLIST_ENTRY ListEntry;
|
||
PPOSSIBLE_ENTRY PossibleEntry;
|
||
DWORD Status = ERROR_CLUSTER_NODE_NOT_FOUND;
|
||
|
||
//
|
||
// If the resource is currently online on the node to be removed,
|
||
// fail the call.
|
||
//
|
||
if ((Resource->Group->OwnerNode == Node) &&
|
||
(Resource->State == ClusterResourceOnline)) {
|
||
return(ERROR_INVALID_STATE);
|
||
}
|
||
|
||
//
|
||
// If it is NOT okay to remove this node from the quorum device,
|
||
// and this is the quorum device, then fail the request.
|
||
//
|
||
if ( !RemoveQuorum &&
|
||
Resource->QuorumResource) {
|
||
return(ERROR_INVALID_OPERATION_ON_QUORUM);
|
||
}
|
||
|
||
//
|
||
// Find the possible entry on the resource's list.
|
||
//
|
||
|
||
ListEntry = Resource->PossibleOwners.Flink;
|
||
while (ListEntry != &Resource->PossibleOwners) {
|
||
PossibleEntry = CONTAINING_RECORD( ListEntry,
|
||
POSSIBLE_ENTRY,
|
||
PossibleLinkage );
|
||
ListEntry = ListEntry->Flink;
|
||
if (PossibleEntry->PossibleNode == Node) {
|
||
//
|
||
// Found a match, remove it from the list.
|
||
//
|
||
RemoveEntryList(&PossibleEntry->PossibleLinkage);
|
||
OmDereferenceObject(PossibleEntry->PossibleNode);
|
||
LocalFree(PossibleEntry);
|
||
|
||
//
|
||
// Now prune the containing group. This is a little bit
|
||
// of overkill, if we were smarter, we could just
|
||
// remove the node from the preferred list directly.
|
||
//
|
||
FmpPrunePreferredList(Resource);
|
||
Status = ERROR_SUCCESS;
|
||
break;
|
||
}
|
||
}
|
||
|
||
return(Status);
|
||
|
||
} // FmpRemovePossibleNode
|
||
|
||
|
||
|
||
DWORD
|
||
FmpRemoveResourceDependency(
|
||
HLOCALXSACTION hXsaction,
|
||
IN PFM_RESOURCE Resource,
|
||
IN PFM_RESOURCE DependsOn
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Removes a dependency relationship to a given resource. Both
|
||
resources must be in the same group.
|
||
|
||
Arguments:
|
||
|
||
Resource - Supplies the resource which is dependent.
|
||
|
||
DependsOn - Supplies the resource that hResource depends on.
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS if successful.
|
||
|
||
Win32 error code otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD status;
|
||
HDMKEY resKey = NULL;
|
||
|
||
//
|
||
// If the resources are not in the same group, fail the
|
||
// call. Also fail if some one tries to make a resource
|
||
// dependent upon itself.
|
||
//
|
||
if ((Resource->Group != DependsOn->Group) ||
|
||
(Resource == DependsOn)) {
|
||
return(ERROR_INVALID_PARAMETER);
|
||
}
|
||
|
||
//
|
||
// Remove the dependency from the registry database.
|
||
//
|
||
resKey = DmOpenKey(DmResourcesKey,
|
||
OmObjectId(Resource),
|
||
KEY_READ | KEY_SET_VALUE);
|
||
if (resKey == NULL)
|
||
{
|
||
status = GetLastError();
|
||
CL_LOGFAILURE(status);
|
||
goto FnExit;
|
||
}
|
||
else
|
||
{
|
||
status = DmLocalRemoveFromMultiSz(hXsaction,
|
||
resKey,
|
||
CLUSREG_NAME_RES_DEPENDS_ON,
|
||
OmObjectId(DependsOn));
|
||
}
|
||
|
||
FnExit:
|
||
if ( resKey ) {
|
||
DmCloseKey(resKey);
|
||
}
|
||
return(status);
|
||
|
||
} // FmpRemoveResourceDependency
|
||
|
||
|
||
DWORD
|
||
FmpChangeResourceGroup(
|
||
IN PFM_RESOURCE pResource,
|
||
IN PFM_GROUP pNewGroup
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Moves a resource from one group to another.
|
||
|
||
Arguments:
|
||
|
||
pResource - Supplies the resource to move.
|
||
|
||
pNewGroup - Supplies the new group that the resource should be in.
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS if successful
|
||
|
||
Win32 error code otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD dwBufSize;
|
||
LPCWSTR pszResourceId;
|
||
DWORD dwResourceLen;
|
||
LPCWSTR pszGroupId;
|
||
DWORD dwGroupLen;
|
||
DWORD dwStatus;
|
||
PGUM_CHANGE_GROUP pGumChange;
|
||
|
||
// we need to validate here as well
|
||
// this is called by the server side
|
||
// this will help avoid a gum call if things have changed
|
||
// since the request started from the originator
|
||
// and got to the server
|
||
|
||
//
|
||
// Check if we're moving to same group.
|
||
//
|
||
if (pResource->Group == pNewGroup) {
|
||
dwStatus = ERROR_ALREADY_EXISTS;
|
||
goto FnExit;
|
||
}
|
||
|
||
//
|
||
// 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 FnExit;
|
||
}
|
||
|
||
|
||
pszResourceId = OmObjectId(pResource);
|
||
dwResourceLen = (lstrlenW(pszResourceId)+1)*sizeof(WCHAR);
|
||
|
||
pszGroupId = OmObjectId(pNewGroup);
|
||
dwGroupLen = (lstrlenW(pszGroupId)+1)*sizeof(WCHAR);
|
||
|
||
dwBufSize = sizeof(GUM_CHANGE_GROUP) - sizeof(WCHAR) + dwResourceLen + dwGroupLen;
|
||
pGumChange = LocalAlloc(LMEM_FIXED, dwBufSize);
|
||
if (pGumChange == NULL) {
|
||
dwStatus = ERROR_NOT_ENOUGH_MEMORY;
|
||
goto FnExit;
|
||
}
|
||
|
||
pGumChange->ResourceIdLen = dwResourceLen;
|
||
CopyMemory(pGumChange->ResourceId, pszResourceId, dwResourceLen);
|
||
CopyMemory((PCHAR)pGumChange->ResourceId + dwResourceLen,
|
||
pszGroupId,
|
||
dwGroupLen);
|
||
dwStatus = GumSendUpdate(GumUpdateFailoverManager,
|
||
FmUpdateChangeGroup,
|
||
dwBufSize,
|
||
pGumChange);
|
||
LocalFree(pGumChange);
|
||
|
||
FnExit:
|
||
return(dwStatus);
|
||
}// FmpChangeResourceGroup
|
||
|
||
|
||
DWORD
|
||
FmpClusterEventPropHandler(
|
||
IN PFM_RESOURCE pResource
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Post a worker item to process a cluster name change.
|
||
|
||
Arguments:
|
||
|
||
pResource - pointer to the resouce which is affected by the cluster
|
||
property change.
|
||
|
||
Return Value:
|
||
|
||
|
||
ERROR_SUCCESS if successful.
|
||
A Win32 error code on failure.
|
||
|
||
--*/
|
||
|
||
{
|
||
PFM_RESTYPE pResType;
|
||
DWORD dwError=ERROR_SUCCESS;
|
||
|
||
pResType = pResource->Type;
|
||
|
||
if ((pResource->ExFlags & CLUS_FLAG_CORE) &&
|
||
( !lstrcmpiW(OmObjectId(pResType), CLUS_RESTYPE_NAME_NETNAME)))
|
||
{
|
||
FmResourceControl(pResource, NmLocalNode,
|
||
CLUSCTL_RESOURCE_CLUSTER_NAME_CHANGED, NULL, 0, NULL, 0, NULL, NULL);
|
||
|
||
}
|
||
return (dwError);
|
||
|
||
} // FmpClusterEventPropHandler
|
||
|
||
|
||
|
||
BOOL
|
||
FmpEnumResourceNodeEvict(
|
||
IN PVOID Context1,
|
||
IN PVOID Context2,
|
||
IN PVOID Object,
|
||
IN LPCWSTR Name
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Resource enumeration callback for removing node references when
|
||
a node is evicted.
|
||
|
||
Arguments:
|
||
|
||
Context1 - Supplies the node that is being evicted.
|
||
|
||
Context2 - not used
|
||
|
||
Object - Supplies a pointer to the resource object
|
||
|
||
Name - Supplies the resource's object name.
|
||
|
||
Return Value:
|
||
|
||
TRUE to continue enumeration
|
||
|
||
--*/
|
||
|
||
{
|
||
PFM_RESOURCE Resource = (PFM_RESOURCE)Object;
|
||
PNM_NODE Node = (PNM_NODE)Context1;
|
||
PLIST_ENTRY listEntry;
|
||
PPOSSIBLE_ENTRY possibleEntry;
|
||
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] EnumResourceNodeEvict: Removing references to node %1!ws! from resource %2!ws!\n",
|
||
OmObjectId(Node),
|
||
OmObjectId(Resource));
|
||
|
||
FmpAcquireLocalResourceLock(Resource);
|
||
FmpRemovePossibleNode(Resource, Node, TRUE);
|
||
FmpReleaseLocalResourceLock(Resource);
|
||
//
|
||
// Notify the resource of the removal of the node.
|
||
//
|
||
FmpRmResourceControl( Resource,
|
||
CLUSCTL_RESOURCE_EVICT_NODE,
|
||
(PUCHAR)OmObjectId(Node),
|
||
((lstrlenW(OmObjectId(Node)) + 1) * sizeof(WCHAR)),
|
||
NULL,
|
||
0,
|
||
NULL,
|
||
NULL );
|
||
// Ignore status return
|
||
|
||
ClusterEvent( CLUSTER_EVENT_RESOURCE_PROPERTY_CHANGE, Resource );
|
||
|
||
return(TRUE);
|
||
|
||
} // FmpEnumResourceNodeEvict
|
||
|
||
|
||
|
||
DWORD
|
||
FmpPrepareQuorumResChange(
|
||
IN PFM_RESOURCE pNewQuoRes,
|
||
IN LPCWSTR lpszQuoLogPath,
|
||
IN DWORD dwMaxQuoLogSize
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine prepares for a quorum resource change operation.
|
||
|
||
Arguments:
|
||
|
||
pNewQuoRes - pointer to the new quorum resource.
|
||
|
||
lpszQuoLogPath - pointer to the quorum log path string name.
|
||
|
||
dwMaxQuoLogSize - the maximum size of the quorum log path string.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
CL_ASSERT(pNewQuoRes->Group->OwnerNode == NmLocalNode);
|
||
|
||
return(DmPrepareQuorumResChange(pNewQuoRes, lpszQuoLogPath, dwMaxQuoLogSize));
|
||
|
||
} // FmpPrepareQuorumResChange
|
||
|
||
|
||
DWORD
|
||
FmpCompleteQuorumResChange(
|
||
IN LPCWSTR lpszOldQuoResId,
|
||
IN LPCWSTR lpszQuoLogPath
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called if the new quorum log path is not the same as the old
|
||
quorum log path. This completes the change of quorum resource by deleting the old
|
||
quorum log files and creating a tompstone for them. A node that tries to do a form
|
||
with this as the quorum resource is prevented and has to do a join to get the location
|
||
of the new quorum resource and quorum log file.
|
||
|
||
Arguments:
|
||
|
||
pOldQuoRes - pointer to the new quorum resource.
|
||
|
||
lpszOldQuoLogPath - pointer to the quorum log path string name.
|
||
|
||
dwMaxQuoLogSize - the maximum size of the quorum log path string.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
return(DmCompleteQuorumResChange(lpszOldQuoResId, lpszQuoLogPath));
|
||
|
||
} // FmpCompleteQuorumResChange
|
||
|
||
|
||
|
||
VOID
|
||
FmpResourceLastReference(
|
||
IN PFM_RESOURCE Resource
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Last dereference to resource object processing routine.
|
||
All cleanup for a resource should really be done here!
|
||
|
||
Arguments:
|
||
|
||
Resource - pointer the resource being removed.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
if ( Resource->DebugPrefix != NULL )
|
||
LocalFree(Resource->DebugPrefix);
|
||
if (Resource->Dependencies)
|
||
LocalFree(Resource->Dependencies);
|
||
if ( Resource->Group ) {
|
||
OmDereferenceObject(Resource->Group);
|
||
}
|
||
if (Resource->Type)
|
||
OmDereferenceObject(Resource->Type);
|
||
return;
|
||
|
||
} // FmpResourceLastReference
|
||
|
||
|
||
|
||
BOOL
|
||
FmpCheckNetworkDependencyWorker(
|
||
IN LPCWSTR DependentNetwork,
|
||
OUT PBOOL FoundDependency,
|
||
IN PFM_RESOURCE Resource,
|
||
IN LPCWSTR Name
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Enumeration callback routine for finding an IP Address resource
|
||
and checking its dependency on given network guid.
|
||
|
||
Arguments:
|
||
|
||
DependentNetwork - The GUID of the network to check for a dependency.
|
||
|
||
FoundDependency - Returns TRUE if a dependency is found.
|
||
|
||
Resource - Supplies the current resource.
|
||
|
||
Name - Supplies the current resource's name.
|
||
|
||
Return Value:
|
||
|
||
TRUE - to continue searching
|
||
|
||
FALSE - to stop the search. The matching resource is returned in
|
||
*FoundResource
|
||
|
||
Notes:
|
||
|
||
The IP Address resource's parameters are searched directly by this
|
||
routine. Fetching them from the resource itself causes a deadlock.
|
||
This routine is called by the NM from within a global udpate
|
||
handler. The resource would have to call back into the cluster registry
|
||
routines, which would deadlock over the GUM lock.
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOL returnValue = TRUE;
|
||
|
||
|
||
if ( lstrcmpiW(
|
||
OmObjectId(Resource->Type),
|
||
CLUS_RESTYPE_NAME_IPADDR
|
||
) == 0
|
||
)
|
||
{
|
||
LPCWSTR resourceId = OmObjectId(Resource);
|
||
DWORD status;
|
||
HDMKEY resourceKey = DmOpenKey(DmResourcesKey, resourceId, KEY_READ);
|
||
|
||
if (resourceKey != NULL) {
|
||
HDMKEY paramsKey = DmOpenKey(
|
||
resourceKey,
|
||
L"Parameters",
|
||
KEY_READ
|
||
);
|
||
|
||
if (paramsKey != NULL) {
|
||
LPWSTR networkId = NULL;
|
||
DWORD valueLength = 0, valueSize = 0;
|
||
|
||
status = DmQueryString(
|
||
paramsKey,
|
||
L"Network",
|
||
REG_SZ,
|
||
&networkId,
|
||
&valueLength,
|
||
&valueSize
|
||
);
|
||
|
||
if (status == ERROR_SUCCESS) {
|
||
|
||
if ( lstrcmpiW( networkId, DependentNetwork ) == 0 ) {
|
||
*FoundDependency = TRUE;
|
||
returnValue = FALSE;
|
||
}
|
||
|
||
LocalFree(networkId);
|
||
}
|
||
else {
|
||
ClRtlLogPrint(LOG_WARNING,
|
||
"[NM] Query of Network value failed for ip addr resource %1!ws!, status %2!u!.\n",
|
||
resourceId,
|
||
status
|
||
);
|
||
}
|
||
|
||
DmCloseKey(paramsKey);
|
||
}
|
||
else {
|
||
status = GetLastError();
|
||
ClRtlLogPrint(LOG_WARNING,
|
||
"[FM] Failed to open params key for resource %1!ws!, status %2!u!\n",
|
||
resourceId,
|
||
status
|
||
);
|
||
}
|
||
|
||
DmCloseKey(resourceKey);
|
||
}
|
||
else {
|
||
status = GetLastError();
|
||
ClRtlLogPrint(LOG_WARNING,
|
||
"[FM] Failed to open key for resource %1!ws!, status %2!u!\n",
|
||
resourceId,
|
||
status
|
||
);
|
||
}
|
||
}
|
||
|
||
return(returnValue);
|
||
|
||
} // FmpCheckNetworkDependencyWorker
|
||
|
||
//lock must be held when this routine is called
|
||
DWORD FmpChangeResourceNode(
|
||
IN PFM_RESOURCE Resource,
|
||
IN LPCWSTR NodeId,
|
||
IN BOOL Add
|
||
|
||
)
|
||
{
|
||
PGUM_CHANGE_POSSIBLE_NODE GumChange;
|
||
LPCWSTR ResourceId;
|
||
DWORD ResourceLen;
|
||
DWORD NodeLen;
|
||
DWORD BufSize;
|
||
DWORD Status;
|
||
PLIST_ENTRY pListEntry;
|
||
PRESTYPE_POSSIBLE_ENTRY pResTypePosEntry = NULL;
|
||
BOOL bNodeSupportsResType = FALSE;
|
||
BOOL bRecalc = TRUE;
|
||
PPOSSIBLE_ENTRY PossibleEntry;
|
||
PNM_NODE pNode = NULL;
|
||
|
||
if ( Resource->QuorumResource ) {
|
||
Status = ERROR_INVALID_OPERATION_ON_QUORUM;
|
||
goto FnExit;
|
||
}
|
||
|
||
//
|
||
// We can't allow the owner node to be removed if the state
|
||
// of the resource or the group is not offline or failed.
|
||
//
|
||
if ( !Add &&
|
||
(NodeId == OmObjectId(NmLocalNode)) &&
|
||
(((Resource->State != ClusterResourceOffline) &&
|
||
(Resource->State != ClusterResourceFailed)) ||
|
||
(FmpGetGroupState( Resource->Group, TRUE ) != ClusterGroupOffline)) ) {
|
||
Status = ERROR_INVALID_STATE;
|
||
goto FnExit;
|
||
}
|
||
|
||
//make sure the node is on the list of possible nodes for this
|
||
// resource type
|
||
if (Add)
|
||
{
|
||
//if it is already on the list, return an error and dont
|
||
//send a gum update call
|
||
|
||
pNode = OmReferenceObjectById(ObjectTypeNode, NodeId);
|
||
pListEntry = Resource->PossibleOwners.Flink;
|
||
while (pListEntry != &Resource->PossibleOwners) {
|
||
PossibleEntry = CONTAINING_RECORD( pListEntry,
|
||
POSSIBLE_ENTRY,
|
||
PossibleLinkage );
|
||
if (PossibleEntry->PossibleNode == pNode) {
|
||
//
|
||
// Found a match, fail the duplicate add. Note that
|
||
// we must fail here, not succeed, so that the API
|
||
// layer knows not to add a duplicate to the registry.
|
||
//
|
||
Status = ERROR_OBJECT_ALREADY_EXISTS;
|
||
goto FnExit;
|
||
}
|
||
pListEntry = pListEntry->Flink;
|
||
}
|
||
|
||
ChkResTypeList:
|
||
pListEntry = &(Resource->Type->PossibleNodeList);
|
||
for (pListEntry = pListEntry->Flink;
|
||
pListEntry != &(Resource->Type->PossibleNodeList);
|
||
pListEntry = pListEntry->Flink)
|
||
{
|
||
|
||
pResTypePosEntry = CONTAINING_RECORD(pListEntry, RESTYPE_POSSIBLE_ENTRY,
|
||
PossibleLinkage);
|
||
|
||
if (!lstrcmpW(OmObjectId(pResTypePosEntry->PossibleNode), NodeId))
|
||
{
|
||
bNodeSupportsResType = TRUE;
|
||
break;
|
||
}
|
||
|
||
}
|
||
|
||
if (!bNodeSupportsResType && bRecalc)
|
||
{
|
||
//if th node is not found, recalc again and retry..since then the
|
||
//dll might have been copied to this node
|
||
FmpSetPossibleNodeForResType(OmObjectId(Resource->Type), TRUE);
|
||
bRecalc = FALSE;
|
||
goto ChkResTypeList;
|
||
}
|
||
if (!bNodeSupportsResType)
|
||
{
|
||
Status = ERROR_CLUSTER_RESTYPE_NOT_SUPPORTED;
|
||
goto FnExit;
|
||
}
|
||
}
|
||
|
||
ResourceId = OmObjectId(Resource);
|
||
ResourceLen = (lstrlenW(ResourceId)+1)*sizeof(WCHAR);
|
||
|
||
NodeLen = (lstrlenW(NodeId)+1)*sizeof(WCHAR);
|
||
|
||
BufSize = sizeof(GUM_CHANGE_POSSIBLE_NODE) - sizeof(WCHAR) + ResourceLen + NodeLen;
|
||
GumChange = LocalAlloc(LMEM_FIXED, BufSize);
|
||
if (GumChange == NULL) {
|
||
CsInconsistencyHalt( ERROR_NOT_ENOUGH_MEMORY );
|
||
Status = ERROR_NOT_ENOUGH_MEMORY;
|
||
goto FnExit;
|
||
}
|
||
|
||
GumChange->ResourceIdLen = ResourceLen;
|
||
CopyMemory(GumChange->ResourceId, ResourceId, ResourceLen);
|
||
CopyMemory((PCHAR)GumChange->ResourceId + ResourceLen,
|
||
NodeId,
|
||
NodeLen);
|
||
|
||
Status = GumSendUpdate(GumUpdateFailoverManager,
|
||
Add ? FmUpdateAddPossibleNode : FmUpdateRemovePossibleNode,
|
||
BufSize,
|
||
GumChange);
|
||
LocalFree(GumChange);
|
||
|
||
FnExit:
|
||
if (pNode)
|
||
OmDereferenceObject(pNode);
|
||
return(Status);
|
||
|
||
}
|
||
|
||
|
||
|
||
BOOL
|
||
FmpCheckNetworkDependency(
|
||
IN LPCWSTR DependentNetwork
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Checks for an IP Address resource that may be dependent on the given
|
||
Network.
|
||
|
||
Arguments:
|
||
|
||
DependentNetwork - the dependent network to check for.
|
||
|
||
Returns:
|
||
|
||
TRUE - if an IP Address depends on the given network.
|
||
FALSE otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOL dependent = FALSE;
|
||
|
||
OmEnumObjects(ObjectTypeResource,
|
||
(OM_ENUM_OBJECT_ROUTINE)FmpCheckNetworkDependencyWorker,
|
||
(PVOID)DependentNetwork,
|
||
&dependent);
|
||
|
||
return(dependent);
|
||
|
||
} // FmpCheckNetworkDependency
|
||
|
||
/****
|
||
@func DWORD | FmpFixupPossibleNodesForResources| This fixes the possible
|
||
node information for a resource based on whether this node
|
||
supports the given resource type.
|
||
|
||
@parm IN BOOL| bJoin | If this node is joining, bJoin is set to TRUE.
|
||
|
||
@comm This routine iterates thru all the resources in a system and fixes
|
||
their possible node information. If this node is not on the possible
|
||
node list for the resource type corresponding to the resource, it
|
||
is also removed from the possible node list for the resource.
|
||
|
||
@rdesc Returns a result code. ERROR_SUCCESS on success.
|
||
|
||
@xref <f FmpEnumFixupPossibleNodeForResource>
|
||
****/
|
||
DWORD
|
||
FmpFixupPossibleNodesForResources(
|
||
BOOL bJoin
|
||
)
|
||
{
|
||
DWORD dwStatus=ERROR_SUCCESS;
|
||
|
||
ClRtlLogPrint(LOG_NOISE,"[FM] FmpFixupPossibleNodesForResources Entry.\n");
|
||
|
||
//
|
||
// Fix up all resources's possible node list information
|
||
//
|
||
OmEnumObjects( ObjectTypeResource,
|
||
FmpEnumFixupPossibleNodesForResource,
|
||
NULL,
|
||
NULL);
|
||
|
||
|
||
ClRtlLogPrint(LOG_NOISE,"[FM] FmpFixupPossibleNodesForResources Exit\r\n");
|
||
|
||
return(dwStatus);
|
||
|
||
} // FmpFixupPossibleNodesForResources
|
||
|
||
/****
|
||
@func DWORD | FmpEnumFixupPossibleNodesForResource | This is the enumeration
|
||
callback for every resource type to fix the possible node
|
||
information related with it.
|
||
|
||
@parm IN PVOID | pContext1 | Not used.
|
||
@parm IN PVOID | pContext2 | Not Used.
|
||
@parm IN PFM_RESTYPE | pResType | Pointer to the resource type object.
|
||
@parm IN LPCWSTR | pszResTypeName | The name of the resource type.
|
||
|
||
@comm This routine iterates thru all the resources in a system and fixes
|
||
their possible node information. If this node is not on the possible
|
||
node list for the resource type corresponding to the resource, it
|
||
is also removed from the possible node list for the resource.
|
||
|
||
@rdesc Returns a result code. ERROR_SUCCESS on success.
|
||
|
||
@xref <f FmpFixupPossibleNodesForResources>
|
||
****/
|
||
BOOL
|
||
FmpEnumFixupPossibleNodesForResource(
|
||
IN PVOID pContext1,
|
||
IN PVOID pContext2,
|
||
IN PFM_RESOURCE pResource,
|
||
IN LPCWSTR pszResName
|
||
)
|
||
{
|
||
|
||
|
||
//if we are on the possible node list for the
|
||
//resource but not for the resource type, remove it
|
||
//from the possible node for the resource as well.
|
||
//We do this because the join logic adds all nodes
|
||
//as possible owners for a resource and we have
|
||
//the rolling upgrade requirements - hence the fixups
|
||
//have to be made later on
|
||
if ((FmpInPossibleListForResource(pResource, NmLocalNode)) &&
|
||
!(FmpInPossibleListForResType(pResource->Type, NmLocalNode)))
|
||
{
|
||
//if we dont support this resource type, make sure it is not on the possible node
|
||
//list for a resource of this type
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] FmpEnumFixupPossibleNode:remove local node for resource %1!ws!\r\n",
|
||
OmObjectId(pResource));
|
||
//we send a gum update to remove it from all nodes
|
||
FmChangeResourceNode(pResource, NmLocalNode, FALSE);
|
||
}
|
||
|
||
//we add ourselves on the list only on a fresh install
|
||
//not on an upgrade
|
||
//csfirst run is also true on an upgrade, hence we need to
|
||
//check that csupgrade is false
|
||
if ((!FmpInPossibleListForResource(pResource, NmLocalNode)) &&
|
||
(FmpInPossibleListForResType(pResource->Type, NmLocalNode))
|
||
&& CsFirstRun && !CsUpgrade)
|
||
{
|
||
//if we support a resource of this type, but we are not on the possible
|
||
//list for this resource, then add the local node to the possible list
|
||
//this may happen because on a setup join the other nodes may not
|
||
//add us because the possible node list exists. The possible node list
|
||
//may exist either because the user set it or we internally set it due
|
||
//to non availability of this resource type dll on one of the nodes
|
||
//Note that irrespective of whether the user had set the possible list
|
||
//or we set it internally, we always add a new node that joins
|
||
//to the possible node list of resources that are supported.
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] FmpEnumFixupPossibleNode:add local node for resource %1!ws!\r\n",
|
||
OmObjectId(pResource));
|
||
//we send a gum update to add it from all nodes
|
||
FmChangeResourceNode(pResource, NmLocalNode, TRUE);
|
||
}
|
||
//continue enumeration
|
||
return (TRUE);
|
||
}
|
||
|
||
|
||
DWORD FmpCleanupPossibleNodeList(
|
||
IN PFM_RESOURCE pResource)
|
||
{
|
||
|
||
PLIST_ENTRY pListEntry;
|
||
PPOSSIBLE_ENTRY pPossibleEntry;
|
||
DWORD dwStatus = ERROR_SUCCESS;
|
||
|
||
//for all possible nodes for this resource, check if the resource type
|
||
//supports it. If it doesnt, then remove that node from the in memory list
|
||
pListEntry = pResource->PossibleOwners.Flink;
|
||
while (pListEntry != &pResource->PossibleOwners)
|
||
{
|
||
//get the possible entry at this link
|
||
pPossibleEntry = CONTAINING_RECORD( pListEntry,
|
||
POSSIBLE_ENTRY,
|
||
PossibleLinkage );
|
||
//save the pointer to the next link
|
||
pListEntry = pListEntry->Flink;
|
||
|
||
if (!FmpInPossibleListForResType(pResource->Type,
|
||
pPossibleEntry->PossibleNode))
|
||
{
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] FmpCleanupPossibleNodeList:remove local node %1!u! for resource %2!ws!\r\n",
|
||
NmGetNodeId(pPossibleEntry->PossibleNode), OmObjectId(pResource));
|
||
FmChangeResourceNode(pResource, pPossibleEntry->PossibleNode,
|
||
FALSE);
|
||
}
|
||
}
|
||
|
||
return (dwStatus);
|
||
}
|
||
|
||
|
||
/****
|
||
@func DWORD | FmpInPossibleListForResource| This checks if a given node
|
||
is in the possible list of nodes for a resource.
|
||
|
||
@parm IN PFM_RESOURCE | pResource | A pointer to the the resource.
|
||
@parm IN PNM_NODE | pNode | A pointer to the node object.
|
||
|
||
@comm This routine check if a node is in the list of possible nodes
|
||
for this resource.
|
||
|
||
@rdesc Returns a result code. ERROR_SUCCESS on success.
|
||
|
||
@xref <f FmpInPossibleListForResType>
|
||
****/
|
||
BOOL
|
||
FmpInPossibleListForResource(
|
||
IN PFM_RESOURCE pResource,
|
||
IN PNM_NODE pNode
|
||
)
|
||
{
|
||
PLIST_ENTRY plistEntry;
|
||
PPOSSIBLE_ENTRY pPossibleEntry;
|
||
|
||
//see if this node is on the possible node list for the resource
|
||
for ( plistEntry = pResource->PossibleOwners.Flink;
|
||
plistEntry != &(pResource->PossibleOwners);
|
||
plistEntry = plistEntry->Flink ) {
|
||
|
||
pPossibleEntry = CONTAINING_RECORD( plistEntry,
|
||
POSSIBLE_ENTRY,
|
||
PossibleLinkage );
|
||
if ( pPossibleEntry->PossibleNode == pNode ) {
|
||
return(TRUE);
|
||
}
|
||
}
|
||
|
||
return(FALSE);
|
||
|
||
} // FmpInPossibleListForResource
|
||
|
||
|
||
/****
|
||
@func DWORD | FmpInPossibleListForResType| This checks if a given node
|
||
is in the possible list of nodes for a resource type.
|
||
|
||
@parm IN PFM_RESTYPE| pResType | A pointer to the the resource type.
|
||
@parm IN PNM_NODE | pNode | A pointer to the node object.
|
||
|
||
@comm This routine check if a node is in the list of possible nodes
|
||
for this resource type.
|
||
|
||
@rdesc Returns a result code. ERROR_SUCCESS on success.
|
||
|
||
@xref <f FmpInPossibleListForResource>
|
||
****/
|
||
BOOL
|
||
FmpInPossibleListForResType(
|
||
IN PFM_RESTYPE pResType,
|
||
IN PNM_NODE pNode
|
||
)
|
||
{
|
||
PLIST_ENTRY pListEntry;
|
||
PRESTYPE_POSSIBLE_ENTRY pResTypePosEntry;
|
||
|
||
ACQUIRE_SHARED_LOCK(gResTypeLock);
|
||
|
||
//see if this node is on the possible node list for the resource
|
||
for ( pListEntry = pResType->PossibleNodeList.Flink;
|
||
pListEntry != &(pResType->PossibleNodeList);
|
||
pListEntry = pListEntry->Flink )
|
||
{
|
||
|
||
pResTypePosEntry = CONTAINING_RECORD(pListEntry, RESTYPE_POSSIBLE_ENTRY,
|
||
PossibleLinkage);
|
||
|
||
if ( pResTypePosEntry->PossibleNode == pNode )
|
||
{
|
||
RELEASE_LOCK(gResTypeLock);
|
||
return(TRUE);
|
||
}
|
||
}
|
||
RELEASE_LOCK(gResTypeLock);
|
||
return(FALSE);
|
||
|
||
} // FmpInPossibleListForResType
|
||
|
||
DWORD
|
||
FmpValAddResourceDependency(
|
||
IN PFM_RESOURCE pResource,
|
||
IN PFM_RESOURCE pDependentResource
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Add a dependency from one resource to another.
|
||
|
||
Arguments:
|
||
|
||
Resource - The resource to add the dependent resource.
|
||
|
||
DependentResource - The dependent resource.
|
||
|
||
Returns:
|
||
|
||
ERROR_SUCCESS if successful.
|
||
|
||
A Win32 error code on failure.
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD dwStatus = ERROR_SUCCESS;
|
||
//
|
||
// If the resource or dependent resource have been marked for
|
||
// delete, then dont let a dependency be added.
|
||
//
|
||
if ((!IS_VALID_FM_RESOURCE(pResource)) ||
|
||
(!IS_VALID_FM_RESOURCE(pDependentResource)))
|
||
{
|
||
dwStatus = ERROR_RESOURCE_NOT_AVAILABLE;
|
||
goto FnExit;
|
||
}
|
||
|
||
if (pResource->QuorumResource)
|
||
{
|
||
dwStatus = ERROR_DEPENDENCY_NOT_ALLOWED;
|
||
goto FnExit;
|
||
}
|
||
//
|
||
// If the resources are not in the same group, fail the
|
||
// call. Also fail if some one tries to make a resource
|
||
// dependent upon itself.
|
||
//
|
||
if ((pResource->Group != pDependentResource->Group) ||
|
||
(pResource == pDependentResource))
|
||
{
|
||
dwStatus = ERROR_INVALID_PARAMETER;
|
||
goto FnExit;
|
||
}
|
||
|
||
// The resource to which the dependency is being added must be offline
|
||
// Otherwise, it looks like the dependency is in effect when the depending
|
||
// resource was not really brought online at the time the dependency
|
||
// existed
|
||
// must also be offline or failed.
|
||
// SS: For instance if a network name is dependent on two ip addresesses
|
||
// and
|
||
// is online and a third ip address resource dependency is added, the
|
||
// network name must be brought offline and online for the dependency
|
||
// to be truly in effect
|
||
//
|
||
if ((pResource->State != ClusterResourceOffline) &&
|
||
(pResource->State != ClusterResourceFailed))
|
||
{
|
||
dwStatus = ERROR_RESOURCE_ONLINE;
|
||
goto FnExit;
|
||
}
|
||
|
||
//
|
||
// Make sure that we don't have any circular dependencies!
|
||
//
|
||
if ( FmDependentResource( pDependentResource, pResource, FALSE ) )
|
||
{
|
||
dwStatus = ERROR_CIRCULAR_DEPENDENCY;
|
||
goto FnExit;
|
||
}
|
||
|
||
//
|
||
// Make sure that this dependency does not already exist!
|
||
//
|
||
if ( FmDependentResource(pResource, pDependentResource, TRUE))
|
||
{
|
||
dwStatus = ERROR_DEPENDENCY_ALREADY_EXISTS;
|
||
goto FnExit;
|
||
}
|
||
|
||
FnExit:
|
||
return(dwStatus);
|
||
|
||
} // FmpValAddResourceDependency
|
||
|
||
|
||
DWORD
|
||
FmpValRemoveResourceDependency(
|
||
IN PFM_RESOURCE pResource,
|
||
IN PFM_RESOURCE pDependentResource
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Validation routine for dependency removal.
|
||
|
||
Arguments:
|
||
|
||
pResource - The resource to remove the dependent resource.
|
||
|
||
pDependentResource - The dependent resource.
|
||
|
||
Returns:
|
||
|
||
ERROR_SUCCESS if the validation is successful.
|
||
|
||
A Win32 error code if the validation fails.
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD dwStatus = ERROR_SUCCESS;
|
||
|
||
//
|
||
// Chittur Subbaraman (chitturs) - 8/3/99
|
||
//
|
||
// This function checks whether it is legal to remove the dependency
|
||
// relationship between 2 resources. Note that this function only
|
||
// does a partial validation, the rest is done in the GUM handler.
|
||
//
|
||
|
||
//
|
||
// If the resource has been marked for delete, then dont
|
||
// let any dependency changes be made.
|
||
//
|
||
if ( !IS_VALID_FM_RESOURCE( pResource ) )
|
||
{
|
||
dwStatus = ERROR_RESOURCE_NOT_AVAILABLE;
|
||
goto FnExit;
|
||
}
|
||
|
||
if ( pResource->QuorumResource )
|
||
{
|
||
dwStatus = ERROR_DEPENDENCY_NOT_ALLOWED;
|
||
goto FnExit;
|
||
}
|
||
//
|
||
// If the resources are not in the same group, fail the
|
||
// call. Also fail if some one tries to make a resource
|
||
// dependent upon itself.
|
||
//
|
||
if ( ( pResource->Group != pDependentResource->Group ) ||
|
||
( pResource == pDependentResource ) )
|
||
{
|
||
dwStatus = ERROR_INVALID_PARAMETER;
|
||
goto FnExit;
|
||
}
|
||
|
||
//
|
||
// Ensure that both the resource and the dependent resource are in
|
||
// a stable state. This is necessary to prevent cases in which the
|
||
// user gets rid of a dependency link when one of the resources is in
|
||
// a pending state and later when the notification from resmon comes
|
||
// in and you try to stablize the rest of the waiting tree, the
|
||
// dependency link is already cut and so the rest of the tree is
|
||
// stuck in pending state for ever !
|
||
//
|
||
if ( ( pResource->State > ClusterResourcePending ) ||
|
||
( pDependentResource->State > ClusterResourcePending ) )
|
||
{
|
||
dwStatus = ERROR_INVALID_STATE;
|
||
goto FnExit;
|
||
}
|
||
|
||
FnExit:
|
||
return( dwStatus );
|
||
|
||
} // FmpValRemoveResourceDependency
|
||
|