1031 lines
31 KiB
C
1031 lines
31 KiB
C
/*++
|
||
|
||
Copyright (c) 1996-1999 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
nmver.c
|
||
|
||
Abstract:
|
||
|
||
Version management functions used by rolling upgrade.
|
||
|
||
Author:
|
||
|
||
Sunita Shrivastava (sunitas)
|
||
|
||
Revision History:
|
||
|
||
1/29/98 Created.
|
||
|
||
--*/
|
||
|
||
#include "nmp.h"
|
||
|
||
error_status_t
|
||
s_CsRpcGetJoinVersionData(
|
||
handle_t handle,
|
||
DWORD JoiningNodeId,
|
||
DWORD JoinerHighestVersion,
|
||
DWORD JoinerLowestVersion,
|
||
LPDWORD SponsorNodeId,
|
||
LPDWORD ClusterHighestVersion,
|
||
LPDWORD ClusterLowestVersion,
|
||
LPDWORD JoinStatus
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Get from and supply to the joiner, version information about the
|
||
sponsor. Mostly a no-op for first version.
|
||
|
||
Arguments:
|
||
|
||
A pile...
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
*SponsorNodeId = NmLocalNodeId;
|
||
|
||
NmpAcquireLock();
|
||
|
||
if (JoiningNodeId == 0)
|
||
{
|
||
//called by setup join
|
||
*ClusterHighestVersion = CsClusterHighestVersion;
|
||
*ClusterLowestVersion = CsClusterLowestVersion;
|
||
//dont exclude any node for version calculation and checking
|
||
*JoinStatus = NmpIsNodeVersionAllowed(ClusterInvalidNodeId, JoinerHighestVersion,
|
||
JoinerLowestVersion, TRUE);
|
||
}
|
||
else
|
||
{
|
||
//called by regular join
|
||
//SS: we should verify this against the cluster version
|
||
NmpCalcClusterVersion(
|
||
JoiningNodeId,
|
||
ClusterHighestVersion,
|
||
ClusterLowestVersion
|
||
);
|
||
*JoinStatus = NmpIsNodeVersionAllowed(JoiningNodeId, JoinerHighestVersion,
|
||
JoinerLowestVersion, TRUE);
|
||
|
||
}
|
||
NmpReleaseLock();
|
||
|
||
return ERROR_SUCCESS;
|
||
}
|
||
|
||
/****
|
||
@func HLOG | NmGetClusterOperationalVersion| This returns the
|
||
operational version for the cluster.
|
||
|
||
@parm LPDWORD | pdwClusterHighestVersion | A pointer to a DWORD where
|
||
the Cluster Highest Version is returned.
|
||
|
||
@parm LPDWORD | pdwClusterHighestVersion | A pointer to a DWORD where
|
||
the Cluster Lowest Version is returned.
|
||
|
||
@parm LPDWORD | pdwFlags | A pointer to a DWORD where the flags
|
||
describing the cluster mode(pure vs fixed version etc) are
|
||
returned.
|
||
|
||
@rdesc Returns ERROR_SUCCESS on success or a win32 error code on failure.
|
||
|
||
@comm
|
||
|
||
@xref <>
|
||
****/
|
||
DWORD NmGetClusterOperationalVersion(
|
||
OUT LPDWORD pdwClusterHighestVersion, OPTIONAL
|
||
OUT LPDWORD pdwClusterLowestVersion, OPTIONAL
|
||
OUT LPDWORD pdwFlags OPTIONAL
|
||
)
|
||
{
|
||
|
||
DWORD dwStatus = ERROR_SUCCESS;
|
||
DWORD flags = 0;
|
||
|
||
//acquire the lock, we are going to be messing with the operational
|
||
//versions for the cluster
|
||
NmpAcquireLock();
|
||
|
||
if (pdwClusterHighestVersion != NULL) {
|
||
*pdwClusterHighestVersion = CsClusterHighestVersion;
|
||
}
|
||
|
||
if (pdwClusterLowestVersion != NULL) {
|
||
*pdwClusterLowestVersion = CsClusterLowestVersion;
|
||
}
|
||
|
||
if (CsClusterHighestVersion == CsClusterLowestVersion) {
|
||
//this is a mixed mode cluster, with the possible exception of
|
||
//nt 4 release(which didnt quite understand anything about rolling
|
||
//upgrades
|
||
flags = CLUSTER_VERSION_FLAG_MIXED_MODE;
|
||
}
|
||
|
||
NmpReleaseLock();
|
||
|
||
if (pdwFlags != NULL) {
|
||
*pdwFlags = flags;
|
||
}
|
||
|
||
return (ERROR_SUCCESS);
|
||
}
|
||
|
||
|
||
/****
|
||
@func HLOG | NmpResetClusterVersion| An operational version of the
|
||
cluster is maintained in the service. This function recalculates
|
||
the operation version. The operational version describes the mode
|
||
in which the cluster is running and prevents nodes which are two
|
||
versions away from running in the same cluster.
|
||
|
||
@rdesc Returns ERROR_SUCCESS on success or a win32 error code on failure.
|
||
|
||
@comm This function is called when a node forms a cluster(to initialize
|
||
the operational version) OR when a node joins a cluster (to
|
||
initialize its version) OR when a node is ejected from a
|
||
cluster(to recalculate the clusterversion).
|
||
|
||
@xref <>
|
||
****/
|
||
VOID
|
||
NmpResetClusterVersion(
|
||
BOOL ProcessChanges
|
||
)
|
||
{
|
||
PNM_NODE pNmNode;
|
||
|
||
//acquire the lock, we are going to be messing with the operational
|
||
//versions for the cluster
|
||
NmpAcquireLock();
|
||
|
||
//initialize the clusterhighestverion and clusterlowest version
|
||
NmpCalcClusterVersion(
|
||
ClusterInvalidNodeId,
|
||
&CsClusterHighestVersion,
|
||
&CsClusterLowestVersion
|
||
);
|
||
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[NM] [NmpResetClusterVersion] ClusterHighestVer=0x%1!08lx! ClusterLowestVer=0x%2!08lx!\r\n",
|
||
CsClusterHighestVersion,
|
||
CsClusterLowestVersion
|
||
);
|
||
|
||
if (ProcessChanges) {
|
||
//
|
||
// If the cluster operational version changed, adjust
|
||
// algorithms and data as needed.
|
||
//
|
||
NmpProcessClusterVersionChange();
|
||
}
|
||
|
||
NmpReleaseLock();
|
||
|
||
return;
|
||
}
|
||
|
||
/****
|
||
@func HLOG | NmpValidateNodeVersion| The sponsor validates that the
|
||
version of the joiner is still the same as before.
|
||
|
||
@parm IN LPWSTR| NodeJoinerId | The Id of the node that is trying to
|
||
join.
|
||
|
||
@parm IN DWORD | NodeHighestVersion | The highest version with which
|
||
this node can communicate.
|
||
|
||
@parm IN DWORD | NodeLowestVersion | The lowest version with which this
|
||
node can communicate.
|
||
|
||
@rdesc Returns ERROR_SUCCESS on success or a win32 error code on failure.
|
||
|
||
@comm This function is called at join time to make sure that the
|
||
joiner's version is still the same as when he last joined. Due
|
||
to uninstalls/upgrade, the cluster service version may change on
|
||
a node. Usually on a complete uninstall, one is expected to
|
||
evict the node out before it may join again.
|
||
|
||
@xref <>
|
||
****/
|
||
DWORD NmpValidateNodeVersion(
|
||
IN LPCWSTR NodeId,
|
||
IN DWORD dwHighestVersion,
|
||
IN DWORD dwLowestVersion
|
||
)
|
||
{
|
||
DWORD dwStatus = ERROR_SUCCESS;
|
||
PNM_NODE pNmNode = NULL;
|
||
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[NM] NmpValidateNodeVersion: Node=%1!ws!, HighestVersion=0x%2!08lx!, LowestVersion=0x%3!08lx!\r\n",
|
||
NodeId, dwHighestVersion, dwLowestVersion);
|
||
|
||
//acquire the NmpLocks, we will be examining the node structure for
|
||
// the joiner node
|
||
NmpAcquireLock();
|
||
|
||
pNmNode = OmReferenceObjectById(ObjectTypeNode, NodeId);
|
||
|
||
if (!pNmNode)
|
||
{
|
||
dwStatus = ERROR_CLUSTER_NODE_NOT_MEMBER;
|
||
goto FnExit;
|
||
}
|
||
|
||
if ((pNmNode->HighestVersion != dwHighestVersion) ||
|
||
(pNmNode->LowestVersion != dwLowestVersion))
|
||
{
|
||
dwStatus = ERROR_REVISION_MISMATCH;
|
||
goto FnExit;
|
||
}
|
||
|
||
FnExit:
|
||
if (pNmNode) OmDereferenceObject(pNmNode);
|
||
ClRtlLogPrint(LOG_NOISE, "[NM] NmpValidateNodeVersion: returns %1!u!\r\n",
|
||
dwStatus);
|
||
NmpReleaseLock();
|
||
return(dwStatus);
|
||
}
|
||
|
||
/****
|
||
@func DWORD | NmpFormFixupNodeVersion| This may be called by a node
|
||
when it is forming a cluster to fix the registry reflect its
|
||
correct version.
|
||
|
||
@parm IN LPCWSTR| NodeId | The Id of the node that is trying to join.
|
||
|
||
@parm IN DWORD | dwHighestVersion | The highest version of the cluster
|
||
s/w running on this code.
|
||
|
||
@parm IN DWORD | dwLowestVersion | The lowest version of the cluster
|
||
s/w running on this node.
|
||
|
||
@rdesc Returns ERROR_SUCCESS on success or a win32 error code on failure.
|
||
|
||
@comm If on a form, there is a mismatch between the versions of the
|
||
cluster s/w and what is recorded as the version in the cluster
|
||
database, the forming node checks to see if the version of
|
||
its current s/w is compatible with the operational version of the
|
||
cluster. If so, it resets the registry to reflect the correct
|
||
version. Else, the form is aborted.
|
||
|
||
@xref <f NmpIsNodeVersionAllowed>
|
||
****/
|
||
DWORD NmpFormFixupNodeVersion(
|
||
IN LPCWSTR NodeId,
|
||
IN DWORD dwHighestVersion,
|
||
IN DWORD dwLowestVersion
|
||
)
|
||
{
|
||
DWORD dwStatus = ERROR_SUCCESS;
|
||
PNM_NODE pNmNode = NULL;
|
||
HDMKEY hNodeKey = NULL;
|
||
|
||
//acquire the NmpLocks, we will be fixing up the node structure for
|
||
// the joiner node
|
||
NmpAcquireLock();
|
||
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[NM] NmpFormFixupNodeVersion: Node=%1!ws! to HighestVer=0x%2!08lx!, LowestVer=0x%3!08lx!\r\n",
|
||
NodeId, dwHighestVersion, dwLowestVersion);
|
||
|
||
pNmNode = OmReferenceObjectById(ObjectTypeNode, NodeId);
|
||
|
||
if (!pNmNode)
|
||
{
|
||
dwStatus = ERROR_CLUSTER_NODE_NOT_MEMBER;
|
||
goto FnExit;
|
||
}
|
||
|
||
hNodeKey = DmOpenKey(DmNodesKey, NodeId, KEY_WRITE);
|
||
|
||
if (hNodeKey == NULL)
|
||
{
|
||
dwStatus = GetLastError();
|
||
ClRtlLogPrint(LOG_CRITICAL,
|
||
"[NM] NmpFormFixupNodeVersion: Failed to open node key, status %1!u!\n",
|
||
dwStatus);
|
||
CL_LOGFAILURE(dwStatus);
|
||
goto FnExit;
|
||
}
|
||
|
||
//set the node's highest version
|
||
dwStatus = DmSetValue(hNodeKey, CLUSREG_NAME_NODE_HIGHEST_VERSION,
|
||
REG_DWORD, (LPBYTE)&dwHighestVersion, sizeof(DWORD));
|
||
|
||
if (dwStatus != ERROR_SUCCESS)
|
||
{
|
||
ClRtlLogPrint(LOG_CRITICAL,
|
||
"[NM] NmpFormFixupNodeVersion: Failed to set the highest version\r\n");
|
||
CL_LOGFAILURE(dwStatus);
|
||
goto FnExit;
|
||
}
|
||
|
||
//set the node's lowest version
|
||
dwStatus = DmSetValue(hNodeKey, CLUSREG_NAME_NODE_LOWEST_VERSION,
|
||
REG_DWORD, (LPBYTE)&dwLowestVersion, sizeof(DWORD));
|
||
|
||
if (dwStatus != ERROR_SUCCESS)
|
||
{
|
||
ClRtlLogPrint(LOG_CRITICAL,
|
||
"[NM] NmpFormFixupNodeVersion: Failed to set the lowest version\r\n");
|
||
CL_LOGFAILURE(dwStatus);
|
||
goto FnExit;
|
||
}
|
||
|
||
pNmNode->HighestVersion = dwHighestVersion;
|
||
pNmNode->LowestVersion = dwLowestVersion;
|
||
|
||
FnExit:
|
||
NmpReleaseLock();
|
||
if (pNmNode)
|
||
OmDereferenceObject(pNmNode);
|
||
if (hNodeKey != NULL)
|
||
DmCloseKey(hNodeKey);
|
||
|
||
return(dwStatus);
|
||
}
|
||
|
||
/****
|
||
@func DWORD | NmpJoinFixupNodeVersion| This may be called by a node
|
||
when it is forming a cluster to fix the registry reflect its
|
||
correct version.
|
||
|
||
@parm IN LPCWSTR| NodeId | The Id of the node that is trying to join.
|
||
|
||
@parm IN DWORD | dwHighestVersion | The highest version of this cluster
|
||
s/w running on this code.
|
||
|
||
@parm IN DWORD | dwLowestVersion | The lowest version of the cluster
|
||
s/w running on this node.
|
||
|
||
@rdesc Returns ERROR_SUCCESS on success or a win32 error code on failure.
|
||
|
||
@comm If on a form, their is a mismatch between the versions of the
|
||
cluster s/w and what is recorded as the version in the cluster
|
||
database, the forming node checks to see if the version of
|
||
its current s/w compatible with the operational version of the
|
||
cluster. If so, it resets the registry to reflect the correct
|
||
version. Else, the form is aborted.
|
||
|
||
@xref <f NmpIsNodeVersionAllowed>
|
||
****/
|
||
DWORD NmpJoinFixupNodeVersion(
|
||
IN HLOCALXSACTION hXsaction,
|
||
IN LPCWSTR szNodeId,
|
||
IN DWORD dwHighestVersion,
|
||
IN DWORD dwLowestVersion
|
||
)
|
||
{
|
||
DWORD dwStatus = ERROR_SUCCESS;
|
||
PNM_NODE pNmNode = NULL;
|
||
HDMKEY hNodeKey = NULL;
|
||
|
||
//acquire the NmpLocks, we will be fixing up the node structure for
|
||
// the joiner node
|
||
NmpAcquireLock();
|
||
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[NM] NmpJoinFixupNodeVersion: Node=%1!ws! to HighestVer=0x%2!08lx!, LowestVer=0x%3!08lx!\r\n",
|
||
szNodeId, dwHighestVersion, dwLowestVersion);
|
||
|
||
pNmNode = OmReferenceObjectById(ObjectTypeNode, szNodeId);
|
||
|
||
if (!pNmNode)
|
||
{
|
||
dwStatus = ERROR_CLUSTER_NODE_NOT_MEMBER;
|
||
goto FnExit;
|
||
}
|
||
|
||
hNodeKey = DmOpenKey(DmNodesKey, szNodeId, KEY_WRITE);
|
||
|
||
if (hNodeKey == NULL)
|
||
{
|
||
dwStatus = GetLastError();
|
||
ClRtlLogPrint(LOG_CRITICAL,
|
||
"[NM] NmpJoinFixupNodeVersion: Failed to open node key, status %1!u!\n",
|
||
dwStatus);
|
||
CL_LOGFAILURE(dwStatus);
|
||
goto FnExit;
|
||
}
|
||
|
||
//set the node's highest version
|
||
dwStatus = DmLocalSetValue(
|
||
hXsaction,
|
||
hNodeKey,
|
||
CLUSREG_NAME_NODE_HIGHEST_VERSION,
|
||
REG_DWORD,
|
||
(LPBYTE)&dwHighestVersion,
|
||
sizeof(DWORD)
|
||
);
|
||
|
||
if (dwStatus != ERROR_SUCCESS)
|
||
{
|
||
ClRtlLogPrint(LOG_CRITICAL,
|
||
"[NM] NmpJoinFixupNodeVersion: Failed to set the highest version\r\n"
|
||
);
|
||
CL_LOGFAILURE(dwStatus);
|
||
goto FnExit;
|
||
}
|
||
|
||
//set the node's lowest version
|
||
dwStatus = DmLocalSetValue(
|
||
hXsaction,
|
||
hNodeKey,
|
||
CLUSREG_NAME_NODE_LOWEST_VERSION,
|
||
REG_DWORD,
|
||
(LPBYTE)&dwLowestVersion,
|
||
sizeof(DWORD)
|
||
);
|
||
|
||
if (dwStatus != ERROR_SUCCESS)
|
||
{
|
||
ClRtlLogPrint(LOG_CRITICAL,
|
||
"[NM] NmpJoinFixupNodeVersion: Failed to set the lowest version\r\n"
|
||
);
|
||
CL_LOGFAILURE(dwStatus);
|
||
goto FnExit;
|
||
}
|
||
|
||
//if written to the registry successfully, update the in-memory structures
|
||
pNmNode->HighestVersion = dwHighestVersion;
|
||
pNmNode->LowestVersion = dwLowestVersion;
|
||
|
||
|
||
if (dwStatus == ERROR_SUCCESS)
|
||
{
|
||
ClusterEvent(CLUSTER_EVENT_NODE_PROPERTY_CHANGE, pNmNode);
|
||
}
|
||
|
||
|
||
FnExit:
|
||
NmpReleaseLock();
|
||
if (pNmNode)
|
||
OmDereferenceObject(pNmNode);
|
||
if (hNodeKey != NULL)
|
||
DmCloseKey(hNodeKey);
|
||
|
||
return(dwStatus);
|
||
}
|
||
|
||
/****
|
||
@func HLOG | NmpIsNodeVersionAllowed| This is called at join time
|
||
(not setup join) e sponsor validates if a joiner
|
||
should be allowed to join a cluster at this time. In a mixed
|
||
mode cluster, a node may not be able to join a cluster if another
|
||
node that is two versions away is already a part of the cluster.
|
||
|
||
@parm IN DWORD | dwExcludeNodeId | The node Id to exclude while
|
||
evaluating the cluster operational version.
|
||
|
||
@parm IN DWORD | NodeHighestVersion | The highest version with which
|
||
this node can communicate.
|
||
|
||
@parm IN DWORD | NodeLowestVersion | The lowest version with which this
|
||
node can communicate.
|
||
|
||
@parm IN BOOL |bJoin| If this is being invoked at join or form time.
|
||
|
||
@rdesc Returns ERROR_SUCCESS on success or a win32 error code on failure.
|
||
|
||
@comm This function is called when a node requests a sponsor to allow
|
||
it to join a cluster.
|
||
|
||
@xref <>
|
||
****/
|
||
DWORD NmpIsNodeVersionAllowed(
|
||
IN DWORD dwExcludeNodeId,
|
||
IN DWORD dwNodeHighestVersion,
|
||
IN DWORD dwNodeLowestVersion,
|
||
IN BOOL bJoin
|
||
)
|
||
{
|
||
DWORD dwStatus = ERROR_SUCCESS;
|
||
DWORD ClusterHighestVersion;
|
||
DWORD ClusterLowestVersion;
|
||
PLIST_ENTRY pListEntry;
|
||
DWORD dwCnt;
|
||
PNM_NODE pNmNode;
|
||
|
||
ClRtlLogPrint(LOG_UNUSUAL,
|
||
"[NM] NmpIsNodeVersionAllowed: Entry\r\n");
|
||
|
||
|
||
//acquire the NmpLocks, we will be examining the node structures
|
||
NmpAcquireLock();
|
||
|
||
//if NoVersionCheckOption is true
|
||
if (CsNoVersionCheck)
|
||
goto FnExit;
|
||
|
||
|
||
//if this is a single node cluster, and this is being called at form
|
||
//the count of nodes is zero.
|
||
//this will happen when the registry versions dont match with
|
||
//cluster service exe version numbers and we need to allow the single
|
||
//node to form
|
||
for (dwCnt=0, pListEntry = NmpNodeList.Flink;
|
||
pListEntry != &NmpNodeList; pListEntry = pListEntry->Flink )
|
||
{
|
||
pNmNode = CONTAINING_RECORD(pListEntry, NM_NODE, Linkage);
|
||
if (NmGetNodeId(pNmNode) == dwExcludeNodeId)
|
||
continue;
|
||
dwCnt++;
|
||
}
|
||
|
||
if (!dwCnt)
|
||
{
|
||
//allow the node to form
|
||
goto FnExit;
|
||
}
|
||
|
||
|
||
dwStatus = NmpCalcClusterVersion(
|
||
dwExcludeNodeId,
|
||
&ClusterHighestVersion,
|
||
&ClusterLowestVersion
|
||
);
|
||
|
||
if (dwStatus != ERROR_SUCCESS)
|
||
{
|
||
goto FnExit;
|
||
}
|
||
|
||
//if the node is forming and this node has just upgraded
|
||
//allow the node to form as long as its minor(or build number)
|
||
//is greater than or equal to all other nodes with the same
|
||
//major number in the cluster
|
||
if (!bJoin && CsUpgrade)
|
||
{
|
||
|
||
DWORD dwMinorVersion = 0x00000000;
|
||
|
||
for (pListEntry = NmpNodeList.Flink; pListEntry != &NmpNodeList;
|
||
pListEntry = pListEntry->Flink )
|
||
{
|
||
pNmNode = CONTAINING_RECORD(pListEntry, NM_NODE, Linkage);
|
||
|
||
if (NmGetNodeId(pNmNode) == dwExcludeNodeId)
|
||
continue;
|
||
|
||
if (CLUSTER_GET_MAJOR_VERSION(pNmNode->HighestVersion) ==
|
||
CLUSTER_GET_MAJOR_VERSION(dwNodeHighestVersion))
|
||
{
|
||
//the minor version to check is the maximum of the
|
||
//build numbers amongst the nodes with the same major
|
||
//version
|
||
dwMinorVersion = max(dwMinorVersion,
|
||
CLUSTER_GET_MINOR_VERSION(pNmNode->HighestVersion));
|
||
}
|
||
}
|
||
|
||
//dont allow a lower build on a node to regress the cluster version
|
||
// if other nodes have already upgraded to higher builds
|
||
if ((dwMinorVersion != 0) &&
|
||
(CLUSTER_GET_MINOR_VERSION(dwNodeHighestVersion) < dwMinorVersion))
|
||
{
|
||
dwStatus = ERROR_CLUSTER_INCOMPATIBLE_VERSIONS;
|
||
goto FnExit;
|
||
}
|
||
else
|
||
{
|
||
//ISSUE ::for now we allow double jumps(from n-1 to n+1)
|
||
// on upgrades
|
||
//skip checking versioning
|
||
goto FnExit;
|
||
}
|
||
}
|
||
|
||
//if the joiners lowest version is equal the clusters highest
|
||
//For instance 3/2, 2/1 and 4/3 can all join 3/2
|
||
if ((dwNodeHighestVersion == ClusterHighestVersion) ||
|
||
(dwNodeHighestVersion == ClusterLowestVersion) ||
|
||
(dwNodeLowestVersion == ClusterHighestVersion))
|
||
{
|
||
|
||
PNM_NODE pNmNode= NULL;
|
||
DWORD dwMinorVersion;
|
||
|
||
//since the version numbers include build number as the minor part
|
||
// and we disallow a node from operating with a cluster if its
|
||
// major number is equal but its minor number is different from
|
||
// any of the nodes in the cluster.
|
||
// The CsClusterHighestVersion doesnt encapsulate this since it just
|
||
// remembers the highest version that the cluster as a whole can talk
|
||
// to.
|
||
// E.g 1.
|
||
// 3.2003 should be able to join a cluster with nodes
|
||
// 3.2002(not running and not upgraded as yet but a part of the cluster)and
|
||
// 3.2003(running).
|
||
// E.g 2
|
||
// 3.2002 will not be able to join a cluster with nodes 3.2003(running)and
|
||
// 3.2002 (not running but a part of the cluster)
|
||
// E.g 3.
|
||
// 3.2003 will not able to join a cluster with nodes 3.2002(running) and
|
||
// 3.2002(running)
|
||
|
||
dwMinorVersion = 0x00000000;
|
||
|
||
for (pListEntry = NmpNodeList.Flink; pListEntry != &NmpNodeList;
|
||
pListEntry = pListEntry->Flink )
|
||
{
|
||
pNmNode = CONTAINING_RECORD(pListEntry, NM_NODE, Linkage);
|
||
|
||
if (NmGetNodeId(pNmNode) == dwExcludeNodeId)
|
||
continue;
|
||
|
||
if (CLUSTER_GET_MAJOR_VERSION(pNmNode->HighestVersion) ==
|
||
CLUSTER_GET_MAJOR_VERSION(dwNodeHighestVersion))
|
||
{
|
||
//the minor version to check is the maximum of the
|
||
//build numbers amongst the nodes with the same major
|
||
//version
|
||
dwMinorVersion = max(dwMinorVersion,
|
||
CLUSTER_GET_MINOR_VERSION(pNmNode->HighestVersion));
|
||
}
|
||
}
|
||
// if the joining node's build number is the same as max of build
|
||
//number of all nodes within the cluster with the same major version
|
||
//allow it to participate in this cluster, else dont allow it to participate in this cluster
|
||
//take care of a single node case by checking the minor number against
|
||
//0
|
||
if ((dwMinorVersion != 0) &&
|
||
(CLUSTER_GET_MINOR_VERSION(dwNodeHighestVersion) != dwMinorVersion))
|
||
{
|
||
dwStatus = ERROR_CLUSTER_INCOMPATIBLE_VERSIONS;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
dwStatus = ERROR_CLUSTER_INCOMPATIBLE_VERSIONS;
|
||
}
|
||
|
||
FnExit:
|
||
NmpReleaseLock();
|
||
ClRtlLogPrint(LOG_UNUSUAL,
|
||
"[NM] NmpIsNodeVersionAllowed: Exit, Status=%1!u!\r\n",
|
||
dwStatus);
|
||
|
||
return(dwStatus);
|
||
}
|
||
|
||
|
||
/****
|
||
@func HLOG | NmpCalcClusterVersion| This is called to calculate the
|
||
operational cluster version.
|
||
|
||
@parm IN DWORD | dwExcludeNodeId | The node Id to exclude while evaluating
|
||
the cluster operational version.
|
||
|
||
@parm OUT LPDWORD | pdwClusterHighestVersion | The highest version with which this node
|
||
can communicate.
|
||
|
||
@parm IN LPDWORD | pdwClusterLowestVersion | The lowest version with which this node can
|
||
communicate.
|
||
|
||
@rdesc Returns ERROR_SUCCESS on success or a win32 error code on failure.
|
||
|
||
@comm This function must be called with the NmpLock held.
|
||
|
||
@xref <f NmpResetClusterVersion> <f NmpIsNodeVersionAllowed>
|
||
****/
|
||
DWORD NmpCalcClusterVersion(
|
||
IN DWORD dwExcludeNodeId,
|
||
OUT LPDWORD pdwClusterHighestVersion,
|
||
OUT LPDWORD pdwClusterLowestVersion
|
||
)
|
||
{
|
||
|
||
WCHAR Buffer[4];
|
||
PNM_NODE pExcludeNode=NULL;
|
||
PNM_NODE pNmNode;
|
||
DWORD dwStatus = ERROR_SUCCESS;
|
||
PLIST_ENTRY pListEntry;
|
||
DWORD dwCnt = 0;
|
||
|
||
//initialize the values such that min/max do the right thing
|
||
*pdwClusterHighestVersion = 0xFFFFFFFF;
|
||
*pdwClusterLowestVersion = 0x00000000;
|
||
|
||
if (dwExcludeNodeId != ClusterInvalidNodeId)
|
||
{
|
||
wsprintfW(Buffer, L"%d", dwExcludeNodeId);
|
||
pExcludeNode = OmReferenceObjectById(ObjectTypeNode, Buffer);
|
||
if (!pExcludeNode)
|
||
{
|
||
dwStatus = ERROR_INVALID_PARAMETER;
|
||
ClRtlLogPrint(LOG_UNUSUAL,
|
||
"[NM] NmpCalcClusterVersion :Node=%1!ws! to be excluded not found\r\n",
|
||
Buffer);
|
||
goto FnExit;
|
||
}
|
||
}
|
||
|
||
for ( pListEntry = NmpNodeList.Flink;
|
||
pListEntry != &NmpNodeList;
|
||
pListEntry = pListEntry->Flink )
|
||
{
|
||
pNmNode = CONTAINING_RECORD(pListEntry, NM_NODE, Linkage);
|
||
if ((pExcludeNode) && (pExcludeNode->NodeId == pNmNode->NodeId))
|
||
continue;
|
||
|
||
//Actually to fix upgrade scenarios, we must fix the cluster
|
||
//version such that the node with the highest minor version
|
||
//is able to form/join but others arent
|
||
// This is needed for multinode clusters
|
||
if (CLUSTER_GET_MAJOR_VERSION(pNmNode->HighestVersion) ==
|
||
CLUSTER_GET_MAJOR_VERSION(*pdwClusterHighestVersion))
|
||
{
|
||
if (CLUSTER_GET_MINOR_VERSION(pNmNode->HighestVersion) >
|
||
CLUSTER_GET_MINOR_VERSION(*pdwClusterHighestVersion))
|
||
{
|
||
*pdwClusterHighestVersion = pNmNode->HighestVersion;
|
||
}
|
||
|
||
}
|
||
else
|
||
{
|
||
*pdwClusterHighestVersion = min(
|
||
*pdwClusterHighestVersion,
|
||
pNmNode->HighestVersion
|
||
);
|
||
|
||
}
|
||
*pdwClusterLowestVersion = max(
|
||
*pdwClusterLowestVersion,
|
||
pNmNode->LowestVersion
|
||
);
|
||
dwCnt++;
|
||
|
||
}
|
||
|
||
if (dwCnt == 0)
|
||
{
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[NM] NmpCalcClusterVersion: Single node version. Setting cluster version to node version\r\n"
|
||
);
|
||
|
||
//single node cluster, even though the we were requested to
|
||
//exclude this node, the cluster version must be calculated
|
||
//using that node's version
|
||
*pdwClusterHighestVersion = pExcludeNode->HighestVersion;
|
||
*pdwClusterLowestVersion = pExcludeNode->LowestVersion;
|
||
}
|
||
CL_ASSERT(*pdwClusterHighestVersion != 0xFFFFFFFF);
|
||
CL_ASSERT(*pdwClusterLowestVersion != 0x00000000);
|
||
|
||
FnExit:
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[NM] NmpCalcClusterVersion: status = %1!u! ClusHighestVer=0x%2!08lx!, ClusLowestVer=0x%3!08lx!\r\n",
|
||
dwStatus, *pdwClusterHighestVersion, *pdwClusterLowestVersion);
|
||
|
||
if (pExcludeNode) OmDereferenceObject(pExcludeNode);
|
||
return(dwStatus);
|
||
}
|
||
|
||
|
||
VOID
|
||
NmpProcessClusterVersionChange(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Notes:
|
||
|
||
Called with the NmpLock held.
|
||
|
||
--*/
|
||
{
|
||
DWORD status;
|
||
LPWSTR szClusterName=NULL;
|
||
DWORD dwSize=0;
|
||
|
||
NmpMulticastProcessClusterVersionChange();
|
||
|
||
//rjain: issue CLUSTER_EVENT_PROPERTY_CHANGE to propagate new
|
||
//cluster version info
|
||
DmQuerySz( DmClusterParametersKey,
|
||
CLUSREG_NAME_CLUS_NAME,
|
||
&szClusterName,
|
||
&dwSize,
|
||
&dwSize);
|
||
if(szClusterName)
|
||
ClusterEventEx(
|
||
CLUSTER_EVENT_PROPERTY_CHANGE,
|
||
EP_FREE_CONTEXT,
|
||
szClusterName
|
||
);
|
||
|
||
return;
|
||
|
||
} // NmpProcessClusterVersionChange
|
||
|
||
|
||
/****
|
||
@func DWORD | NmpBuildVersionInfo| Its a callback function used by
|
||
NmPerformFixups to build a property list of the Major Version,
|
||
Minor version, Build Number and CSDVersionInfo, This propertylist
|
||
is used by NmUpdatePerformFixups Update type to store this info
|
||
in registry.
|
||
|
||
@parm IN DWORD | dwFixupType| JoinFixup or FormFixup
|
||
|
||
@parm OUT PVOID* | ppPropertyList| Pointer to the pointer to the property list
|
||
@parm OUT LPDWORD | pdwProperyListSize | Pointer to the property list size
|
||
|
||
@param OUT LPWSTR* | pszKeyName. The name of registry key for which this
|
||
property list is being constructed.
|
||
|
||
@rdesc Returns a result code. ERROR_SUCCESS on success.
|
||
|
||
@xref <f NmpUpdatePerformFixups2>
|
||
****/
|
||
|
||
|
||
DWORD NmpBuildVersionInfo(
|
||
IN DWORD dwFixUpType,
|
||
OUT PVOID * ppPropertyList,
|
||
OUT LPDWORD pdwPropertyListSize,
|
||
OUT LPWSTR * pszKeyName
|
||
)
|
||
{
|
||
DWORD dwStatus=ERROR_SUCCESS;
|
||
LPBYTE pInParams=NULL;
|
||
DWORD Required,Returned;
|
||
HDMKEY hdmKey;
|
||
DWORD dwTemp;
|
||
CLUSTERVERSIONINFO ClusterVersionInfo;
|
||
LPWSTR szTemp=NULL;
|
||
|
||
*ppPropertyList = NULL;
|
||
*pdwPropertyListSize = 0;
|
||
|
||
//check we if need to send this information
|
||
dwTemp=(lstrlenW(CLUSREG_KEYNAME_NODES) + lstrlenW(L"\\")+lstrlenW(NmLocalNodeIdString)+1)*sizeof(WCHAR);
|
||
*pszKeyName=(LPWSTR)LocalAlloc(LMEM_FIXED,dwTemp);
|
||
if(*pszKeyName==NULL)
|
||
{
|
||
dwStatus =GetLastError();
|
||
goto FnExit;
|
||
}
|
||
lstrcpyW(*pszKeyName,CLUSREG_KEYNAME_NODES);
|
||
lstrcatW(*pszKeyName,L"\\");
|
||
lstrcatW(*pszKeyName,NmLocalNodeIdString);
|
||
|
||
// Build the parameter list
|
||
|
||
pInParams=(LPBYTE)LocalAlloc(LMEM_FIXED,4*sizeof(DWORD)+sizeof(LPWSTR));
|
||
if(pInParams==NULL)
|
||
{
|
||
dwStatus =GetLastError();
|
||
goto FnExit;
|
||
}
|
||
|
||
CsGetClusterVersionInfo(&ClusterVersionInfo);
|
||
|
||
dwTemp=(DWORD)ClusterVersionInfo.MajorVersion;
|
||
CopyMemory(pInParams,&dwTemp,sizeof(DWORD));
|
||
|
||
dwTemp=(DWORD)ClusterVersionInfo.MinorVersion;
|
||
CopyMemory(pInParams+sizeof(DWORD),&dwTemp,sizeof(DWORD));
|
||
|
||
dwTemp=(DWORD)ClusterVersionInfo.BuildNumber;
|
||
CopyMemory(pInParams+2*sizeof(DWORD),&dwTemp,sizeof(DWORD));
|
||
|
||
if(ClusterVersionInfo.szCSDVersion==NULL)
|
||
szTemp=NULL;
|
||
else
|
||
{
|
||
szTemp=(LPWSTR)LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT,(lstrlenW(ClusterVersionInfo.szCSDVersion) +1)*sizeof(WCHAR));
|
||
if (szTemp==NULL)
|
||
{
|
||
dwStatus=GetLastError();
|
||
goto FnExit;
|
||
}
|
||
lstrcpyW(szTemp,ClusterVersionInfo.szCSDVersion);
|
||
szTemp[lstrlenW(ClusterVersionInfo.szCSDVersion)]=L'\0';
|
||
}
|
||
CopyMemory(pInParams+3*sizeof(DWORD),&szTemp,sizeof(LPWSTR));
|
||
|
||
//copy the suite information
|
||
CopyMemory(pInParams+3*sizeof(DWORD)+sizeof(LPWSTR*),
|
||
&CsMyProductSuite, sizeof(DWORD));
|
||
|
||
Required=sizeof(DWORD);
|
||
AllocMem:
|
||
*ppPropertyList=(LPBYTE)LocalAlloc(LMEM_FIXED, Required);
|
||
if(*ppPropertyList==NULL)
|
||
{
|
||
dwStatus=GetLastError();
|
||
goto FnExit;
|
||
}
|
||
*pdwPropertyListSize=Required;
|
||
dwStatus = ClRtlPropertyListFromParameterBlock(
|
||
NmFixupVersionInfo,
|
||
*ppPropertyList,
|
||
pdwPropertyListSize,
|
||
(LPBYTE)pInParams,
|
||
&Returned,
|
||
&Required
|
||
);
|
||
*pdwPropertyListSize=Returned;
|
||
if (dwStatus==ERROR_MORE_DATA)
|
||
{
|
||
LocalFree(*ppPropertyList);
|
||
*ppPropertyList=NULL;
|
||
goto AllocMem;
|
||
}
|
||
else
|
||
if (dwStatus != ERROR_SUCCESS)
|
||
{
|
||
ClRtlLogPrint(LOG_CRITICAL,"[NM] NmBuildVersionInfo - error = %1!u!\r\n",dwStatus);
|
||
goto FnExit;
|
||
}
|
||
|
||
FnExit:
|
||
// Cleanup
|
||
if (szTemp)
|
||
LocalFree(szTemp);
|
||
if(pInParams)
|
||
LocalFree(pInParams);
|
||
return dwStatus;
|
||
}//NmpBuildVersionInfo
|
||
|
||
/****
|
||
@func HLOG | NmpCalcClusterNodeLimit|This is called to calculate the
|
||
operational cluster node limit.
|
||
|
||
@rdesc Returns ERROR_SUCCESS on success or a win32 error code on failure.
|
||
|
||
@comm This acquires/releases NmpLock.
|
||
|
||
@xref <f NmpResetClusterVersion> <f NmpIsNodeVersionAllowed>
|
||
****/
|
||
DWORD NmpCalcClusterNodeLimit(
|
||
)
|
||
{
|
||
PNM_NODE pNmNode;
|
||
DWORD dwStatus = ERROR_SUCCESS;
|
||
PLIST_ENTRY pListEntry;
|
||
|
||
//acquire the lock, we are going to be messing with the operational
|
||
//versions for the cluster
|
||
NmpAcquireLock();
|
||
|
||
CsClusterNodeLimit = NmMaxNodeId;
|
||
|
||
for ( pListEntry = NmpNodeList.Flink;
|
||
pListEntry != &NmpNodeList;
|
||
pListEntry = pListEntry->Flink )
|
||
{
|
||
pNmNode = CONTAINING_RECORD(pListEntry, NM_NODE, Linkage);
|
||
|
||
CsClusterNodeLimit = min(
|
||
CsClusterNodeLimit,
|
||
ClRtlGetDefaultNodeLimit(
|
||
pNmNode->ProductSuite
|
||
)
|
||
);
|
||
}
|
||
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[NM] Calculated cluster node limit = %1!u!\r\n",
|
||
CsClusterNodeLimit);
|
||
|
||
NmpReleaseLock();
|
||
|
||
return (dwStatus);
|
||
}
|
||
|
||
|
||
/****
|
||
@func VOID| NmpResetClusterNodeLimit| An operational node limit
|
||
on the number of nodes that can join this cluster is maintained.
|
||
|
||
@rdesc Returns ERROR_SUCCESS on success or a win32 error code on failure.
|
||
|
||
@comm This function is called when a node forms a cluster(to initialize
|
||
the operational version) OR when a node joins a cluster (to
|
||
initialize its version) OR when a node is ejected from a
|
||
cluster(to recalculate the clusterversion).
|
||
|
||
@xref <>
|
||
****/
|
||
VOID
|
||
NmpResetClusterNodeLimit(
|
||
)
|
||
{
|
||
NmpCalcClusterNodeLimit();
|
||
}
|
||
|