/*++ Copyright (c) 1996 Microsoft Corporation Module Name: reconnect.c Abstract: Implements the support to enable the cluster API to transparently reconnect to a cluster when the node that the connection was made to fails. This module contains wrappers for all the cluster RPC interfaces defined in api_rpc.idl. These wrappers filter out communication errors and attempt to reconnect to the cluster when a communication error occurs. This allows the caller to be completely ignorant of any node failures. Author: John Vert (jvert) 9/24/1996 Revision History: --*/ #include "clusapip.h" // // Local function prototypes // DWORD ReconnectKeys( IN PCLUSTER Cluster ); DWORD ReopenKeyWorker( IN PCKEY Key ); DWORD ReconnectResources( IN PCLUSTER Cluster ); DWORD ReconnectGroups( IN PCLUSTER Cluster ); DWORD ReconnectNodes( IN PCLUSTER Cluster ); DWORD ReconnectNetworks( IN PCLUSTER Cluster ); DWORD ReconnectNetInterfaces( IN PCLUSTER Cluster ); DWORD ReconnectNotifySessions( IN PCLUSTER Cluster ); DWORD ReconnectCandidate( IN PCLUSTER Cluster, IN DWORD dwIndex, OUT PBOOL pIsContinue ); DWORD ReconnectCluster( IN PCLUSTER Cluster, IN DWORD Error, IN DWORD Generation ) /*++ Routine Description: Attempts to reconnect to the specified cluster. The supplied error code is checked against RPC errors that indicate the server on the other end is unavailable. If it matches, a reconnect is attempted. Arguments: Cluster - Supplies the cluster. Error - Supplies the error returned from RPC. Generation - Supplies the cluster connection generation that was in effect when the error occurred. Return Value: ERROR_SUCCESS if the reconnect was successful and the RPC should be retried Win32 error code otherwise. --*/ { // // filter out all RPC errors that might indicate the connection // has dropped. // switch (Error) { case RPC_S_CALL_FAILED: case ERROR_INVALID_HANDLE: case RPC_S_INVALID_BINDING: case RPC_S_SERVER_UNAVAILABLE: case RPC_S_SERVER_TOO_BUSY: case RPC_S_UNKNOWN_IF: case RPC_S_CALL_FAILED_DNE: case RPC_X_SS_IN_NULL_CONTEXT: case ERROR_CLUSTER_NODE_SHUTTING_DOWN: case EPT_S_NOT_REGISTERED: case ERROR_CLUSTER_NODE_NOT_READY: case RPC_S_UNKNOWN_AUTHN_SERVICE: TIME_PRINT(("Reconnect Cluster - reconnecting on Error %d\n",Error)); break; default: // // Anything else we don't know how to deal with, so return // the error directly. // return(Error); } // // Attempt to reconnect the cluster. // if ((Cluster->Flags & CLUS_DEAD) || (Cluster->Flags & CLUS_LOCALCONNECT)) { // // Don't bother trying to reconnect. Either we've already // declared the cluster dead, or the connection was over // LPC (to the local machine) and we do not necessarily want // to try to reconnect. // if (Cluster->Flags & CLUS_LOCALCONNECT) Cluster->Flags |= CLUS_DEAD; TIME_PRINT(("ReconnectCluster - Cluster dead or local, giving up - error %d\n",Error)); return(Error); } if (Generation < Cluster->Generation) { // // We have already successfully reconnected since the error occurred, // so retry immediately. // TIME_PRINT(("ReconnectCluster - Generation %d < Current %d, retrying\n", Generation, Cluster->Generation)); return(ERROR_SUCCESS); } EnterCriticalSection(&Cluster->Lock); // // Check again for cluster death, in case the previous owner // of the lock declared the cluster dead. // if (Cluster->Flags & CLUS_DEAD) { TIME_PRINT(("ReconnectCluster - Cluster dead or local, giving up - error %d\n",Error)); LeaveCriticalSection(&Cluster->Lock); return(Error); } if (Generation < Cluster->Generation) { // // We have already reconnected since the error occurred, // so retry immediately. // Error = ERROR_SUCCESS; TIME_PRINT(("ReconnectCluster - Generation %d < Current %d, retrying\n", Generation, Cluster->Generation)); } else { DWORD i, CurrentConnectionIndex = -1; BOOL IsContinue = TRUE; for (i=0; iReconnectCount; i++) { if (Cluster->Reconnect[i].IsCurrent) { // // This is something we've already connected to and // it's obviously gone, so skip this node. // TIME_PRINT(("ReconnectCluster - skipping current %ws\n", Cluster->Reconnect[i].Name)); CurrentConnectionIndex = i; continue; } if (!Cluster->Reconnect[i].IsUp) { // // skip this candidate, it is not up. // // BUGBUG John Vert (jvert) 11/14/1996 // We could do another pass through the list if all // the nodes that we think are up fail. // TIME_PRINT(("ReconnectCluster - skipping down node %ws\n", Cluster->Reconnect[i].Name)); continue; } // // Chittur Subbaraman (chitturs) - 08/29/1998 // // Try to reconnect to the cluster using a candidate // Error = ReconnectCandidate ( Cluster, i, &IsContinue ); if (Error == ERROR_SUCCESS) { // // Chittur Subbaraman (chitturs) - 08/29/1998 // // Break out of the loop and return if you // succeed in reconnecting // break; } if (IsContinue == FALSE) { // // Chittur Subbaraman (chitturs) - 08/29/1998 // // Exit immediately if you encounter an error // that will not let you proceed any further // TIME_PRINT(("ReconnectCluster unable to continue - Exiting with code %d\n", Error)); goto error_exit; } } if (Error != ERROR_SUCCESS) { // // Chittur Subbaraman (chitturs) - 08/29/98 // // Try reconnecting with the current candidate (which // you skipped before), if the CurrentConnectionIndex // is valid and the party is up. This is required // in the case of a 1 node cluster in which the // client takes the cluster group offline. In this // case, the current candidate (i.e., the node) is // valid and the client should be able to retry and // reconnect to the node. // if ((CurrentConnectionIndex != -1) && (Cluster->Reconnect[CurrentConnectionIndex].IsUp)) { Error = ReconnectCandidate (Cluster, CurrentConnectionIndex, &IsContinue); if ((Error != ERROR_SUCCESS) && (IsContinue == FALSE)) { // // Chittur Subbaraman (chitturs) - 08/29/1998 // // Exit immediately if you encounter an error // that will not let you proceed any further // TIME_PRINT(("ReconnectCluster - unable to continue for current party %ws - Exiting with code %d\n", Cluster->Reconnect[CurrentConnectionIndex].Name, Error)); goto error_exit; } } else { TIME_PRINT(("ReconnectCluster - unable to retry for current party %ws - Error %d\n", Cluster->Reconnect[CurrentConnectionIndex].Name, Error)); } if (Error != ERROR_SUCCESS) { TIME_PRINT(("ReconnectCluster - all reconnects failed, giving up - error %d\n", Error)); Cluster->Flags |= CLUS_DEAD; } } } error_exit: LeaveCriticalSection(&Cluster->Lock); return(Error); } DWORD ReconnectKeys( IN PCLUSTER Cluster ) /*++ Routine Description: Reopens all cluster registry keys after a reconnect Arguments: Cluster - Supplies the cluster to be reconnected. Return Value: ERROR_SUCCESS if successful Win32 error code otherwise --*/ { PLIST_ENTRY ListEntry; PCKEY Key; DWORD Status; ListEntry = Cluster->KeyList.Flink; while (ListEntry != &Cluster->KeyList) { // // Each key in the cluster's list represents the // root of a registry tree. // Key = CONTAINING_RECORD(ListEntry, CKEY, ParentList); ListEntry = ListEntry->Flink; Status = ReopenKeyWorker(Key); if (Status != ERROR_SUCCESS) { return(Status); } } return(ERROR_SUCCESS); } DWORD ReopenKeyWorker( IN PCKEY Key ) /*++ Routine Description: Recursive worker routine for opening a key and all its children. Arguments: Key - Supplies the root key to reopen. Return Value: ERROR_SUCCESS if successful. Win32 error code otherwise --*/ { PLIST_ENTRY ListEntry; PCKEY Child; DWORD Status = ERROR_GEN_FAILURE; BOOL CloseAfterOpen; if (Key->RemoteKey != NULL) { // // Destroy the old context // Status = MyRpcSmDestroyClientContext(Key->Cluster, &Key->RemoteKey); if (Status != ERROR_SUCCESS) { TIME_PRINT(("ReopenKeyWorker - RpcSmDestroyClientContext failed Error %d\n",Status)); } CloseAfterOpen = FALSE; } else { CloseAfterOpen = TRUE; } // // Next, reopen this key. // if (Key->Parent == NULL) { Key->RemoteKey = ApiGetRootKey(Key->Cluster->RpcBinding, Key->SamDesired, &Status); } else { Key->RemoteKey = ApiOpenKey(Key->Parent->RemoteKey, Key->RelativeName, Key->SamDesired, &Status); } if (Key->RemoteKey == NULL) { return(Status); } // // Now open all this keys children recursively. // ListEntry = Key->ChildList.Flink; while (ListEntry != &Key->ChildList) { Child = CONTAINING_RECORD(ListEntry, CKEY, ParentList); ListEntry = ListEntry->Flink; Status = ReopenKeyWorker(Child); if (Status != ERROR_SUCCESS) { return(Status); } } // // If the key had been closed and was just kept around to do the reopens, close it // now as the reopens are done. // if (CloseAfterOpen) { ApiCloseKey(&Key->RemoteKey); } return(ERROR_SUCCESS); } DWORD ReconnectResources( IN PCLUSTER Cluster ) /*++ Routine Description: Reopens all cluster resources after a reconnect Arguments: Cluster - Supplies the cluster to be reconnected. Return Value: ERROR_SUCCESS if successful Win32 error code otherwise --*/ { PLIST_ENTRY ListEntry; PCRESOURCE Resource; DWORD Status; ListEntry = Cluster->ResourceList.Flink; while (ListEntry != &Cluster->ResourceList) { Resource = CONTAINING_RECORD(ListEntry, CRESOURCE, ListEntry); ListEntry = ListEntry->Flink; // // Close the current RPC handle. // TIME_PRINT(("ReconnectResources - destroying context %08lx\n",Resource->hResource)); Status = MyRpcSmDestroyClientContext(Cluster, &Resource->hResource); if (Status != ERROR_SUCCESS) { TIME_PRINT(("ReconnectResources - RpcSmDestroyClientContext failed Error %d\n",Status)); } // // Open a new RPC handle. // Resource->hResource = ApiOpenResource(Cluster->RpcBinding, Resource->Name, &Status); if (Resource->hResource == NULL) { TIME_PRINT(("ReconnectResources: failed to reopen resource %ws\n",Resource->Name)); return(Status); } } return(ERROR_SUCCESS); } DWORD ReconnectGroups( IN PCLUSTER Cluster ) /*++ Routine Description: Reopens all cluster groups after a reconnect Arguments: Cluster - Supplies the cluster to be reconnected. Return Value: ERROR_SUCCESS if successful Win32 error code otherwise --*/ { PLIST_ENTRY ListEntry; PCGROUP Group; DWORD Status; ListEntry = Cluster->GroupList.Flink; while (ListEntry != &Cluster->GroupList) { Group = CONTAINING_RECORD(ListEntry, CGROUP, ListEntry); ListEntry = ListEntry->Flink; // // Close the old RPC handle // TIME_PRINT(("ReconnectGroups - destroying context %08lx\n",Group->hGroup)); Status = MyRpcSmDestroyClientContext(Cluster, &Group->hGroup); if (Status != ERROR_SUCCESS) { TIME_PRINT(("ReconnectGroups - RpcSmDestroyClientContext failed Error %d\n",Status)); } // // Open a new RPC handle. // Group->hGroup = ApiOpenGroup(Cluster->RpcBinding, Group->Name, &Status); if (Group->hGroup == NULL) { return(Status); } } return(ERROR_SUCCESS); } DWORD ReconnectNodes( IN PCLUSTER Cluster ) /*++ Routine Description: Reopens all cluster nodes after a reconnect Arguments: Cluster - Supplies the cluster to be reconnected. Return Value: ERROR_SUCCESS if successful Win32 error code otherwise --*/ { PLIST_ENTRY ListEntry; PCNODE Node; DWORD Status; ListEntry = Cluster->NodeList.Flink; while (ListEntry != &Cluster->NodeList) { Node = CONTAINING_RECORD(ListEntry, CNODE, ListEntry); ListEntry = ListEntry->Flink; // // Close the old RPC handle. // TIME_PRINT(("ReconnectNodes - destroying context %08lx\n",Node->hNode)); Status = MyRpcSmDestroyClientContext(Cluster, &Node->hNode); if (Status != ERROR_SUCCESS) { TIME_PRINT(("ReconnectNodes - RpcSmDestroyClientContext failed Error %d\n",Status)); } // // Open a new RPC handle. // Node->hNode = ApiOpenNode(Cluster->RpcBinding, Node->Name, &Status); if (Node->hNode == NULL) { return(Status); } } return(ERROR_SUCCESS); } DWORD ReconnectNetworks( IN PCLUSTER Cluster ) /*++ Routine Description: Reopens all cluster networks after a reconnect Arguments: Cluster - Supplies the cluster to be reconnected. Return Value: ERROR_SUCCESS if successful Win32 error code otherwise --*/ { PLIST_ENTRY ListEntry; PCNETWORK Network; DWORD Status; ListEntry = Cluster->NetworkList.Flink; while (ListEntry != &Cluster->NetworkList) { Network = CONTAINING_RECORD(ListEntry, CNETWORK, ListEntry); ListEntry = ListEntry->Flink; // // Close the old RPC handle. // TIME_PRINT(("ReconnectNetworks - destroying context %08lx\n",Network->hNetwork)); Status = MyRpcSmDestroyClientContext(Cluster, &Network->hNetwork); if (Status != ERROR_SUCCESS) { TIME_PRINT(("ReconnectNetworks - RpcSmDestroyClientContext failed Error %d\n",Status)); } // // Open a new RPC handle. // Network->hNetwork = ApiOpenNetwork(Cluster->RpcBinding, Network->Name, &Status); if (Network->hNetwork == NULL) { return(Status); } } return(ERROR_SUCCESS); } DWORD ReconnectNetInterfaces( IN PCLUSTER Cluster ) /*++ Routine Description: Reopens all cluster network interfaces after a reconnect Arguments: Cluster - Supplies the cluster to be reconnected. Return Value: ERROR_SUCCESS if successful Win32 error code otherwise --*/ { PLIST_ENTRY ListEntry; PCNETINTERFACE NetInterface; DWORD Status; ListEntry = Cluster->NetInterfaceList.Flink; while (ListEntry != &Cluster->NetInterfaceList) { NetInterface = CONTAINING_RECORD(ListEntry, CNETINTERFACE, ListEntry); ListEntry = ListEntry->Flink; // // Close the old RPC handle. // TIME_PRINT(("ReconnectNetInterfaces - destroying context %08lx\n",NetInterface->hNetInterface)); Status = MyRpcSmDestroyClientContext(Cluster, &NetInterface->hNetInterface); if (Status != ERROR_SUCCESS) { TIME_PRINT(("ReconnectNetInterfaces - RpcSmDestroyClientContext failed Error %d\n",Status)); } // // Open a new RPC handle. // NetInterface->hNetInterface = ApiOpenNetInterface(Cluster->RpcBinding, NetInterface->Name, &Status); if (NetInterface->hNetInterface == NULL) { return(Status); } } return(ERROR_SUCCESS); } DWORD ReconnectNotifySessions( IN PCLUSTER Cluster ) /*++ Routine Description: Reopens all cluster notify sessions after a reconnect Arguments: Cluster - Supplies the cluster to be reconnected. Return Value: ERROR_SUCCESS if successful Win32 error code otherwise --*/ { PLIST_ENTRY ListEntry, NotifyListEntry; PCNOTIFY_SESSION Session; DWORD Status; PCNOTIFY_PACKET Packet = NULL; PLIST_ENTRY EventEntry; PCNOTIFY_EVENT NotifyEvent; LPCWSTR Name; ListEntry = Cluster->SessionList.Flink; while (ListEntry != &Cluster->SessionList) { Session = CONTAINING_RECORD(ListEntry, CNOTIFY_SESSION, ClusterList); ListEntry = ListEntry->Flink; // // Close the old RPC handle. // TIME_PRINT(("ReconnectNotifySessions - destroying context 0x%08lx\n",Session->hNotify)); //close the old port, since the reconnect may connect to the same //node again Status = ApiCloseNotify(&Session->hNotify); if (Status != ERROR_SUCCESS) { TIME_PRINT(("ReconnectNotifySessions - ApiCloseNotify failed %d\n", Status)); Status = MyRpcSmDestroyClientContext(Cluster, &Session->hNotify); if (Status != ERROR_SUCCESS) { TIME_PRINT(("ReconnectNotifySessions - RpcSmDestroyClientContext failed Error %d\n",Status)); } } // // Open a new RPC handle. // TIME_PRINT(("ReconnectNotifySessions - Calling ApiCreateNotify\n")); Session->hNotify = ApiCreateNotify(Cluster->RpcBinding, &Status); if (Session->hNotify == NULL) { return(Status); } TIME_PRINT(("ReconnectNotifySessions - Session=0x%08lx Notify=0x%08x\n", Session, Session->hNotify)); // // Now repost all the notifications // EventEntry = Session->EventList.Flink; while (EventEntry != &Session->EventList) { NotifyEvent = CONTAINING_RECORD(EventEntry, CNOTIFY_EVENT, ListEntry); EventEntry = EventEntry->Flink; TIME_PRINT(("ReconnectNotifySession: registering event type %lx\n",NotifyEvent->dwFilter)); Status = ReRegisterNotifyEvent(Session, NotifyEvent, NULL); if (Status != ERROR_SUCCESS) { return(Status); } } // Run down the notify list for this cluster and post a packet for // each registered notify event for CLUSTER_CHANGE_RECONNECT_EVENT // Name = Cluster->ClusterName; NotifyListEntry = Cluster->NotifyList.Flink; while (NotifyListEntry != &Cluster->NotifyList) { NotifyEvent = CONTAINING_RECORD(NotifyListEntry, CNOTIFY_EVENT, ObjectList); if (NotifyEvent->dwFilter & CLUSTER_CHANGE_CLUSTER_RECONNECT) { if (Packet == NULL) { Packet = LocalAlloc(LMEM_FIXED, sizeof(CNOTIFY_PACKET)); if (Packet == NULL) { return(ERROR_NOT_ENOUGH_MEMORY); } } //SS: Dont know what the Status was meant for //It looks like it is not being used Packet->Status = ERROR_SUCCESS; Packet->Filter = CLUSTER_CHANGE_CLUSTER_RECONNECT; Packet->KeyId = NotifyEvent->EventId; Packet->Name = MIDL_user_allocate((lstrlenW(Name)+1)*sizeof(WCHAR)); if (Packet->Name != NULL) { lstrcpyW(Packet->Name, Name); } TIME_PRINT(("NotifyThread - posting CLUSTER_CHANGE_CLUSTER_RECONNECT to notify queue\n")); ClRtlInsertTailQueue(&Session->ParentNotify->Queue, &Packet->ListEntry); Packet = NULL; } NotifyListEntry = NotifyListEntry->Flink; } } return(ERROR_SUCCESS); } DWORD GetReconnectCandidates( IN PCLUSTER Cluster ) /*++ Routine Description: Computes the list of reconnect candidates that will be used in case of a connection failure. Arguments: Cluster - supplies the cluster Return Value: ERROR_SUCCESS if successful Win32 error code otherwise --*/ { DWORD Status; PENUM_LIST EnumList = NULL; DWORD i; // // Real bad algorithm here, just get a list of all the nodes // Status = ApiCreateEnum(Cluster->RpcBinding, CLUSTER_ENUM_NODE, &EnumList); if (Status != ERROR_SUCCESS) { return(Status); } Cluster->ReconnectCount = EnumList->EntryCount + 1; Cluster->Reconnect = LocalAlloc(LMEM_FIXED, sizeof(RECONNECT_CANDIDATE)*Cluster->ReconnectCount); if (Cluster->Reconnect == NULL) { MIDL_user_free(EnumList); return(ERROR_NOT_ENOUGH_MEMORY); } for (i=0; iReconnectCount-1; i++) { Cluster->Reconnect[i].IsUp = TRUE; Cluster->Reconnect[i].Name = EnumList->Entry[i].Name; if (lstrcmpiW(Cluster->Reconnect[i].Name, Cluster->NodeName) == 0) { Cluster->Reconnect[i].IsCurrent = TRUE; } else { Cluster->Reconnect[i].IsCurrent = FALSE; } } MIDL_user_free(EnumList); // // Now add the cluster name. // Cluster->Reconnect[i].IsUp = TRUE; Cluster->Reconnect[i].Name = MIDL_user_allocate((lstrlenW(Cluster->ClusterName)+1)*sizeof(WCHAR)); if (Cluster->Reconnect[i].Name == NULL) { // // Just forget about the cluster name. // --Cluster->ReconnectCount; } else { lstrcpyW(Cluster->Reconnect[i].Name, Cluster->ClusterName); Cluster->Reconnect[i].IsCurrent = FALSE; } return(ERROR_SUCCESS); } VOID FreeReconnectCandidates( IN PCLUSTER Cluster ) /*++ Routine Description: Frees and cleans up any reconnect candidates Arguments: Cluster - Supplies the cluster Return Value: None. --*/ { DWORD i; for (i=0; iReconnectCount; i++) { MIDL_user_free(Cluster->Reconnect[i].Name); } LocalFree(Cluster->Reconnect); Cluster->Reconnect = NULL; Cluster->ReconnectCount = 0; } DWORD ReconnectCandidate( IN PCLUSTER Cluster, IN DWORD dwIndex, OUT PBOOL pIsContinue ) /*++ Routine Description: Try to reconnect to the cluster using a reconnection candidate. Called with lock held. Arguments: Cluster - Supplies the cluster dwIndex - Supplies the index of the reconnection candidate in the Cluster->Reconnect[] array pIsContinue - Helps decide whether to continue trying reconnection with other candidates in case this try with the current candidate fails Return Value: ERROR_SUCCESS if successful Win32 error code otherwise --*/ { LPWSTR NewClusterName; LPWSTR NewNodeName; WCHAR *Binding = NULL; RPC_BINDING_HANDLE NewBinding; RPC_BINDING_HANDLE OldBinding; DWORD Status, j; // // Go ahead and try the reconnect. // TIME_PRINT(("ReconnectCandidate - Binding to %ws\n",Cluster->Reconnect[dwIndex].Name)); Status = RpcStringBindingComposeW(L"b97db8b2-4c63-11cf-bff6-08002be23f2f", L"ncadg_ip_udp", Cluster->Reconnect[dwIndex].Name, NULL, NULL, &Binding); if (Status != RPC_S_OK) { TIME_PRINT(("ReconnectCandidate - RpcStringBindingComposeW failed %d\n", Status)); *pIsContinue = FALSE; return(Status); } Status = RpcBindingFromStringBindingW(Binding, &NewBinding); RpcStringFreeW(&Binding); if (Status != RPC_S_OK) { TIME_PRINT(("ReconnectCandidate - RpcBindingFromStringBindingW failed %d\n", Status)); *pIsContinue = FALSE; return(Status); } // // Resolve the binding handle endpoint // TIME_PRINT(("ReconnectCluster - resolving binding endpoint\n")); Status = RpcEpResolveBinding(NewBinding, clusapi_v2_0_c_ifspec); if (Status != RPC_S_OK) { TIME_PRINT(("ReconnectCandidate - RpcEpResolveBinding failed %d\n", Status)); *pIsContinue = TRUE; return(Status); } TIME_PRINT(("ReconnectCandidate - binding endpoint resolved\n")); // // Set authentication information // Status = RpcBindingSetAuthInfoW(NewBinding, NULL, Cluster->AuthnLevel, RPC_C_AUTHN_WINNT, NULL, RPC_C_AUTHZ_NAME); if (Status != RPC_S_OK) { TIME_PRINT(("ReconnectCandidate - RpcBindingSetAuthInfoW failed %d\n", Status)); *pIsContinue = FALSE; return(Status); } OldBinding = Cluster->RpcBinding; Cluster->RpcBinding = NewBinding; MyRpcBindingFree(Cluster, &OldBinding); // // Now that we have a binding, get the cluster name and node name. // NewClusterName = NewNodeName = NULL; Status = ApiGetClusterName(Cluster->RpcBinding, &NewClusterName, &NewNodeName); if (Status != RPC_S_OK) { // // Try the next candidate in our list. // TIME_PRINT(("ReconnectCandidate - ApiGetClusterName failed %d\n",Status)); *pIsContinue = TRUE; return(Status); } TIME_PRINT(("ReconnectCandidate - ApiGetClusterName succeeded, reopening handles\n",Status)); MIDL_user_free(Cluster->ClusterName); MIDL_user_free(Cluster->NodeName); Cluster->ClusterName = NewClusterName; Cluster->NodeName = NewNodeName; if (Cluster->hCluster != NULL) { MyRpcSmDestroyClientContext(Cluster, &Cluster->hCluster); } Cluster->hCluster = ApiOpenCluster(Cluster->RpcBinding, &Status); if (Cluster->hCluster == NULL) { TIME_PRINT(("ReconnectCandidate - ApiOpenCluster failed %d\n", Status)); *pIsContinue = TRUE; return(Status); } // // We got this far, so assume we have a valid connection to a new server. // Reopen the cluster objects. // Status = ReconnectKeys(Cluster); if (Status != ERROR_SUCCESS) { TIME_PRINT(("ReconnectCandidate - ReconnectKeys failed %d\n", Status)); *pIsContinue = TRUE; return(Status); } Status = ReconnectResources(Cluster); if (Status != ERROR_SUCCESS) { TIME_PRINT(("ReconnectCandidate - ReconnectResources failed %d\n", Status)); *pIsContinue = TRUE; return(Status); } Status = ReconnectGroups(Cluster); if (Status != ERROR_SUCCESS) { TIME_PRINT(("ReconnectCandidate - ReconnectGroups failed %d\n", Status)); *pIsContinue = TRUE; return(Status); } Status = ReconnectNodes(Cluster); if (Status != ERROR_SUCCESS) { TIME_PRINT(("ReconnectCandidate - ReconnectNodes failed %d\n", Status)); *pIsContinue = TRUE; return(Status); } Status = ReconnectNetworks(Cluster); if (Status != ERROR_SUCCESS) { TIME_PRINT(("ReconnectCandidate - ReconnectNetworks failed %d\n", Status)); *pIsContinue = TRUE; return(Status); } Status = ReconnectNetInterfaces(Cluster); if (Status != ERROR_SUCCESS) { TIME_PRINT(("ReconnectCandidate - ReconnectNetInterfaces failed %d\n", Status)); *pIsContinue = TRUE; return(Status); } // // Finally, reissue clusterwide notification events. // Status = ReconnectNotifySessions(Cluster); if (Status != ERROR_SUCCESS) { TIME_PRINT(("ReconnectCandidate - ReconnectNotifySessions failed %d\n", Status)); *pIsContinue = TRUE; return(Status); } // // We have successfully reconnected! // ++Cluster->Generation; // // Mark all the other reconnect candidates as not the current. // Mark the successful reconnect candidate as current. // for (j=0; jReconnectCount; j++) { if (j != dwIndex) { Cluster->Reconnect[j].IsCurrent = FALSE; } else { Cluster->Reconnect[dwIndex].IsCurrent = TRUE; } } TIME_PRINT(("ReconnectCandidate - successful!\n", Status)); return (ERROR_SUCCESS); }