/*++ Copyright (c) 1996-1999 Microsoft Corporation Module Name: group.c Abstract: Provides interface for managing cluster groups Author: John Vert (jvert) 30-Jan-1996 Revision History: --*/ #include "clusapip.h" HGROUP WINAPI CreateClusterGroup( IN HCLUSTER hCluster, IN LPCWSTR lpszGroupName ) /*++ Routine Description: Creates a new cluster group. Arguments: hCluster - Supplies a handle to a previously opened cluster. lpszGroupName - Supplies the name of the group. If the specified group already exists, it is opened. Return Value: non-NULL - returns an open handle to the specified group. NULL - The operation failed. Extended error status is available using GetLastError() --*/ { PCLUSTER Cluster; PCGROUP Group; error_status_t Status = ERROR_SUCCESS; Cluster = (PCLUSTER)hCluster; Group = LocalAlloc(LMEM_FIXED, sizeof(CGROUP)); if (Group == NULL) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return(NULL); } Group->Name = LocalAlloc(LMEM_FIXED, (lstrlenW(lpszGroupName)+1)*sizeof(WCHAR)); if (Group->Name == NULL) { LocalFree(Group); SetLastError(ERROR_NOT_ENOUGH_MEMORY); return(NULL); } lstrcpyW(Group->Name, lpszGroupName); Group->Cluster = Cluster; InitializeListHead(&Group->NotifyList); WRAP_NULL(Group->hGroup, (ApiCreateGroup(Cluster->RpcBinding, lpszGroupName, &Status)), &Status, Cluster); if ((Group->hGroup == NULL) || (Status != ERROR_SUCCESS)) { LocalFree(Group->Name); LocalFree(Group); SetLastError(Status); return(NULL); } // // Link newly opened group onto the cluster structure. // EnterCriticalSection(&Cluster->Lock); InsertHeadList(&Cluster->GroupList, &Group->ListEntry); LeaveCriticalSection(&Cluster->Lock); return ((HGROUP)Group); } HGROUP WINAPI OpenClusterGroup( IN HCLUSTER hCluster, IN LPCWSTR lpszGroupName ) /*++ Routine Description: Opens a handle to the specified group Arguments: hCluster - Supplies a handle to the cluster lpszGroupName - Supplies the name of the group to be opened Return Value: non-NULL - returns an open handle to the specified group. NULL - The operation failed. Extended error status is available using GetLastError() --*/ { PCLUSTER Cluster; PCGROUP Group; error_status_t Status = ERROR_SUCCESS; Cluster = (PCLUSTER)hCluster; Group = LocalAlloc(LMEM_FIXED, sizeof(CGROUP)); if (Group == NULL) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return(NULL); } Group->Name = LocalAlloc(LMEM_FIXED, (lstrlenW(lpszGroupName)+1)*sizeof(WCHAR)); if (Group->Name == NULL) { LocalFree(Group); SetLastError(ERROR_NOT_ENOUGH_MEMORY); return(NULL); } lstrcpyW(Group->Name, lpszGroupName); Group->Cluster = Cluster; InitializeListHead(&Group->NotifyList); WRAP_NULL(Group->hGroup, (ApiOpenGroup(Cluster->RpcBinding, lpszGroupName, &Status)), &Status, Cluster); if ((Group->hGroup == NULL) || (Status != ERROR_SUCCESS)) { LocalFree(Group->Name); LocalFree(Group); SetLastError(Status); return(NULL); } // // Link newly opened group onto the cluster structure. // EnterCriticalSection(&Cluster->Lock); InsertHeadList(&Cluster->GroupList, &Group->ListEntry); LeaveCriticalSection(&Cluster->Lock); return ((HGROUP)Group); } BOOL WINAPI CloseClusterGroup( IN HGROUP hGroup ) /*++ Routine Description: Closes a group handle returned from OpenClusterGroup Arguments: hGroup - Supplies the group handle Return Value: TRUE - The operation was successful. FALSE - The operation failed. Extended error status is available using GetLastError() --*/ { PCGROUP Group; PCLUSTER Cluster; Group = (PCGROUP)hGroup; Cluster = (PCLUSTER)Group->Cluster; // // Unlink group from cluster list. // EnterCriticalSection(&Cluster->Lock); RemoveEntryList(&Group->ListEntry); // // Remove any notifications posted against this group. // RundownNotifyEvents(&Group->NotifyList, Group->Name); //if the cluster is dead and the reconnect has failed, //the group->hgroup might be NULL if s_apiopengroup for //this group failed on a reconnect //the cluster may be dead and hgroup may be non null, say //if reconnectgroups succeeded but the reconnect networks //failed //At reconnect, the old context is saved in the obsolete //list for deletion when the cluster handle is closed or //when the next api call is made if ((Cluster->Flags & CLUS_DEAD) && (Group->hGroup)) { RpcSmDestroyClientContext(&Group->hGroup); LeaveCriticalSection(&Cluster->Lock); goto FnExit; } LeaveCriticalSection(&Cluster->Lock); //SS :: If this fails, should we delete the client side context //there is a potential leak here since this client side context //will never get cleaned up since this context is not on the //obsolete list and the error here is simply igonored // // Close RPC context handle // If the server dies, we still clean up the client side // and rely on the rundown mechanism to clean up server side state // ApiCloseGroup(&Group->hGroup); FnExit: // // Free memory allocations // LocalFree(Group->Name); LocalFree(Group); // // Give the cluster a chance to clean up in case this // group was the only thing keeping it around. // CleanupCluster(Cluster); return(TRUE); } CLUSTER_GROUP_STATE WINAPI GetClusterGroupState( IN HGROUP hGroup, OUT LPWSTR lpszNodeName, IN OUT LPDWORD lpcchNodeName ) /*++ Routine Description: Returns the group's current state and the node where it is currently online. Arguments: hGroup - Supplies a handle to a cluster group lpszNodeName - Returns the name of the node in the cluster where the given group is currently online lpcchNodeName - Supplies a pointer to a DWORD containing the number of characters available in the lpszNodeName buffer Returns the number of characters (not including the terminating NULL character) written to the lpszNodeName buffer Return Value: Returns the current state of the group. Possible states are ClusterGroupOnline ClusterGroupOffline ClusterGroupFailed ClusterGroupPartialOnline ClusterGroupPending If the function fails, the return value is -1. Extended error status is available using GetLastError() --*/ { PCGROUP Group; LPWSTR NodeName=NULL; CLUSTER_GROUP_STATE State; DWORD Status; DWORD Length; Group = (PCGROUP)hGroup; WRAP(Status, (ApiGetGroupState( Group->hGroup, (LPDWORD)&State, // cast for win64 warning &NodeName )), Group->Cluster); if (Status == ERROR_SUCCESS) { if (ARGUMENT_PRESENT(lpszNodeName)) { MylstrcpynW(lpszNodeName, NodeName, *lpcchNodeName); Length = lstrlenW(NodeName); if (Length >= *lpcchNodeName) { Status = ERROR_MORE_DATA; State = ClusterGroupStateUnknown; // -1 } *lpcchNodeName = Length; } MIDL_user_free(NodeName); } else { State = ClusterGroupStateUnknown; } SetLastError(Status); return (State); } DWORD WINAPI SetClusterGroupName( IN HGROUP hGroup, IN LPCWSTR lpszGroupName ) /*++ Routine Description: Sets the friendly name of a cluster group Arguments: hGroup - Supplies a handle to a cluster group lpszGroupName - Supplies the new name of the cluster group Return Value: ERROR_SUCCESS if successful Win32 error code otherwise --*/ { PCGROUP Group; DWORD Status; Group = (PCGROUP)hGroup; WRAP(Status, (ApiSetGroupName(Group->hGroup, lpszGroupName)), Group->Cluster); return(Status); } DWORD WINAPI SetClusterGroupNodeList( IN HGROUP hGroup, IN DWORD NodeCount, IN HNODE NodeList[] ) /*++ Routine Description: Sets the preferred node list of the specified cluster group Arguments: hGroup - Supplies the group whose preferred node list is to be set. NodeCount - Supplies the number of nodes in the preferred node list. NodeList - Supplies a pointer to an array of node handles. The number of nodes in the array is specified by the NodeCount parameter. The nodes in the array should be ordered by their preference. The first node in the array is the most preferred node. Return Value: ERROR_SUCCESS if successful Win32 error code otherwise --*/ { PCGROUP Group = (PCGROUP)hGroup; DWORD i,j; LPWSTR *IdArray; DWORD Status; DWORD ListLength = sizeof(WCHAR); HKEY GroupKey = NULL; LPWSTR List = NULL; LPWSTR p; DWORD Length; PCNODE Node; // // First, iterate through all the nodes and obtain their IDs. // IdArray = LocalAlloc(LMEM_ZEROINIT, NodeCount*sizeof(LPWSTR)); if (IdArray == NULL) { return( ERROR_NOT_ENOUGH_MEMORY ); } for (i=0; iCluster != Group->Cluster) { Status = ERROR_INVALID_PARAMETER; goto error_exit; } WRAP(Status, (ApiGetNodeId(Node->hNode, &IdArray[i])), Group->Cluster); if (Status != ERROR_SUCCESS) { goto error_exit; } // // Make sure there are no duplicates // for (j=0; jhGroup, (UCHAR *)List, ListLength)), Group->Cluster); error_exit: if (GroupKey != NULL) { ClusterRegCloseKey(GroupKey); } if (List != NULL) { LocalFree(List); } for (i=0; ihGroup, Node->hNode)), Group->Cluster); if (Status != ERROR_SUCCESS) { return(Status); } } WRAP(Status, (ApiOnlineGroup( Group->hGroup )), Group->Cluster); return(Status); } DWORD WINAPI MoveClusterGroup( IN HGROUP hGroup, IN OPTIONAL HNODE hDestinationNode ) /*++ Routine Description: Moves an entire group from one node to another. If hDestinationNode is specified, but the group is not capable of being brought online there, this API fails. If NULL is specified as the hDestinationNode, the best possible node is chosen by the cluster software. If NULL is specified but no node where this group can be brought online is currently available, this API fails. Arguments: hGroup - Supplies a handle to the group to be moved hDestinationNode - If present, supplies the node where this group should be brought back online. Return Value: If the function succeeds, the return value is ERROR_SUCCESS. If the function fails, the return value is an error value. If a suitable host node is not availabe, the error value is ERROR_HOST_NODE_NOT_AVAILABLE. --*/ { PCGROUP Group; PCNODE Node; DWORD Status; DWORD MoveStatus; DWORD Generation; BOOL bReconnected = FALSE; Group = (PCGROUP)hGroup; Node = (PCNODE)hDestinationNode; // // This API is not as simple as it should be because it is not idempotent. // In the case where hDestinationNode == NULL, we don't know where the group // will end up. And it will move each time we call it. So the normal mechanism // of failing over the API will not work in the case where the group to be // moved contains the cluster name we are connected to. The RPC call to move // the group will "fail" because the connection is dropped, but the call really // succeeded. So we will reconnect, retry, and fail again as the group moves again. // // So the approach taken here if hDestinationNode is not specified is to find out // where the group is currently, then move the group (somewhere else). If // ApiMoveGroup fails, and ReconnectCluster succeeds, then find out where the // group is again. If it is different, return success. If it is the same, try again. // if (hDestinationNode != NULL) { // // Chittur Subbaraman (chitturs) - 10/13/99 // // If ApiMoveGroupToNode returns ERROR_INVALID_STATE due to the // reissue of the move upon a reconnect, then tell the caller // that the move is pending. // Generation = Group->Cluster->Generation; WRAP(Status, (ApiMoveGroupToNode( Group->hGroup, Node->hNode)), Group->Cluster); if ((Status == ERROR_INVALID_STATE) && (Generation < Group->Cluster->Generation)) { Status = ERROR_IO_PENDING; } } else { LPWSTR OldNodeName = NULL; CLUSTER_GROUP_STATE State; WRAP(Status, (ApiGetGroupState( Group->hGroup, (LPDWORD)&State, // cast for win64 warning &OldNodeName)), Group->Cluster); if (Status != ERROR_SUCCESS) { return(Status); } // // Chittur Subbaraman (chitturs) - 5/5/99 // // Added logic to call ApiMoveGroup until it is successful or // until all possible candidates have been tried. // do { Status = MoveStatus = ApiMoveGroup(Group->hGroup); // // Get out if the move is successful // if ((Status == ERROR_IO_PENDING) || (Status == ERROR_SUCCESS)) { break; } // // Chittur Subbaraman (chitturs) - 7/8/99 // // If the group is not quiet and you have reconnected, then // just tell the client that the group state is pending. // This case happens if the node to which the client is // connected to crashes and this function reissues the move // "blindly" on a reconnect. In such a case, the group could // be in pending state and there is no point in returning an // error status. Note however that the group ownership may // not change in such a case and then the client has to figure // this out and reissue the move. // if ((Status == ERROR_INVALID_STATE) && (bReconnected)) { Status = ERROR_IO_PENDING; break; } Generation = Group->Cluster->Generation; // // The above move attempt may have failed. So, try reconnecting. // Status = ReconnectCluster(Group->Cluster, Status, Generation); if (Status == ERROR_SUCCESS) { LPWSTR NewNodeName = NULL; // // Successfully reconnected, see where the group is now. // WRAP(Status, (ApiGetGroupState(Group->hGroup, (LPDWORD)&State, // cast for win64 warn &NewNodeName)), Group->Cluster); if (Status == ERROR_SUCCESS) { if (lstrcmpiW(NewNodeName, OldNodeName) != 0) { // // The group has already moved. Return ERROR_SUCCESS. // MIDL_user_free(NewNodeName); break; } bReconnected = TRUE; MIDL_user_free(NewNodeName); } else { // // Return status of the failed move operation. // Status = MoveStatus; break; } } else { // // Return status of the failed move operation. // Status = MoveStatus; break; } } while ( TRUE ); MIDL_user_free(OldNodeName); } return(Status); } DWORD WINAPI OfflineClusterGroup( IN HGROUP hGroup ) /*++ Routine Description: Brings an online group offline Arguments: hGroup - Supplies a handle to the group to be taken offline Return Value: If the function succeeds, the return value is ERROR_SUCCESS. If the function fails, the return value is an error value. --*/ { PCGROUP Group; DWORD Status; Group = (PCGROUP)hGroup; WRAP(Status, (ApiOfflineGroup( Group->hGroup )), Group->Cluster); return(Status); } DWORD WINAPI DeleteClusterGroup( IN HGROUP hGroup ) /*++ Routine Description: Deletes the specified cluster group from the cluster. The cluster group must contain no resources. Arguments: hGroup - Specifies the cluster group to be deleted. Return Value: If the function succeeds, the return value is ERROR_SUCCESS. If the function fails, the return value is an error value. --*/ { PCGROUP Group; DWORD Status; Group = (PCGROUP)hGroup; WRAP(Status, (ApiDeleteGroup( Group->hGroup )), Group->Cluster); return(Status); } HCLUSTER WINAPI GetClusterFromGroup( IN HGROUP hGroup ) /*++ Routine Description: Returns the cluster handle from the associated group handle. Arguments: hGroup - Supplies the group. Return Value: Handle to the cluster associated with the group handle. --*/ { DWORD nStatus; PCGROUP Group = (PCGROUP)hGroup; HCLUSTER hCluster = (HCLUSTER)Group->Cluster; nStatus = AddRefToClusterHandle( hCluster ); if ( nStatus != ERROR_SUCCESS ) { SetLastError( nStatus ); hCluster = NULL; } return( hCluster ); } // GetClusterFromGroup()