windows-nt/Source/XPSP1/NT/base/cluster/clusapi/group.c

863 lines
21 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
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; i<NodeCount; i++) {
Node = (PCNODE)NodeList[i];
//
// Make sure this isn't a handle to a node from a different
// cluster
//
if (Node->Cluster != 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; j<i; j++) {
if (lstrcmpiW(IdArray[j],IdArray[i]) == 0) {
//
// A duplicate node is in the list
//
Status = ERROR_INVALID_PARAMETER;
goto error_exit;
}
}
ListLength += (lstrlenW(IdArray[i])+1)*sizeof(WCHAR);
}
GroupKey = GetClusterGroupKey(hGroup, KEY_READ | KEY_WRITE);
if (GroupKey == NULL) {
Status = GetLastError();
goto error_exit;
}
//
// Allocate a buffer to hold the REG_MULTI_SZ
//
List = LocalAlloc(LMEM_FIXED, ListLength);
if (List == NULL) {
Status = ERROR_NOT_ENOUGH_MEMORY;
goto error_exit;
}
//
// Copy all the strings into the buffer.
//
p = List;
for (i=0; i<NodeCount; i++) {
lstrcpyW(p, IdArray[i]);
p += lstrlenW(IdArray[i])+1;
}
*p = L'\0'; // add the final NULL terminator to the MULTI_SZ
//
// Finally, tell the backend
//
WRAP(Status,
(ApiSetGroupNodeList(Group->hGroup, (UCHAR *)List, ListLength)),
Group->Cluster);
error_exit:
if (GroupKey != NULL) {
ClusterRegCloseKey(GroupKey);
}
if (List != NULL) {
LocalFree(List);
}
for (i=0; i<NodeCount; i++) {
if (IdArray[i] != NULL) {
MIDL_user_free(IdArray[i]);
}
}
LocalFree(IdArray);
return(Status);
}
DWORD
WINAPI
OnlineClusterGroup(
IN HGROUP hGroup,
IN OPTIONAL HNODE hDestinationNode
)
/*++
Routine Description:
Brings an offline group online.
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 failed over
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 available, the error value is
ERROR_HOST_NODE_NOT_AVAILABLE.
--*/
{
PCNODE Node;
PCGROUP Group;
DWORD Status;
Group = (PCGROUP)hGroup;
Node = (PCNODE)hDestinationNode;
if (Node != NULL) {
WRAP(Status,
(ApiMoveGroupToNode( Group->hGroup,
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()