Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1236 lines
39 KiB

  1. /*++
  2. Copyright (c) 1996-1999 Microsoft Corporation
  3. Module Name:
  4. nmver.c
  5. Abstract:
  6. Version management functions used by rolling upgrade.
  7. Author:
  8. Sunita Shrivastava (sunitas)
  9. Revision History:
  10. 1/29/98 Created.
  11. --*/
  12. #include "nmp.h"
  13. #define NMP_DEFAULT_JOIN_DELAY 3000
  14. DWORD
  15. NmpGetJoinVersionDelay(
  16. RPC_BINDING_HANDLE ClientHandle
  17. )
  18. /*++
  19. Routine Description:
  20. Determine the delay to introduce before responding to a
  21. join-version request. The delay is determined by network
  22. priority. The highest priority network has no delay.
  23. Arguments:
  24. ClientHandle - client RPC binding handle
  25. Return value:
  26. Delay in milliseconds
  27. Notes:
  28. Called with NM lock held
  29. --*/
  30. {
  31. RPC_BINDING_HANDLE serverBinding = NULL;
  32. LPWSTR serverStringBinding = NULL;
  33. LPWSTR networkAddressString = NULL;
  34. PNM_NETWORK network = NULL;
  35. DWORD delay = NMP_DEFAULT_JOIN_DELAY;
  36. DWORD error;
  37. error = RpcBindingServerFromClient(ClientHandle, &serverBinding);
  38. if (error != RPC_S_OK) {
  39. ClRtlLogPrint(LOG_UNUSUAL,
  40. "[NM] Failed to get server binding, error %1!u!.\n",
  41. error
  42. );
  43. goto error_exit;
  44. }
  45. error = RpcBindingToStringBinding(serverBinding, &serverStringBinding);
  46. if (error != RPC_S_OK) {
  47. ClRtlLogPrint(LOG_UNUSUAL,
  48. "[NM] Failed to convert server binding to string binding, "
  49. "error %1!u!.\n",
  50. error
  51. );
  52. goto error_exit;
  53. }
  54. error = RpcStringBindingParse(
  55. serverStringBinding,
  56. NULL, // object uuid
  57. NULL, // prot seq
  58. &networkAddressString,
  59. NULL, // endpoint
  60. NULL // network options
  61. );
  62. if (error != RPC_S_OK) {
  63. ClRtlLogPrint(LOG_UNUSUAL,
  64. "[NM] Failed to parse network address from "
  65. "server binding string, error %1!u!.\n",
  66. error
  67. );
  68. goto error_exit;
  69. } else {
  70. ClRtlLogPrint(LOG_NOISE,
  71. "[NM] Received sponsorship request from client "
  72. "address %1!ws!.\n",
  73. networkAddressString
  74. );
  75. }
  76. network = NmpReferenceNetworkByRemoteAddress(networkAddressString);
  77. if (network == NULL) {
  78. ClRtlLogPrint(LOG_UNUSUAL,
  79. "[NM] Failed to find network matching address %1!ws!, "
  80. "error %2!u!.\n",
  81. networkAddressString, error
  82. );
  83. goto error_exit;
  84. }
  85. if (network->Priority == 1) {
  86. delay = 0;
  87. }
  88. error_exit:
  89. ClRtlLogPrint(LOG_NOISE,
  90. "[NM] Calculated join-version delay of %1!u! milliseconds "
  91. "for request from address %2!ws!.\n",
  92. delay,
  93. ((networkAddressString == NULL) ? NmpUnknownString : networkAddressString)
  94. );
  95. if (network != NULL) {
  96. NmpDereferenceNetwork(network);
  97. }
  98. if (networkAddressString != NULL) {
  99. RpcStringFree(&networkAddressString);
  100. }
  101. if (serverStringBinding != NULL) {
  102. RpcStringFree(&serverStringBinding);
  103. }
  104. if (serverBinding != NULL) {
  105. RpcBindingFree(serverBinding);
  106. }
  107. return(delay);
  108. } // NmpGetJoinVersionDelay
  109. error_status_t
  110. s_CsRpcGetJoinVersionData(
  111. handle_t handle,
  112. DWORD JoiningNodeId,
  113. DWORD JoinerHighestVersion,
  114. DWORD JoinerLowestVersion,
  115. LPDWORD SponsorNodeId,
  116. LPDWORD ClusterHighestVersion,
  117. LPDWORD ClusterLowestVersion,
  118. LPDWORD JoinStatus
  119. )
  120. /*++
  121. Routine Description:
  122. Get from and supply to the joiner, version information about the
  123. sponsor. Mostly a no-op for first version.
  124. Determine network priority. Delay response to clients over networks
  125. that are not top priority. This heuristic increases the chance that
  126. join will occur over a private, hence physically secure network.
  127. Arguments:
  128. A pile...
  129. Return Value:
  130. None
  131. --*/
  132. {
  133. *SponsorNodeId = NmLocalNodeId;
  134. NmpAcquireLock();
  135. if (JoiningNodeId == 0)
  136. {
  137. //called by setup join
  138. *ClusterHighestVersion = CsClusterHighestVersion;
  139. *ClusterLowestVersion = CsClusterLowestVersion;
  140. //dont exclude any node for version calculation and checking
  141. *JoinStatus = NmpIsNodeVersionAllowed(ClusterInvalidNodeId, JoinerHighestVersion,
  142. JoinerLowestVersion, TRUE);
  143. NmpReleaseLock();
  144. }
  145. else
  146. {
  147. //called by regular join
  148. DWORD delay;
  149. //SS: we should verify this against the cluster version
  150. NmpCalcClusterVersion(
  151. JoiningNodeId,
  152. ClusterHighestVersion,
  153. ClusterLowestVersion
  154. );
  155. *JoinStatus = NmpIsNodeVersionAllowed(JoiningNodeId, JoinerHighestVersion,
  156. JoinerLowestVersion, TRUE);
  157. // Determine the delay.
  158. delay = NmpGetJoinVersionDelay((RPC_BINDING_HANDLE) handle);
  159. NmpReleaseLock();
  160. if (delay > 0) {
  161. Sleep(delay);
  162. }
  163. }
  164. return ERROR_SUCCESS;
  165. }
  166. /****
  167. @func HLOG | NmGetClusterOperationalVersion| This returns the
  168. operational version for the cluster.
  169. @parm LPDWORD | pdwClusterHighestVersion | A pointer to a DWORD where
  170. the Cluster Highest Version is returned.
  171. @parm LPDWORD | pdwClusterHighestVersion | A pointer to a DWORD where
  172. the Cluster Lowest Version is returned.
  173. @parm LPDWORD | pdwFlags | A pointer to a DWORD where the flags
  174. describing the cluster mode(pure vs fixed version etc) are
  175. returned.
  176. @rdesc Returns ERROR_SUCCESS on success or a win32 error code on failure.
  177. @comm
  178. @xref <>
  179. ****/
  180. DWORD NmGetClusterOperationalVersion(
  181. OUT LPDWORD pdwClusterHighestVersion, OPTIONAL
  182. OUT LPDWORD pdwClusterLowestVersion, OPTIONAL
  183. OUT LPDWORD pdwFlags OPTIONAL
  184. )
  185. {
  186. DWORD dwStatus = ERROR_SUCCESS;
  187. DWORD flags = 0;
  188. //acquire the lock, we are going to be messing with the operational
  189. //versions for the cluster
  190. NmpAcquireLock();
  191. if (pdwClusterHighestVersion != NULL) {
  192. *pdwClusterHighestVersion = CsClusterHighestVersion;
  193. }
  194. if (pdwClusterLowestVersion != NULL) {
  195. *pdwClusterLowestVersion = CsClusterLowestVersion;
  196. }
  197. if (CsClusterHighestVersion == CsClusterLowestVersion) {
  198. //this is a mixed mode cluster, with the possible exception of
  199. //nt 4 release(which didnt quite understand anything about rolling
  200. //upgrades
  201. flags = CLUSTER_VERSION_FLAG_MIXED_MODE;
  202. }
  203. NmpReleaseLock();
  204. if (pdwFlags != NULL) {
  205. *pdwFlags = flags;
  206. }
  207. return (ERROR_SUCCESS);
  208. }
  209. /****
  210. @func HLOG | NmpResetClusterVersion| An operational version of the
  211. cluster is maintained in the service. This function recalculates
  212. the operation version. The operational version describes the mode
  213. in which the cluster is running and prevents nodes which are two
  214. versions away from running in the same cluster.
  215. @rdesc Returns ERROR_SUCCESS on success or a win32 error code on failure.
  216. @comm This function is called when a node forms a cluster(to initialize
  217. the operational version) OR when a node joins a cluster (to
  218. initialize its version) OR when a node is ejected from a
  219. cluster(to recalculate the clusterversion).
  220. @xref <>
  221. ****/
  222. VOID
  223. NmpResetClusterVersion(
  224. BOOL ProcessChanges
  225. )
  226. {
  227. PNM_NODE pNmNode;
  228. //acquire the lock, we are going to be messing with the operational
  229. //versions for the cluster
  230. NmpAcquireLock();
  231. //initialize the clusterhighestverion and clusterlowest version
  232. NmpCalcClusterVersion(
  233. ClusterInvalidNodeId,
  234. &CsClusterHighestVersion,
  235. &CsClusterLowestVersion
  236. );
  237. ClRtlLogPrint(LOG_NOISE,
  238. "[NM] [NmpResetClusterVersion] ClusterHighestVer=0x%1!08lx! ClusterLowestVer=0x%2!08lx!\r\n",
  239. CsClusterHighestVersion,
  240. CsClusterLowestVersion
  241. );
  242. if (ProcessChanges) {
  243. //
  244. // If the cluster operational version changed, adjust
  245. // algorithms and data as needed.
  246. //
  247. NmpProcessClusterVersionChange();
  248. }
  249. NmpReleaseLock();
  250. return;
  251. }
  252. /****
  253. @func HLOG | NmpValidateNodeVersion| The sponsor validates that the
  254. version of the joiner is still the same as before.
  255. @parm IN LPWSTR| NodeJoinerId | The Id of the node that is trying to
  256. join.
  257. @parm IN DWORD | NodeHighestVersion | The highest version with which
  258. this node can communicate.
  259. @parm IN DWORD | NodeLowestVersion | The lowest version with which this
  260. node can communicate.
  261. @rdesc Returns ERROR_SUCCESS on success or a win32 error code on failure.
  262. @comm This function is called at join time to make sure that the
  263. joiner's version is still the same as when he last joined. Due
  264. to uninstalls/upgrade, the cluster service version may change on
  265. a node. Usually on a complete uninstall, one is expected to
  266. evict the node out before it may join again.
  267. @xref <>
  268. ****/
  269. DWORD NmpValidateNodeVersion(
  270. IN LPCWSTR NodeId,
  271. IN DWORD dwHighestVersion,
  272. IN DWORD dwLowestVersion
  273. )
  274. {
  275. DWORD dwStatus = ERROR_SUCCESS;
  276. PNM_NODE pNmNode = NULL;
  277. ClRtlLogPrint(LOG_NOISE,
  278. "[NM] NmpValidateNodeVersion: Node=%1!ws!, HighestVersion=0x%2!08lx!, LowestVersion=0x%3!08lx!\r\n",
  279. NodeId, dwHighestVersion, dwLowestVersion);
  280. //acquire the NmpLocks, we will be examining the node structure for
  281. // the joiner node
  282. NmpAcquireLock();
  283. pNmNode = OmReferenceObjectById(ObjectTypeNode, NodeId);
  284. if (!pNmNode)
  285. {
  286. dwStatus = ERROR_CLUSTER_NODE_NOT_MEMBER;
  287. goto FnExit;
  288. }
  289. if ((pNmNode->HighestVersion != dwHighestVersion) ||
  290. (pNmNode->LowestVersion != dwLowestVersion))
  291. {
  292. dwStatus = ERROR_REVISION_MISMATCH;
  293. goto FnExit;
  294. }
  295. FnExit:
  296. if (pNmNode) OmDereferenceObject(pNmNode);
  297. ClRtlLogPrint(LOG_NOISE, "[NM] NmpValidateNodeVersion: returns %1!u!\r\n",
  298. dwStatus);
  299. NmpReleaseLock();
  300. return(dwStatus);
  301. }
  302. /****
  303. @func DWORD | NmpFormFixupNodeVersion| This may be called by a node
  304. when it is forming a cluster to fix the registry reflect its
  305. correct version.
  306. @parm IN LPCWSTR| NodeId | The Id of the node that is trying to join.
  307. @parm IN DWORD | dwHighestVersion | The highest version of the cluster
  308. s/w running on this code.
  309. @parm IN DWORD | dwLowestVersion | The lowest version of the cluster
  310. s/w running on this node.
  311. @rdesc Returns ERROR_SUCCESS on success or a win32 error code on failure.
  312. @comm If on a form, there is a mismatch between the versions of the
  313. cluster s/w and what is recorded as the version in the cluster
  314. database, the forming node checks to see if the version of
  315. its current s/w is compatible with the operational version of the
  316. cluster. If so, it resets the registry to reflect the correct
  317. version. Else, the form is aborted.
  318. @xref <f NmpIsNodeVersionAllowed>
  319. ****/
  320. DWORD NmpFormFixupNodeVersion(
  321. IN LPCWSTR NodeId,
  322. IN DWORD dwHighestVersion,
  323. IN DWORD dwLowestVersion
  324. )
  325. {
  326. DWORD dwStatus = ERROR_SUCCESS;
  327. PNM_NODE pNmNode = NULL;
  328. HDMKEY hNodeKey = NULL;
  329. //acquire the NmpLocks, we will be fixing up the node structure for
  330. // the joiner node
  331. NmpAcquireLock();
  332. ClRtlLogPrint(LOG_NOISE,
  333. "[NM] NmpFormFixupNodeVersion: Node=%1!ws! to HighestVer=0x%2!08lx!, LowestVer=0x%3!08lx!\r\n",
  334. NodeId, dwHighestVersion, dwLowestVersion);
  335. pNmNode = OmReferenceObjectById(ObjectTypeNode, NodeId);
  336. if (!pNmNode)
  337. {
  338. dwStatus = ERROR_CLUSTER_NODE_NOT_MEMBER;
  339. goto FnExit;
  340. }
  341. hNodeKey = DmOpenKey(DmNodesKey, NodeId, KEY_WRITE);
  342. if (hNodeKey == NULL)
  343. {
  344. dwStatus = GetLastError();
  345. ClRtlLogPrint(LOG_CRITICAL,
  346. "[NM] NmpFormFixupNodeVersion: Failed to open node key, status %1!u!\n",
  347. dwStatus);
  348. CL_LOGFAILURE(dwStatus);
  349. goto FnExit;
  350. }
  351. //set the node's highest version
  352. dwStatus = DmSetValue(hNodeKey, CLUSREG_NAME_NODE_HIGHEST_VERSION,
  353. REG_DWORD, (LPBYTE)&dwHighestVersion, sizeof(DWORD));
  354. if (dwStatus != ERROR_SUCCESS)
  355. {
  356. ClRtlLogPrint(LOG_CRITICAL,
  357. "[NM] NmpFormFixupNodeVersion: Failed to set the highest version\r\n");
  358. CL_LOGFAILURE(dwStatus);
  359. goto FnExit;
  360. }
  361. //set the node's lowest version
  362. dwStatus = DmSetValue(hNodeKey, CLUSREG_NAME_NODE_LOWEST_VERSION,
  363. REG_DWORD, (LPBYTE)&dwLowestVersion, sizeof(DWORD));
  364. if (dwStatus != ERROR_SUCCESS)
  365. {
  366. ClRtlLogPrint(LOG_CRITICAL,
  367. "[NM] NmpFormFixupNodeVersion: Failed to set the lowest version\r\n");
  368. CL_LOGFAILURE(dwStatus);
  369. goto FnExit;
  370. }
  371. pNmNode->HighestVersion = dwHighestVersion;
  372. pNmNode->LowestVersion = dwLowestVersion;
  373. FnExit:
  374. NmpReleaseLock();
  375. if (pNmNode)
  376. OmDereferenceObject(pNmNode);
  377. if (hNodeKey != NULL)
  378. DmCloseKey(hNodeKey);
  379. return(dwStatus);
  380. }
  381. /****
  382. @func DWORD | NmpJoinFixupNodeVersion| This may be called by a node
  383. when it is forming a cluster to fix the registry reflect its
  384. correct version.
  385. @parm IN LPCWSTR| NodeId | The Id of the node that is trying to join.
  386. @parm IN DWORD | dwHighestVersion | The highest version of this cluster
  387. s/w running on this code.
  388. @parm IN DWORD | dwLowestVersion | The lowest version of the cluster
  389. s/w running on this node.
  390. @rdesc Returns ERROR_SUCCESS on success or a win32 error code on failure.
  391. @comm If on a form, their is a mismatch between the versions of the
  392. cluster s/w and what is recorded as the version in the cluster
  393. database, the forming node checks to see if the version of
  394. its current s/w compatible with the operational version of the
  395. cluster. If so, it resets the registry to reflect the correct
  396. version. Else, the form is aborted.
  397. @xref <f NmpIsNodeVersionAllowed>
  398. ****/
  399. DWORD NmpJoinFixupNodeVersion(
  400. IN HLOCALXSACTION hXsaction,
  401. IN LPCWSTR szNodeId,
  402. IN DWORD dwHighestVersion,
  403. IN DWORD dwLowestVersion
  404. )
  405. {
  406. DWORD dwStatus = ERROR_SUCCESS;
  407. PNM_NODE pNmNode = NULL;
  408. HDMKEY hNodeKey = NULL;
  409. //acquire the NmpLocks, we will be fixing up the node structure for
  410. // the joiner node
  411. NmpAcquireLock();
  412. ClRtlLogPrint(LOG_NOISE,
  413. "[NM] NmpJoinFixupNodeVersion: Node=%1!ws! to HighestVer=0x%2!08lx!, LowestVer=0x%3!08lx!\r\n",
  414. szNodeId, dwHighestVersion, dwLowestVersion);
  415. pNmNode = OmReferenceObjectById(ObjectTypeNode, szNodeId);
  416. if (!pNmNode)
  417. {
  418. dwStatus = ERROR_CLUSTER_NODE_NOT_MEMBER;
  419. goto FnExit;
  420. }
  421. hNodeKey = DmOpenKey(DmNodesKey, szNodeId, KEY_WRITE);
  422. if (hNodeKey == NULL)
  423. {
  424. dwStatus = GetLastError();
  425. ClRtlLogPrint(LOG_CRITICAL,
  426. "[NM] NmpJoinFixupNodeVersion: Failed to open node key, status %1!u!\n",
  427. dwStatus);
  428. CL_LOGFAILURE(dwStatus);
  429. goto FnExit;
  430. }
  431. //set the node's highest version
  432. dwStatus = DmLocalSetValue(
  433. hXsaction,
  434. hNodeKey,
  435. CLUSREG_NAME_NODE_HIGHEST_VERSION,
  436. REG_DWORD,
  437. (LPBYTE)&dwHighestVersion,
  438. sizeof(DWORD)
  439. );
  440. if (dwStatus != ERROR_SUCCESS)
  441. {
  442. ClRtlLogPrint(LOG_CRITICAL,
  443. "[NM] NmpJoinFixupNodeVersion: Failed to set the highest version\r\n"
  444. );
  445. CL_LOGFAILURE(dwStatus);
  446. goto FnExit;
  447. }
  448. //set the node's lowest version
  449. dwStatus = DmLocalSetValue(
  450. hXsaction,
  451. hNodeKey,
  452. CLUSREG_NAME_NODE_LOWEST_VERSION,
  453. REG_DWORD,
  454. (LPBYTE)&dwLowestVersion,
  455. sizeof(DWORD)
  456. );
  457. if (dwStatus != ERROR_SUCCESS)
  458. {
  459. ClRtlLogPrint(LOG_CRITICAL,
  460. "[NM] NmpJoinFixupNodeVersion: Failed to set the lowest version\r\n"
  461. );
  462. CL_LOGFAILURE(dwStatus);
  463. goto FnExit;
  464. }
  465. //if written to the registry successfully, update the in-memory structures
  466. pNmNode->HighestVersion = dwHighestVersion;
  467. pNmNode->LowestVersion = dwLowestVersion;
  468. if (dwStatus == ERROR_SUCCESS)
  469. {
  470. ClusterEvent(CLUSTER_EVENT_NODE_PROPERTY_CHANGE, pNmNode);
  471. }
  472. FnExit:
  473. NmpReleaseLock();
  474. if (pNmNode)
  475. OmDereferenceObject(pNmNode);
  476. if (hNodeKey != NULL)
  477. DmCloseKey(hNodeKey);
  478. return(dwStatus);
  479. }
  480. /****
  481. @func HLOG | NmpIsNodeVersionAllowed| This is called at join time
  482. (not setup join) e sponsor validates if a joiner
  483. should be allowed to join a cluster at this time. In a mixed
  484. mode cluster, a node may not be able to join a cluster if another
  485. node that is two versions away is already a part of the cluster.
  486. @parm IN DWORD | dwExcludeNodeId | The node Id to exclude while
  487. evaluating the cluster operational version.
  488. @parm IN DWORD | NodeHighestVersion | The highest version with which
  489. this node can communicate.
  490. @parm IN DWORD | NodeLowestVersion | The lowest version with which this
  491. node can communicate.
  492. @parm IN BOOL |bJoin| If this is being invoked at join or form time.
  493. @rdesc Returns ERROR_SUCCESS on success or a win32 error code on failure.
  494. @comm This function is called when a node requests a sponsor to allow
  495. it to join a cluster.
  496. @xref <>
  497. ****/
  498. DWORD NmpIsNodeVersionAllowed(
  499. IN DWORD dwExcludeNodeId,
  500. IN DWORD dwNodeHighestVersion,
  501. IN DWORD dwNodeLowestVersion,
  502. IN BOOL bJoin
  503. )
  504. {
  505. DWORD dwStatus = ERROR_SUCCESS;
  506. DWORD ClusterHighestVersion;
  507. DWORD ClusterLowestVersion;
  508. PLIST_ENTRY pListEntry;
  509. DWORD dwCnt;
  510. PNM_NODE pNmNode;
  511. ClRtlLogPrint(LOG_NOISE,
  512. "[NM] NmpIsNodeVersionAllowed: Entry ExcludeNodeId=%1!u! HighestVersion=0x%2!08lx! LowestVersion=0x%3!08lx!\r\n",
  513. dwExcludeNodeId, dwNodeHighestVersion, dwNodeLowestVersion);
  514. //acquire the NmpLocks, we will be examining the node structures
  515. NmpAcquireLock();
  516. //if NoVersionCheckOption is true
  517. if (CsNoVersionCheck)
  518. goto FnExit;
  519. //if this is a single node cluster, and this is being called at form
  520. //the count of nodes is zero.
  521. //this will happen when the registry versions dont match with
  522. //cluster service exe version numbers and we need to allow the single
  523. //node to form
  524. for (dwCnt=0, pListEntry = NmpNodeList.Flink;
  525. pListEntry != &NmpNodeList; pListEntry = pListEntry->Flink )
  526. {
  527. pNmNode = CONTAINING_RECORD(pListEntry, NM_NODE, Linkage);
  528. if (NmGetNodeId(pNmNode) == dwExcludeNodeId)
  529. continue;
  530. dwCnt++;
  531. }
  532. if (!dwCnt)
  533. {
  534. //allow the node to form
  535. goto FnExit;
  536. }
  537. dwStatus = NmpCalcClusterVersion(
  538. dwExcludeNodeId,
  539. &ClusterHighestVersion,
  540. &ClusterLowestVersion
  541. );
  542. if (dwStatus != ERROR_SUCCESS)
  543. {
  544. goto FnExit;
  545. }
  546. //if the node is forming
  547. if (!bJoin)
  548. {
  549. DWORD dwMinorVersion = 0x00000000;
  550. PNM_NODE pFormingNode = NULL;
  551. DWORD dwMaxHighestVersion = 0x00000000;
  552. for (pListEntry = NmpNodeList.Flink; pListEntry != &NmpNodeList;
  553. pListEntry = pListEntry->Flink )
  554. {
  555. pNmNode = CONTAINING_RECORD(pListEntry, NM_NODE, Linkage);
  556. if (NmGetNodeId(pNmNode) == dwExcludeNodeId)
  557. {
  558. pFormingNode = pNmNode;
  559. continue;
  560. }
  561. dwMaxHighestVersion = max( dwMaxHighestVersion, pNmNode->HighestVersion);
  562. if (CLUSTER_GET_MAJOR_VERSION(pNmNode->HighestVersion) ==
  563. CLUSTER_GET_MAJOR_VERSION(dwNodeHighestVersion))
  564. {
  565. //the minor version to check is the maximum of the
  566. //build numbers amongst the nodes with the same major
  567. //version
  568. dwMinorVersion = max(dwMinorVersion,
  569. CLUSTER_GET_MINOR_VERSION(pNmNode->HighestVersion));
  570. }
  571. }
  572. //on the form path, the forming node must be passed in as the node to
  573. //exclude
  574. if (!pFormingNode)
  575. {
  576. ClRtlLogPrint(LOG_UNUSUAL,
  577. "[NM] NmpIsNodeVersionAllowed: Form requested without excluding the forming node\r\n");
  578. dwStatus = ERROR_CLUSTER_INCOMPATIBLE_VERSIONS;
  579. goto FnExit;
  580. }
  581. //dont allow a node to form unless its minor(or build number)
  582. //is greater than or equal to all other nodes with the same
  583. //major number in the cluster
  584. //this is to prevent a lower build on a node to regress the cluster version
  585. // if other nodes have already upgraded to higher builds
  586. if ((dwMinorVersion != 0) &&
  587. (CLUSTER_GET_MINOR_VERSION(dwNodeHighestVersion) < dwMinorVersion))
  588. {
  589. ClRtlLogPrint(LOG_UNUSUAL,
  590. "[NM] NmpIsNodeVersionAllowed: Minor Version of forming node is lower\r\n");
  591. dwStatus = ERROR_CLUSTER_INCOMPATIBLE_VERSIONS;
  592. goto FnExit;
  593. }
  594. else
  595. {
  596. //there is no other node with the same major version in the cluster
  597. //or the forming node's minor version is higher than those of other
  598. //nodes with the same major version in which case it may form
  599. //Note: Do not use the CsUpgrade variable on the joining path
  600. //since it is local and this function is invoked from rpc calls
  601. //on join and that can have an unintended effect
  602. if (!CsUpgrade)
  603. {
  604. //if the service is not being upgraded the value in the registry
  605. //should be uptodate with the version of the executable
  606. if ((pFormingNode->HighestVersion != dwNodeHighestVersion) ||
  607. (pFormingNode->LowestVersion != dwNodeLowestVersion))
  608. {
  609. //this is not an upgrade
  610. //somebody has just copied a different version of service
  611. //without going through a proper upgrade
  612. //dont allow that
  613. ClRtlLogPrint(LOG_UNUSUAL,
  614. "[NM] NmpIsNodeVersionAllowed: Copied binary without proper upgrade??\r\n");
  615. dwStatus = ERROR_CLUSTER_INCOMPATIBLE_VERSIONS;
  616. goto FnExit;
  617. }
  618. }
  619. if (dwNodeHighestVersion >= dwMaxHighestVersion)
  620. {
  621. //allow the node to form if its highest version number is greater
  622. //than or equal to that of the node with the max highest version
  623. //irrespective of whether it is an upgrade or not..this will
  624. //allow a node that has just double upgraded and stopped and restarted
  625. //to be able to restart
  626. ClRtlLogPrint(LOG_UNUSUAL,
  627. "[NM] NmpIsNodeVersionAllowed: Allow a node that has double upgraded or stopped and restarted to form\r\n");
  628. goto FnExit;
  629. }
  630. //else we fall through to the regular check
  631. }
  632. }
  633. //if the joiners lowest version is equal the clusters highest
  634. //For instance 3/2, 2/1 and 4/3 can all join 3/2
  635. if ((dwNodeHighestVersion == ClusterHighestVersion) ||
  636. (dwNodeHighestVersion == ClusterLowestVersion) ||
  637. (dwNodeLowestVersion == ClusterHighestVersion))
  638. {
  639. PNM_NODE pNmNode= NULL;
  640. DWORD dwMinorVersion;
  641. //since the version numbers include build number as the minor part
  642. // and we disallow a node from operating with a cluster if its
  643. // major number is equal but its minor number is different from
  644. // any of the nodes in the cluster.
  645. // The CsClusterHighestVersion doesnt encapsulate this since it just
  646. // remembers the highest version that the cluster as a whole can talk
  647. // to.
  648. // E.g 1.
  649. // 3.2003 should be able to join a cluster with nodes
  650. // 3.2002(not running and not upgraded as yet but a part of the cluster)and
  651. // 3.2003(running).
  652. // E.g 2
  653. // 3.2002 will not be able to join a cluster with nodes 3.2003(running)and
  654. // 3.2002 (not running but a part of the cluster)
  655. // E.g 3.
  656. // 3.2003 will not able to join a cluster with nodes 3.2002(running) and
  657. // 3.2002(running)
  658. dwMinorVersion = 0x00000000;
  659. for (pListEntry = NmpNodeList.Flink; pListEntry != &NmpNodeList;
  660. pListEntry = pListEntry->Flink )
  661. {
  662. pNmNode = CONTAINING_RECORD(pListEntry, NM_NODE, Linkage);
  663. if (NmGetNodeId(pNmNode) == dwExcludeNodeId)
  664. continue;
  665. if (CLUSTER_GET_MAJOR_VERSION(pNmNode->HighestVersion) ==
  666. CLUSTER_GET_MAJOR_VERSION(dwNodeHighestVersion))
  667. {
  668. //the minor version to check is the maximum of the
  669. //build numbers amongst the nodes with the same major
  670. //version
  671. dwMinorVersion = max(dwMinorVersion,
  672. CLUSTER_GET_MINOR_VERSION(pNmNode->HighestVersion));
  673. }
  674. }
  675. // if the joining node's build number is the same as max of build
  676. //number of all nodes within the cluster with the same major version
  677. //allow it to participate in this cluster, else dont allow it to participate in this cluster
  678. //take care of a single node case by checking the minor number against
  679. //0
  680. if ((dwMinorVersion != 0) &&
  681. (CLUSTER_GET_MINOR_VERSION(dwNodeHighestVersion) != dwMinorVersion))
  682. {
  683. dwStatus = ERROR_CLUSTER_INCOMPATIBLE_VERSIONS;
  684. }
  685. }
  686. else
  687. {
  688. dwStatus = ERROR_CLUSTER_INCOMPATIBLE_VERSIONS;
  689. }
  690. FnExit:
  691. NmpReleaseLock();
  692. ClRtlLogPrint(LOG_NOISE,
  693. "[NM] NmpIsNodeVersionAllowed: Exit, Status=%1!u!\r\n",
  694. dwStatus);
  695. return(dwStatus);
  696. }
  697. /****
  698. @func HLOG | NmpCalcClusterVersion| This is called to calculate the
  699. operational cluster version.
  700. @parm IN DWORD | dwExcludeNodeId | The node Id to exclude while evaluating
  701. the cluster operational version.
  702. @parm OUT LPDWORD | pdwClusterHighestVersion | The highest version with which this node
  703. can communicate.
  704. @parm IN LPDWORD | pdwClusterLowestVersion | The lowest version with which this node can
  705. communicate.
  706. @rdesc Returns ERROR_SUCCESS on success or a win32 error code on failure.
  707. @comm This function must be called with the NmpLock held.
  708. @xref <f NmpResetClusterVersion> <f NmpIsNodeVersionAllowed>
  709. ****/
  710. DWORD NmpCalcClusterVersion(
  711. IN DWORD dwExcludeNodeId,
  712. OUT LPDWORD pdwClusterHighestVersion,
  713. OUT LPDWORD pdwClusterLowestVersion
  714. )
  715. {
  716. WCHAR Buffer[4];
  717. PNM_NODE pExcludeNode=NULL;
  718. PNM_NODE pNmNode;
  719. DWORD dwStatus = ERROR_SUCCESS;
  720. PLIST_ENTRY pListEntry;
  721. DWORD dwCnt = 0;
  722. DWORD dwMaxHighestVersion = 0x00000000;
  723. PNM_NODE pNmNodeWithHighestVersion;
  724. //initialize the values such that min/max do the right thing
  725. *pdwClusterHighestVersion = 0xFFFFFFFF;
  726. *pdwClusterLowestVersion = 0x00000000;
  727. if (dwExcludeNodeId != ClusterInvalidNodeId)
  728. {
  729. wsprintfW(Buffer, L"%d", dwExcludeNodeId);
  730. pExcludeNode = OmReferenceObjectById(ObjectTypeNode, Buffer);
  731. if (!pExcludeNode)
  732. {
  733. dwStatus = ERROR_INVALID_PARAMETER;
  734. ClRtlLogPrint(LOG_UNUSUAL,
  735. "[NM] NmpCalcClusterVersion :Node=%1!ws! to be excluded not found\r\n",
  736. Buffer);
  737. goto FnExit;
  738. }
  739. }
  740. for ( pListEntry = NmpNodeList.Flink;
  741. pListEntry != &NmpNodeList;
  742. pListEntry = pListEntry->Flink )
  743. {
  744. pNmNode = CONTAINING_RECORD(pListEntry, NM_NODE, Linkage);
  745. if ((pExcludeNode) && (pExcludeNode->NodeId == pNmNode->NodeId))
  746. continue;
  747. //Actually to fix upgrade scenarios, we must fix the cluster
  748. //version such that the node with the highest minor version
  749. //is able to form/join but others arent
  750. // This is needed for multinode clusters
  751. if (CLUSTER_GET_MAJOR_VERSION(pNmNode->HighestVersion) ==
  752. CLUSTER_GET_MAJOR_VERSION(*pdwClusterHighestVersion))
  753. {
  754. if (CLUSTER_GET_MINOR_VERSION(pNmNode->HighestVersion) >
  755. CLUSTER_GET_MINOR_VERSION(*pdwClusterHighestVersion))
  756. {
  757. *pdwClusterHighestVersion = pNmNode->HighestVersion;
  758. }
  759. }
  760. else
  761. {
  762. *pdwClusterHighestVersion = min(
  763. *pdwClusterHighestVersion,
  764. pNmNode->HighestVersion
  765. );
  766. }
  767. *pdwClusterLowestVersion = max(
  768. *pdwClusterLowestVersion,
  769. pNmNode->LowestVersion
  770. );
  771. dwCnt++;
  772. if (pNmNode->HighestVersion > dwMaxHighestVersion)
  773. {
  774. dwMaxHighestVersion = pNmNode->HighestVersion;
  775. pNmNodeWithHighestVersion = pNmNode;
  776. }
  777. }
  778. //SS: if there is a node which skipped a build while upgrading
  779. //the regular cluster version calculations dont make sense
  780. if (CLUSTER_GET_MAJOR_VERSION(*pdwClusterHighestVersion) < CLUSTER_GET_MAJOR_VERSION
  781. (*pdwClusterLowestVersion))
  782. {
  783. ClRtlLogPrint(LOG_NOISE,
  784. "[NM] NmpCalcClusterVersion: One of the nodes skipped a build on upgrade\r\n");
  785. //We will pull the cluster version to be the highest of all nodes
  786. //except the excluded node
  787. *pdwClusterHighestVersion = dwMaxHighestVersion;
  788. *pdwClusterLowestVersion = pNmNodeWithHighestVersion->LowestVersion;
  789. }
  790. if (dwCnt == 0)
  791. {
  792. ClRtlLogPrint(LOG_NOISE,
  793. "[NM] NmpCalcClusterVersion: Single node version. Setting cluster version to node version\r\n"
  794. );
  795. //single node cluster, even though the we were requested to
  796. //exclude this node, the cluster version must be calculated
  797. //using that node's version
  798. *pdwClusterHighestVersion = pExcludeNode->HighestVersion;
  799. *pdwClusterLowestVersion = pExcludeNode->LowestVersion;
  800. }
  801. CL_ASSERT(*pdwClusterHighestVersion != 0xFFFFFFFF);
  802. CL_ASSERT(*pdwClusterLowestVersion != 0x00000000);
  803. FnExit:
  804. ClRtlLogPrint(LOG_NOISE,
  805. "[NM] NmpCalcClusterVersion: status = %1!u! ClusHighestVer=0x%2!08lx!, ClusLowestVer=0x%3!08lx!\r\n",
  806. dwStatus, *pdwClusterHighestVersion, *pdwClusterLowestVersion);
  807. if (pExcludeNode) OmDereferenceObject(pExcludeNode);
  808. return(dwStatus);
  809. }
  810. VOID
  811. NmpProcessClusterVersionChange(
  812. VOID
  813. )
  814. /*++
  815. Notes:
  816. Called with the NmpLock held.
  817. --*/
  818. {
  819. DWORD status;
  820. LPWSTR szClusterName=NULL;
  821. DWORD dwSize=0;
  822. NmpMulticastProcessClusterVersionChange();
  823. //rjain: issue CLUSTER_EVENT_PROPERTY_CHANGE to propagate new
  824. //cluster version info
  825. DmQuerySz( DmClusterParametersKey,
  826. CLUSREG_NAME_CLUS_NAME,
  827. &szClusterName,
  828. &dwSize,
  829. &dwSize);
  830. if(szClusterName)
  831. ClusterEventEx(
  832. CLUSTER_EVENT_PROPERTY_CHANGE,
  833. EP_FREE_CONTEXT,
  834. szClusterName
  835. );
  836. return;
  837. } // NmpProcessClusterVersionChange
  838. /****
  839. @func DWORD | NmpBuildVersionInfo| Its a callback function used by
  840. NmPerformFixups to build a property list of the Major Version,
  841. Minor version, Build Number and CSDVersionInfo, This propertylist
  842. is used by NmUpdatePerformFixups Update type to store this info
  843. in registry.
  844. @parm IN DWORD | dwFixupType| JoinFixup or FormFixup
  845. @parm OUT PVOID* | ppPropertyList| Pointer to the pointer to the property list
  846. @parm OUT LPDWORD | pdwProperyListSize | Pointer to the property list size
  847. @param OUT LPWSTR* | pszKeyName. The name of registry key for which this
  848. property list is being constructed.
  849. @rdesc Returns a result code. ERROR_SUCCESS on success.
  850. @xref <f NmpUpdatePerformFixups2>
  851. ****/
  852. DWORD NmpBuildVersionInfo(
  853. IN DWORD dwFixUpType,
  854. OUT PVOID * ppPropertyList,
  855. OUT LPDWORD pdwPropertyListSize,
  856. OUT LPWSTR * pszKeyName
  857. )
  858. {
  859. DWORD dwStatus=ERROR_SUCCESS;
  860. LPBYTE pInParams=NULL;
  861. DWORD Required,Returned;
  862. HDMKEY hdmKey;
  863. DWORD dwTemp;
  864. CLUSTERVERSIONINFO ClusterVersionInfo;
  865. LPWSTR szTemp=NULL;
  866. *ppPropertyList = NULL;
  867. *pdwPropertyListSize = 0;
  868. //check we if need to send this information
  869. dwTemp=(lstrlenW(CLUSREG_KEYNAME_NODES) + lstrlenW(L"\\")+lstrlenW(NmLocalNodeIdString)+1)*sizeof(WCHAR);
  870. *pszKeyName=(LPWSTR)LocalAlloc(LMEM_FIXED,dwTemp);
  871. if(*pszKeyName==NULL)
  872. {
  873. dwStatus =GetLastError();
  874. goto FnExit;
  875. }
  876. lstrcpyW(*pszKeyName,CLUSREG_KEYNAME_NODES);
  877. lstrcatW(*pszKeyName,L"\\");
  878. lstrcatW(*pszKeyName,NmLocalNodeIdString);
  879. // Build the parameter list
  880. pInParams=(LPBYTE)LocalAlloc(LMEM_FIXED,4*sizeof(DWORD)+sizeof(LPWSTR));
  881. if(pInParams==NULL)
  882. {
  883. dwStatus =GetLastError();
  884. goto FnExit;
  885. }
  886. CsGetClusterVersionInfo(&ClusterVersionInfo);
  887. dwTemp=(DWORD)ClusterVersionInfo.MajorVersion;
  888. CopyMemory(pInParams,&dwTemp,sizeof(DWORD));
  889. dwTemp=(DWORD)ClusterVersionInfo.MinorVersion;
  890. CopyMemory(pInParams+sizeof(DWORD),&dwTemp,sizeof(DWORD));
  891. dwTemp=(DWORD)ClusterVersionInfo.BuildNumber;
  892. CopyMemory(pInParams+2*sizeof(DWORD),&dwTemp,sizeof(DWORD));
  893. if(ClusterVersionInfo.szCSDVersion==NULL)
  894. szTemp=NULL;
  895. else
  896. {
  897. szTemp=(LPWSTR)LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT,(lstrlenW(ClusterVersionInfo.szCSDVersion) +1)*sizeof(WCHAR));
  898. if (szTemp==NULL)
  899. {
  900. dwStatus=GetLastError();
  901. goto FnExit;
  902. }
  903. lstrcpyW(szTemp,ClusterVersionInfo.szCSDVersion);
  904. szTemp[lstrlenW(ClusterVersionInfo.szCSDVersion)]=L'\0';
  905. }
  906. CopyMemory(pInParams+3*sizeof(DWORD),&szTemp,sizeof(LPWSTR));
  907. //copy the suite information
  908. CopyMemory(pInParams+3*sizeof(DWORD)+sizeof(LPWSTR*),
  909. &CsMyProductSuite, sizeof(DWORD));
  910. Required=sizeof(DWORD);
  911. AllocMem:
  912. *ppPropertyList=(LPBYTE)LocalAlloc(LMEM_FIXED, Required);
  913. if(*ppPropertyList==NULL)
  914. {
  915. dwStatus=GetLastError();
  916. goto FnExit;
  917. }
  918. *pdwPropertyListSize=Required;
  919. dwStatus = ClRtlPropertyListFromParameterBlock(
  920. NmFixupVersionInfo,
  921. *ppPropertyList,
  922. pdwPropertyListSize,
  923. (LPBYTE)pInParams,
  924. &Returned,
  925. &Required
  926. );
  927. *pdwPropertyListSize=Returned;
  928. if (dwStatus==ERROR_MORE_DATA)
  929. {
  930. LocalFree(*ppPropertyList);
  931. *ppPropertyList=NULL;
  932. goto AllocMem;
  933. }
  934. else
  935. if (dwStatus != ERROR_SUCCESS)
  936. {
  937. ClRtlLogPrint(LOG_CRITICAL,"[NM] NmBuildVersionInfo - error = %1!u!\r\n",dwStatus);
  938. goto FnExit;
  939. }
  940. FnExit:
  941. // Cleanup
  942. if (szTemp)
  943. LocalFree(szTemp);
  944. if(pInParams)
  945. LocalFree(pInParams);
  946. return dwStatus;
  947. }//NmpBuildVersionInfo
  948. /****
  949. @func HLOG | NmpCalcClusterNodeLimit|This is called to calculate the
  950. operational cluster node limit.
  951. @rdesc Returns ERROR_SUCCESS on success or a win32 error code on failure.
  952. @comm This acquires/releases NmpLock.
  953. @xref <f NmpResetClusterVersion> <f NmpIsNodeVersionAllowed>
  954. ****/
  955. DWORD NmpCalcClusterNodeLimit(
  956. )
  957. {
  958. PNM_NODE pNmNode;
  959. DWORD dwStatus = ERROR_SUCCESS;
  960. PLIST_ENTRY pListEntry;
  961. //acquire the lock, we are going to be messing with the operational
  962. //versions for the cluster
  963. NmpAcquireLock();
  964. CsClusterNodeLimit = NmMaxNodeId;
  965. for ( pListEntry = NmpNodeList.Flink;
  966. pListEntry != &NmpNodeList;
  967. pListEntry = pListEntry->Flink )
  968. {
  969. pNmNode = CONTAINING_RECORD(pListEntry, NM_NODE, Linkage);
  970. CsClusterNodeLimit = min(
  971. CsClusterNodeLimit,
  972. ClRtlGetDefaultNodeLimit(
  973. pNmNode->ProductSuite
  974. )
  975. );
  976. }
  977. ClRtlLogPrint(LOG_NOISE,
  978. "[NM] Calculated cluster node limit = %1!u!\r\n",
  979. CsClusterNodeLimit);
  980. NmpReleaseLock();
  981. return (dwStatus);
  982. }
  983. /****
  984. @func VOID| NmpResetClusterNodeLimit| An operational node limit
  985. on the number of nodes that can join this cluster is maintained.
  986. @rdesc Returns ERROR_SUCCESS on success or a win32 error code on failure.
  987. @comm This function is called when a node forms a cluster(to initialize
  988. the operational version) OR when a node joins a cluster (to
  989. initialize its version) OR when a node is ejected from a
  990. cluster(to recalculate the clusterversion).
  991. @xref <>
  992. ****/
  993. VOID
  994. NmpResetClusterNodeLimit(
  995. )
  996. {
  997. NmpCalcClusterNodeLimit();
  998. }