/*++ Copyright (c) 1996-1999 Microsoft Corporation Module Name: fmval.c Abstract: Cluster manager api validation/support routines. Author: Sunita Shrivastava (sunitas) 29-April-1999. Revision History: --*/ #include "fmp.h" #define LOG_MODULE FMVAL //////////////////////////////////////////////////////// // // Validation routines for Group operations. // //////////////////////////////////////////////////////// DWORD FmpValOnlineGroup( IN PFM_GROUP Group ) /*++ Routine Description: Validation routine before group is brought online. Arguments: Group - Supplies a pointer to the group structure to bring online. Comments: Is called with the localgroup lock held Returns: ERROR_SUCCESS if the validation is successful. A Win32 error code if the validation fails. --*/ { DWORD dwStatus = ERROR_SUCCESS; PLIST_ENTRY listEntry; //if the group has been marked for delete, then fail this call if (!IS_VALID_FM_GROUP(Group)) { dwStatus = ERROR_GROUP_NOT_AVAILABLE; goto FnExit; } // // Make sure the owning node can run the group. // if ( !FmpInPreferredList( Group, Group->OwnerNode ) ) { dwStatus = ERROR_CLUSTER_OWNER_NOT_IN_PREFLIST; goto FnExit; } // // Make sure the owning node is not paused. // if (NmGetNodeState(Group->OwnerNode) == ClusterNodePaused) { dwStatus = ERROR_SHARING_PAUSED; goto FnExit; } FnExit: return(dwStatus); } // FmpValOnlineGroup DWORD FmpValMoveGroup( IN PFM_GROUP Group, IN PNM_NODE DestinationNode OPTIONAL ) /*++ Routine Description: Validation routine for group move. Arguments: Group - Supplies a pointer to the group structure to move. DestinationNode - Supplies the node object to move the group to. If not present, then move it to THE OTHER node. Returns: ERROR_SUCCESS if the validation is successful. A Win32 error code if the validation fails. --*/ { DWORD dwStatus = ERROR_SUCCESS; //if the group has been marked for delete, then fail this call if (!IS_VALID_FM_GROUP(Group)) { dwStatus = ERROR_GROUP_NOT_AVAILABLE; goto FnExit; } if ( FmpIsGroupPending(Group) ) { dwStatus = ERROR_GROUP_NOT_AVAILABLE; goto FnExit; } if ( Group->OwnerNode == NULL ) { dwStatus = ERROR_HOST_NODE_NOT_AVAILABLE; goto FnExit; } FnExit: return(dwStatus); } // FmpValMoveGroup //////////////////////////////////////////////////////// // // Validation routines for resource operations // //////////////////////////////////////////////////////// DWORD FmpValCreateResource( IN PFM_GROUP Group, IN LPWSTR ResourceId, IN LPCWSTR ResourceName, OUT PGUM_CREATE_RESOURCE *ppGumResource, OUT PDWORD pdwBufSize ) /*++ Routine Description: Validation routine for resource creation. Arguments: Group - Supplies the group in which this resource belongs. ResourceId - Supplies the Id of the resource to create. ResourceName - Supplies the 'user-friendly' name of the resource. ppGumResource - Message buffer to hold resource info. pdwBufSize - Message buffer size. Returns: ERROR_SUCCESS if the validation is successful. A Win32 error code if the validation fails. --*/ { DWORD dwStatus = ERROR_SUCCESS; PFM_RESOURCE Resource; LPCWSTR GroupId; PGUM_CREATE_RESOURCE GumResource; DWORD GroupIdLen; DWORD ResourceIdLen; DWORD ResourceNameLen; DWORD BufSize; HDMKEY ResourceKey; HDMKEY ParamsKey; DWORD Disposition; *ppGumResource = NULL; *pdwBufSize = 0; // // First create the parameters field. // ResourceKey = DmOpenKey( DmResourcesKey, ResourceId, MAXIMUM_ALLOWED ); if ( ResourceKey == NULL ) { ClRtlLogPrint(LOG_NOISE, "[FM] CreateResource: Failed to open registry key for %1!ws!, status = %2!u!.\n", ResourceId, GetLastError() ); dwStatus = GetLastError(); goto FnExit; } ParamsKey = DmCreateKey( ResourceKey, CLUSREG_KEYNAME_PARAMETERS, 0, KEY_READ | KEY_WRITE, NULL, &Disposition ); if ( ParamsKey != NULL ) { DmCloseKey( ParamsKey ); } DmCloseKey( ResourceKey ); // // Allocate a message buffer. // GroupId = OmObjectId(Group); GroupIdLen = (lstrlenW(GroupId)+1) * sizeof(WCHAR); ResourceIdLen = (lstrlenW(ResourceId)+1) * sizeof(WCHAR); ResourceNameLen = (lstrlenW(ResourceName)+1) * sizeof(WCHAR); BufSize = sizeof(GUM_CREATE_RESOURCE) - sizeof(WCHAR) + GroupIdLen + ResourceIdLen + ResourceNameLen; GumResource = LocalAlloc(LMEM_FIXED, BufSize); if (GumResource == NULL) { CsInconsistencyHalt( ERROR_NOT_ENOUGH_MEMORY ); dwStatus = ERROR_NOT_ENOUGH_MEMORY; goto FnExit; } // // Fill in message buffer. // GumResource->Resource = NULL; GumResource->GroupIdLen = GroupIdLen; GumResource->ResourceIdLen = ResourceIdLen; CopyMemory(GumResource->GroupId, GroupId, GroupIdLen); CopyMemory((PCHAR)GumResource->GroupId + GroupIdLen, ResourceId, ResourceIdLen); CopyMemory((PCHAR)GumResource->GroupId + GroupIdLen + ResourceIdLen, ResourceName, ResourceNameLen); *ppGumResource = GumResource; *pdwBufSize = BufSize; FnExit: return(dwStatus); } // FmpValCreateResource DWORD FmpValDeleteResource( IN PFM_RESOURCE pResource ) /*++ Routine Description: Validation routine for delete resource. Arguments: Resource - Supplies the resource to delete. Returns: ERROR_SUCCESS if the validation is successful. A Win32 error code if the validation fails. --*/ { DWORD dwStatus = ERROR_SUCCESS; // // Check if this is the quorum resource. // if ( pResource->QuorumResource ) { dwStatus = ERROR_QUORUM_RESOURCE; goto FnExit; } //other core resources cannot be deleted either if (pResource->ExFlags & CLUS_FLAG_CORE) { dwStatus = ERROR_CORE_RESOURCE; goto FnExit; } // // Check the state of the resource, before attempting to delete it. // It must be offline or failed in order to perform the delete. // if ((pResource->State != ClusterResourceOffline) && (pResource->State != ClusterResourceFailed)) { dwStatus = ERROR_RESOURCE_ONLINE; goto FnExit; } // // Check whether this resource provides for any other resources. // If so, it cannot be deleted. // if (!IsListEmpty(&pResource->ProvidesFor)) { dwStatus = ERROR_DEPENDENT_RESOURCE_EXISTS; goto FnExit; } if (pResource->Group->MovingList) { dwStatus = ERROR_INVALID_STATE; goto FnExit; } FnExit: return(dwStatus); } // FmpValDeleteResource DWORD FmpValOnlineResource( IN PFM_RESOURCE pResource ) /*++ Routine Description: This routine validates if a resource can be brought online. Arguments: Resource - A pointer to the resource to bring online. Returns: ERROR_SUCCESS if the validation is successful. A Win32 error code if the validation fails. --*/ { DWORD dwStatus = ERROR_SUCCESS; //if the resource has been marked for delete, then dont let //it be brought online if (!IS_VALID_FM_RESOURCE(pResource)) { dwStatus = ERROR_RESOURCE_NOT_AVAILABLE; goto FnExit; } // // Check if the resource has been initialized. If not, attempt // to initialize the resource now. // if ( pResource->Monitor == NULL ) { dwStatus = FmpInitializeResource( pResource, TRUE ); } FnExit: return(dwStatus); } // FmpValOnlineResource DWORD FmpValOfflineResource( IN PFM_RESOURCE pResource ) /*++ Routine Description: This routine validates if a given resource can be taken offline. Arguments: Resource - A pointer to the resource to take offline. Returns: ERROR_SUCCESS if the validation is successful. A Win32 error code if the validation fails. --*/ { DWORD dwStatus = ERROR_SUCCESS; //if the resource has been marked for delete, then fail this call if (!IS_VALID_FM_RESOURCE(pResource)) { dwStatus = ERROR_RESOURCE_NOT_AVAILABLE; goto FnExit; } // // Check if this is the quorum resource. // if ( pResource->QuorumResource ) { dwStatus = ERROR_QUORUM_RESOURCE; goto FnExit; } // // Check if the resource has been initialized. If not, return // success because the resource is not online. // if ( pResource->Monitor == NULL ) { dwStatus = ERROR_SUCCESS; goto FnExit; } // // Chittur Subbaraman (chitturs) - 4/8/99 // // Don't attempt to do anything if the resource has failed. You could // get into some funny cases in which the resource switches between // offline pending and failed states for ever. // if ( pResource->State == ClusterResourceFailed ) { dwStatus = ERROR_INVALID_STATE; goto FnExit; } FnExit: return(dwStatus); } // FmpValOfflineResource DWORD FmpValAddResourceDependency( IN PFM_RESOURCE pResource, IN PFM_RESOURCE pDependentResource ) /*++ Routine Description: Validation routine for dependency addition. Arguments: Resource - The resource to add the dependent resource. DependentResource - The dependent resource. Returns: ERROR_SUCCESS if the validation is successful. A Win32 error code if the validation fails. --*/ { DWORD dwStatus = ERROR_SUCCESS; //if the resource has been marked for delete, then dont let //it be brought online 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; } // 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 FmpValChangeResourceNode( IN PFM_RESOURCE pResource, IN LPCWSTR pszNodeId, IN BOOL bAdd, OUT PGUM_CHANGE_POSSIBLE_NODE *ppGumChange, OUT PDWORD pdwBufSize ) /*++ Routine Description: Validation routine for changing the possible owner node of a resource. Arguments: pResource - A pointer to the resource structure. pszNodeId - A pointer to the node id bAdd - Indicates add or remove ppGumChange - Message buffer to hold the resource info pdwBufSize - Size of the message buffer Comments: Lock must be held when this routine is called Returns: ERROR_SUCCESS if the validation is successful. A Win32 error code if the validation fails. --*/ { DWORD dwStatus = ERROR_SUCCESS; PLIST_ENTRY pListEntry; PRESTYPE_POSSIBLE_ENTRY pResTypePosEntry = NULL; BOOL bNodeSupportsResType = FALSE; LPCWSTR pszResourceId; DWORD dwResourceLen; DWORD dwNodeLen; DWORD dwBufSize; PGUM_CHANGE_POSSIBLE_NODE pGumChange; *ppGumChange = NULL; *pdwBufSize = 0; //if the resource has been marked for delete, then perform //any operations on it if (!IS_VALID_FM_RESOURCE(pResource)) { dwStatus = ERROR_RESOURCE_NOT_AVAILABLE; goto FnExit; } if ( pResource->QuorumResource ) { dwStatus = 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 ( !bAdd && (pszNodeId == OmObjectId(NmLocalNode)) && (((pResource->State != ClusterResourceOffline) && (pResource->State != ClusterResourceFailed)) || (FmpGetGroupState( pResource->Group, TRUE ) != ClusterGroupOffline)) ) { dwStatus = ERROR_INVALID_STATE; goto FnExit; } //make sure the node is on the list of possible nodes for this // resource type if (bAdd) { pListEntry = &(pResource->Type->PossibleNodeList); for (pListEntry = pListEntry->Flink; pListEntry != &(pResource->Type->PossibleNodeList); pListEntry = pListEntry->Flink) { pResTypePosEntry = CONTAINING_RECORD(pListEntry, RESTYPE_POSSIBLE_ENTRY, PossibleLinkage); if (!lstrcmpW(OmObjectId(pResTypePosEntry->PossibleNode), pszNodeId)) { bNodeSupportsResType = TRUE; break; } } if (!bNodeSupportsResType) { dwStatus = ERROR_CLUSTER_RESTYPE_NOT_SUPPORTED; goto FnExit; } } pszResourceId = OmObjectId(pResource); dwResourceLen = (lstrlenW(pszResourceId)+1)*sizeof(WCHAR); dwNodeLen = (lstrlenW(pszNodeId)+1)*sizeof(WCHAR); dwBufSize = sizeof(GUM_CHANGE_POSSIBLE_NODE) - sizeof(WCHAR) + dwResourceLen + dwNodeLen; pGumChange = LocalAlloc(LMEM_FIXED, dwBufSize); if (pGumChange == NULL) { CsInconsistencyHalt( ERROR_NOT_ENOUGH_MEMORY ); return(ERROR_NOT_ENOUGH_MEMORY); } pGumChange->ResourceIdLen = dwResourceLen; CopyMemory(pGumChange->ResourceId, pszResourceId, dwResourceLen); CopyMemory((PCHAR)pGumChange->ResourceId + dwResourceLen, pszNodeId, dwNodeLen); *ppGumChange = pGumChange; *pdwBufSize = dwBufSize; FnExit: return(dwStatus); } // FmpValChangeResourceNode DWORD FmpValChangeResourceGroup( IN PFM_RESOURCE pResource, IN PFM_GROUP pNewGroup, OUT PGUM_CHANGE_GROUP *ppGumChange, OUT LPDWORD pdwBufSize) /*++ Routine Description: Validation routine for changing a resource's group. Arguments: pResource - Pointer to the resource structure pNewGroup - Pointer to the group to which the resource is moved to ppGumChange - Message buffer to hold the resource info pdwBufSize - Size of the message buffer Comments: Lock must be held when this routine is called Returns: ERROR_SUCCESS if validation is successful. A Win32 error code otherwise. --*/ { DWORD dwBufSize; LPCWSTR pszResourceId; DWORD dwResourceLen; LPCWSTR pszGroupId; DWORD dwGroupLen; DWORD dwStatus = ERROR_SUCCESS; PGUM_CHANGE_GROUP pGumChange; *pdwBufSize = 0; *ppGumChange = NULL; // 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 //if the resource has been marked for delete, then fail this call if (!IS_VALID_FM_RESOURCE(pResource)) { dwStatus = ERROR_RESOURCE_NOT_AVAILABLE; goto FnExit; } // // 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); *ppGumChange = pGumChange; *pdwBufSize = dwBufSize; FnExit: return(dwStatus); } // FmpValChangeResourceGroup