579 lines
12 KiB
C
579 lines
12 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1996-1999 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
nodeapi.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
Public interfaces for managing cluster nodes.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
John Vert (jvert) 15-Jan-1996
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
#include "clusapip.h"
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Cluster Node Management Routines.
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
HNODE
|
|||
|
WINAPI
|
|||
|
OpenClusterNode(
|
|||
|
IN HCLUSTER hCluster,
|
|||
|
IN LPCWSTR lpszNodeName
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Opens an individual node in the cluster.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
hCluster - Supplies a cluster handle returned from OpenCluster.
|
|||
|
|
|||
|
lpszNodeName - Supplies the name of the individual node to open.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
non-NULL - returns an open handle to the specified cluster.
|
|||
|
|
|||
|
NULL - The operation failed. Extended error status is available
|
|||
|
using GetLastError()
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PCLUSTER Cluster;
|
|||
|
PCNODE Node;
|
|||
|
error_status_t Status = ERROR_SUCCESS;
|
|||
|
|
|||
|
Cluster = (PCLUSTER)hCluster;
|
|||
|
Node = LocalAlloc(LMEM_FIXED, sizeof(CNODE));
|
|||
|
if (Node == NULL) {
|
|||
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|||
|
return(NULL);
|
|||
|
}
|
|||
|
Node->Name = LocalAlloc(LMEM_FIXED, (lstrlenW(lpszNodeName)+1)*sizeof(WCHAR));
|
|||
|
if (Node->Name == NULL) {
|
|||
|
LocalFree(Node);
|
|||
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|||
|
return(NULL);
|
|||
|
}
|
|||
|
lstrcpyW(Node->Name, lpszNodeName);
|
|||
|
Node->Cluster = Cluster;
|
|||
|
InitializeListHead(&Node->NotifyList);
|
|||
|
WRAP_NULL(Node->hNode,
|
|||
|
(ApiOpenNode(Cluster->RpcBinding,
|
|||
|
lpszNodeName,
|
|||
|
&Status)),
|
|||
|
&Status,
|
|||
|
Cluster);
|
|||
|
if ((Node->hNode == NULL) || (Status != ERROR_SUCCESS)) {
|
|||
|
LocalFree(Node->Name);
|
|||
|
LocalFree(Node);
|
|||
|
SetLastError(Status);
|
|||
|
return(NULL);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Link newly opened Node onto the cluster structure.
|
|||
|
//
|
|||
|
EnterCriticalSection(&Cluster->Lock);
|
|||
|
InsertHeadList(&Cluster->NodeList, &Node->ListEntry);
|
|||
|
LeaveCriticalSection(&Cluster->Lock);
|
|||
|
|
|||
|
return((HNODE)Node);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
WINAPI
|
|||
|
CloseClusterNode(
|
|||
|
IN HNODE hNode
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Closes a handle to an individual cluster node
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
hNode - Supplies the cluster node to be closed
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE - The operation was successful.
|
|||
|
|
|||
|
FALSE - The operation failed. Extended error status is available
|
|||
|
using GetLastError()
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
PCNODE Node;
|
|||
|
PCLUSTER Cluster;
|
|||
|
|
|||
|
Node = (PCNODE)hNode;
|
|||
|
Cluster = (PCLUSTER)Node->Cluster;
|
|||
|
|
|||
|
//
|
|||
|
// Unlink node from cluster list.
|
|||
|
//
|
|||
|
EnterCriticalSection(&Cluster->Lock);
|
|||
|
RemoveEntryList(&Node->ListEntry);
|
|||
|
|
|||
|
//
|
|||
|
// Remove any notifications posted against this resource.
|
|||
|
//
|
|||
|
RundownNotifyEvents(&Node->NotifyList, Node->Name);
|
|||
|
|
|||
|
//if the cluster is dead and the reconnect has failed,
|
|||
|
//the Node->hNode might be NULL if s_apiopennode for
|
|||
|
//this node failed on a reconnect
|
|||
|
//the cluster may be dead and hNode may be non null, say
|
|||
|
//if reconnectnodes succeeded but the reconnectnetworks
|
|||
|
//failed
|
|||
|
// At reconnect, the old context is saved in the obsolete
|
|||
|
// list for deletion when the cluster handle is closed
|
|||
|
if ((Cluster->Flags & CLUS_DEAD) && (Node->hNode)) {
|
|||
|
RpcSmDestroyClientContext(&Node->hNode);
|
|||
|
LeaveCriticalSection(&Cluster->Lock);
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
LeaveCriticalSection(&Cluster->Lock);
|
|||
|
|
|||
|
//
|
|||
|
// Close RPC context handle
|
|||
|
//
|
|||
|
ApiCloseNode(&Node->hNode);
|
|||
|
|
|||
|
FnExit:
|
|||
|
//
|
|||
|
// Free memory allocations
|
|||
|
//
|
|||
|
LocalFree(Node->Name);
|
|||
|
LocalFree(Node);
|
|||
|
|
|||
|
//
|
|||
|
// Give the cluster a chance to clean up in case this
|
|||
|
// node was the only thing keeping it around.
|
|||
|
//
|
|||
|
CleanupCluster(Cluster);
|
|||
|
return(TRUE);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
#undef GetCurrentClusterNodeId
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
GetCurrentClusterNodeId(
|
|||
|
OUT LPWSTR lpszNodeId,
|
|||
|
IN OUT LPDWORD lpcchName
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Returns the node identifier of the current node. This function
|
|||
|
is only available on a node that is currently online and a member
|
|||
|
of a cluster.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
lpszNodeId - Points to a buffer that receives the unique ID of the object,
|
|||
|
including the terminating null character.
|
|||
|
|
|||
|
lpcchName - Points to a variable that specifies the size, in characters
|
|||
|
of the buffer pointed to by the lpszNodeId parameter. This size
|
|||
|
should include the terminating null character. When the function
|
|||
|
returns, the variable pointed to be lpcchName contains the number
|
|||
|
of characters stored in the buffer. The count returned does not
|
|||
|
include the terminating null character.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
If the function succeeds, the return value is ERROR_SUCCESS.
|
|||
|
|
|||
|
If the function fails, the return value is an error value.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
HCLUSTER Cluster;
|
|||
|
HNODE CurrentNode;
|
|||
|
DWORD Status;
|
|||
|
|
|||
|
Cluster = OpenCluster(NULL);
|
|||
|
if (Cluster == NULL) {
|
|||
|
return(GetLastError());
|
|||
|
}
|
|||
|
|
|||
|
CurrentNode = OpenClusterNode(Cluster,
|
|||
|
((PCLUSTER)Cluster)->NodeName);
|
|||
|
if (CurrentNode != NULL) {
|
|||
|
|
|||
|
Status = GetClusterNodeId(CurrentNode,
|
|||
|
lpszNodeId,
|
|||
|
lpcchName);
|
|||
|
CloseClusterNode(CurrentNode);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
Status = GetLastError();
|
|||
|
}
|
|||
|
CloseCluster(Cluster);
|
|||
|
return(Status);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
WINAPI
|
|||
|
GetClusterNodeId(
|
|||
|
IN HNODE hNode,
|
|||
|
OUT LPWSTR lpszNodeId,
|
|||
|
IN OUT LPDWORD lpcchName
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Returns the unique identifier of the specified node
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
hNode - Supplies the node whose unique ID is to be returned.
|
|||
|
|
|||
|
lpszNodeId - Points to a buffer that receives the unique ID of the object,
|
|||
|
including the terminating null character.
|
|||
|
|
|||
|
lpcchName - Points to a variable that specifies the size, in characters
|
|||
|
of the buffer pointed to by the lpszNodeId parameter. This size
|
|||
|
should include the terminating null character. When the function
|
|||
|
returns, the variable pointed to be lpcchName contains the number
|
|||
|
of characters stored in the buffer. The count returned does not
|
|||
|
include the terminating null character.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
If the function succeeds, the return value is ERROR_SUCCESS.
|
|||
|
|
|||
|
If the function fails, the return value is an error value.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
DWORD Status;
|
|||
|
DWORD Length;
|
|||
|
PCNODE Node = (PCNODE)hNode;
|
|||
|
LPWSTR Guid=NULL;
|
|||
|
|
|||
|
if (Node == NULL) {
|
|||
|
return(GetCurrentClusterNodeId(lpszNodeId, lpcchName));
|
|||
|
}
|
|||
|
|
|||
|
WRAP(Status,
|
|||
|
(ApiGetNodeId(Node->hNode,
|
|||
|
&Guid)),
|
|||
|
Node->Cluster);
|
|||
|
if (Status != ERROR_SUCCESS) {
|
|||
|
return(Status);
|
|||
|
}
|
|||
|
|
|||
|
MylstrcpynW(lpszNodeId, Guid, *lpcchName);
|
|||
|
Length = lstrlenW(Guid);
|
|||
|
if (Length >= *lpcchName) {
|
|||
|
if (lpszNodeId == NULL) {
|
|||
|
Status = ERROR_SUCCESS;
|
|||
|
} else {
|
|||
|
Status = ERROR_MORE_DATA;
|
|||
|
}
|
|||
|
}
|
|||
|
*lpcchName = Length;
|
|||
|
MIDL_user_free(Guid);
|
|||
|
return(Status);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
CLUSTER_NODE_STATE
|
|||
|
WINAPI
|
|||
|
GetClusterNodeState(
|
|||
|
IN HNODE hNode
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Returns the current state of a cluster node.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
hNode - Supplies the cluster node whose current state is to be returned
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
The current state of the cluster node. Currently defined node states
|
|||
|
include:
|
|||
|
|
|||
|
ClusterNodeUp
|
|||
|
ClusterNodeDown
|
|||
|
ClusterNodePaused
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
DWORD Status;
|
|||
|
CLUSTER_NODE_STATE State;
|
|||
|
PCNODE Node = (PCNODE)hNode;
|
|||
|
|
|||
|
WRAP(Status,
|
|||
|
(ApiGetNodeState(Node->hNode, (LPDWORD)&State)),
|
|||
|
Node->Cluster);
|
|||
|
if (Status == ERROR_SUCCESS) {
|
|||
|
return(State);
|
|||
|
} else {
|
|||
|
SetLastError(Status);
|
|||
|
return(ClusterNodeStateUnknown);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
WINAPI
|
|||
|
PauseClusterNode(
|
|||
|
IN HNODE hNode
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Requests that a node pauses its cluster activity.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
hNode - Supplies a handle to the node to leave its cluster.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
ERROR_SUCCESS if successful
|
|||
|
|
|||
|
Win32 error code otherwise
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
DWORD Status;
|
|||
|
PCNODE Node = (PCNODE)hNode;
|
|||
|
|
|||
|
WRAP(Status,
|
|||
|
(ApiPauseNode(Node->hNode)),
|
|||
|
Node->Cluster);
|
|||
|
return(Status);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
WINAPI
|
|||
|
ResumeClusterNode(
|
|||
|
IN HNODE hNode
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Requests that a node resume cluster activity, after it had been paused.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
hNode - Supplies a handle to the node to resume its cluster activity.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
ERROR_SUCCESS if successful
|
|||
|
|
|||
|
Win32 error code otherwise
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
DWORD Status;
|
|||
|
PCNODE Node = (PCNODE)hNode;
|
|||
|
|
|||
|
WRAP(Status,
|
|||
|
(ApiResumeNode(Node->hNode)),
|
|||
|
Node->Cluster);
|
|||
|
return(Status);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
WINAPI
|
|||
|
EvictClusterNode(
|
|||
|
IN HNODE hNode
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Evict the specified Node from the list of nodes in the permanent cluster
|
|||
|
database (registry).
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
hNode - Supplies a handle to the node to remove from the list of cluster
|
|||
|
nodes.
|
|||
|
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
ERROR_SUCCESS if successful
|
|||
|
|
|||
|
Win32 error code otherwise
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
DWORD Status;
|
|||
|
PCNODE Node = (PCNODE)hNode;
|
|||
|
|
|||
|
WRAP(Status,
|
|||
|
(ApiEvictNode(Node->hNode)),
|
|||
|
Node->Cluster);
|
|||
|
return(Status);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
WINAPI
|
|||
|
EvictClusterNodeEx(
|
|||
|
IN HNODE hNode,
|
|||
|
IN DWORD dwTimeout,
|
|||
|
OUT HRESULT* phrCleanupStatus
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Evict the specified node from the list of nodes in the permanent cluster
|
|||
|
database (registry) and initate the cleanup(unconfiguration) process on the
|
|||
|
cluster node. Note that if the node is down, the clean up process will not
|
|||
|
occur. However, when the node comes up, clustering will detect that the
|
|||
|
node was supposed to be evicted and it will unconfigure itself.
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
IN HNODE hNode - Supplies a handle to the node to remove from the list of cluster
|
|||
|
nodes.
|
|||
|
|
|||
|
IN DWORD dwTimeOut - Timeout in milliseconds for the cleanup(unconfiguration
|
|||
|
of clustering) to complete. If the cleanup doesnt complete
|
|||
|
in the given amount of time, the function will return.
|
|||
|
|
|||
|
OUT phrCleanupStatus - The status of cleanup is returned.
|
|||
|
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Returns the status of the eviction and not of cleanup.
|
|||
|
|
|||
|
ERROR_SUCCESS if successful
|
|||
|
|
|||
|
ERROR_CLUSTER_EVICT_WITHOUT_CLEANUP if eviction succeeded but cleanup returned
|
|||
|
an error. The phrCleanupStatus param will contain more information about
|
|||
|
the cleanup error. (Usually this will be RPC_S_CALL_FAILED.)
|
|||
|
|
|||
|
Win32 error code otherwise
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
DWORD Status;
|
|||
|
PCNODE Node = (PCNODE)hNode;
|
|||
|
HRESULT hr = E_ABORT;
|
|||
|
|
|||
|
WRAP(Status,
|
|||
|
(ApiEvictNode(Node->hNode)),
|
|||
|
Node->Cluster);
|
|||
|
|
|||
|
if (Status != ERROR_SUCCESS) {
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Any error after this is not "fatal". The node has been evicted
|
|||
|
// but the cleanup may fail for numerous reasons.
|
|||
|
//
|
|||
|
hr = ClRtlAsyncCleanupNode(Node->Name, 0, dwTimeout);
|
|||
|
if (FAILED(hr)) {
|
|||
|
Status = ERROR_CLUSTER_EVICT_WITHOUT_CLEANUP;
|
|||
|
}
|
|||
|
|
|||
|
FnExit:
|
|||
|
|
|||
|
if (phrCleanupStatus != NULL) {
|
|||
|
*phrCleanupStatus = hr;
|
|||
|
}
|
|||
|
|
|||
|
return(Status);
|
|||
|
|
|||
|
} // EvictClusterNodeEx()
|
|||
|
|
|||
|
|
|||
|
HCLUSTER
|
|||
|
WINAPI
|
|||
|
GetClusterFromNode(
|
|||
|
IN HNODE hNode
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Returns the cluster handle from the associated node handle.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
hNode - Supplies the node.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Handle to the cluster associated with the node handle.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
DWORD nStatus;
|
|||
|
PCNODE Node = (PCNODE)hNode;
|
|||
|
HCLUSTER hCluster = (HCLUSTER)Node->Cluster;
|
|||
|
|
|||
|
nStatus = AddRefToClusterHandle( hCluster );
|
|||
|
if ( nStatus != ERROR_SUCCESS ) {
|
|||
|
SetLastError( nStatus );
|
|||
|
hCluster = NULL;
|
|||
|
}
|
|||
|
return( hCluster );
|
|||
|
|
|||
|
} // GetClusterFromNode()
|