///////////////////////////////////////////////////////////////////////////// // // Copyright (c) 1996-2000 Microsoft Corporation // // Module Name: // Node.cpp // // Description: // Implementation of the CClusNode class. // // Maintained By: // David Potter (davidp) May 3, 1996 // // Revision History: // // Notes: // ///////////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "CluAdmin.h" #include "ConstDef.h" #include "Node.h" #include "ClusItem.inl" #include "NodeProp.h" #include "ExcOper.h" #include "TraceTag.h" #include "Cluster.h" #include "CASvc.h" #include "ResType.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // Global Variables ///////////////////////////////////////////////////////////////////////////// #ifdef _DEBUG CTraceTag g_tagNode(_T("Document"), _T("NODE"), 0); CTraceTag g_tagNodeDrag(_T("Drag&Drop"), _T("NODE DRAG"), 0); CTraceTag g_tagNodeNotify(_T("Notify"), _T("NODE NOTIFY"), 0); CTraceTag g_tagNodeRegNotify(_T("Notify"), _T("NODE REG NOTIFY"), 0); #endif ///////////////////////////////////////////////////////////////////////////// // CClusterNode ///////////////////////////////////////////////////////////////////////////// IMPLEMENT_DYNCREATE(CClusterNode, CClusterItem) ///////////////////////////////////////////////////////////////////////////// // Message Maps ///////////////////////////////////////////////////////////////////////////// BEGIN_MESSAGE_MAP(CClusterNode, CClusterItem) //{{AFX_MSG_MAP(CClusterNode) ON_UPDATE_COMMAND_UI(ID_FILE_PAUSE_NODE, OnUpdatePauseNode) ON_UPDATE_COMMAND_UI(ID_FILE_RESUME_NODE, OnUpdateResumeNode) ON_UPDATE_COMMAND_UI(ID_FILE_EVICT_NODE, OnUpdateEvictNode) ON_UPDATE_COMMAND_UI(ID_FILE_START_SERVICE, OnUpdateStartService) ON_UPDATE_COMMAND_UI(ID_FILE_STOP_SERVICE, OnUpdateStopService) ON_UPDATE_COMMAND_UI(ID_FILE_PROPERTIES, OnUpdateProperties) ON_COMMAND(ID_FILE_PAUSE_NODE, OnCmdPauseNode) ON_COMMAND(ID_FILE_RESUME_NODE, OnCmdResumeNode) ON_COMMAND(ID_FILE_EVICT_NODE, OnCmdEvictNode) ON_COMMAND(ID_FILE_START_SERVICE, OnCmdStartService) ON_COMMAND(ID_FILE_STOP_SERVICE, OnCmdStopService) //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterNode::CClusterNode // // Description: // Default constructor. // // Arguments: // None. // // Return Values: // None. // //-- ///////////////////////////////////////////////////////////////////////////// CClusterNode::CClusterNode(void) : CClusterItem(NULL, IDS_ITEMTYPE_NODE) { m_idmPopupMenu = IDM_NODE_POPUP; m_hnode = NULL; m_nNodeHighestVersion = 0; m_nNodeLowestVersion = 0; m_nMajorVersion = 0; m_nMinorVersion = 0; m_nBuildNumber = 0; m_plpcigrpOnline = NULL; m_plpciresOnline = NULL; m_plpciNetInterfaces = NULL; // Set the object type image. m_iimgObjectType = GetClusterAdminApp()->Iimg(IMGLI_NODE); // Setup the property array. { m_rgProps[epropName].Set(CLUSREG_NAME_NODE_NAME, m_strName, m_strName); m_rgProps[epropDescription].Set(CLUSREG_NAME_NODE_DESC, m_strDescription, m_strDescription); m_rgProps[epropNodeHighestVersion].Set(CLUSREG_NAME_NODE_HIGHEST_VERSION, m_nNodeHighestVersion, m_nNodeHighestVersion); m_rgProps[epropNodeLowestVersion].Set(CLUSREG_NAME_NODE_LOWEST_VERSION, m_nNodeLowestVersion, m_nNodeLowestVersion); m_rgProps[epropMajorVersion].Set(CLUSREG_NAME_NODE_MAJOR_VERSION, m_nMajorVersion, m_nMajorVersion); m_rgProps[epropMinorVersion].Set(CLUSREG_NAME_NODE_MINOR_VERSION, m_nMinorVersion, m_nMinorVersion); m_rgProps[epropBuildNumber].Set(CLUSREG_NAME_NODE_BUILD_NUMBER, m_nBuildNumber, m_nBuildNumber); m_rgProps[epropCSDVersion].Set(CLUSREG_NAME_NODE_CSDVERSION, m_strCSDVersion, m_strCSDVersion); } // Setup the property array // To keep the application running as long as an OLE automation // object is active, the constructor calls AfxOleLockApp. // AfxOleLockApp(); } //*** CClusterNode::CClusterNode() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterNode::~CClusterNode // // Description: // Destructor. // // Arguments: // None. // // Return Values: // None. // //-- ///////////////////////////////////////////////////////////////////////////// CClusterNode::~CClusterNode(void) { delete m_plpcigrpOnline; delete m_plpciresOnline; delete m_plpciNetInterfaces; // Close the node. if (Hnode() != NULL) CloseClusterNode(Hnode()); // To terminate the application when all objects created with // with OLE automation, the destructor calls AfxOleUnlockApp. // AfxOleUnlockApp(); } //*** CClusterNode::~CClusterNode() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterNode::Cleanup // // Description: // Cleanup the item. // // Arguments: // None. // // Return Values: // None. // // Exceptions Thrown: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterNode::Cleanup(void) { // Delete the Groups Online list. if (m_plpcigrpOnline != NULL) m_plpcigrpOnline->RemoveAll(); // Delete the Resources Online list. if (m_plpciresOnline != NULL) m_plpciresOnline->RemoveAll(); // Delete the Network Interfaces list. if (m_plpciNetInterfaces != NULL) m_plpciNetInterfaces->RemoveAll(); // Remove the item from the node list. { POSITION posPci; posPci = Pdoc()->LpciNodes().Find(this); if (posPci != NULL) { Pdoc()->LpciNodes().RemoveAt(posPci); } // if: found in the document's list } // Remove the item from the node list } //*** CClusterNode::Cleanup() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterNode::Init // // Description: // Initialize the item. // // Arguments: // pdoc [IN OUT] Document to which this item belongs. // lpszName [IN] Name of the item. // // Return Values: // None. // // Exceptions Thrown: // CNTException Errors from OpenClusterGroup or ClusterRegOpenKey. // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterNode::Init(IN OUT CClusterDoc * pdoc, IN LPCTSTR lpszName) { DWORD dwStatus = ERROR_SUCCESS; LONG lResult; CWaitCursor wc; ASSERT(Hnode() == NULL); ASSERT(Hkey() == NULL); // Call the base class method. CClusterItem::Init(pdoc, lpszName); try { // Open the node. m_hnode = OpenClusterNode(Hcluster(), lpszName); if (Hnode() == NULL) { dwStatus = GetLastError(); ThrowStaticException(dwStatus, IDS_OPEN_NODE_ERROR, lpszName); } // if: error opening the cluster node // Get the node registry key. m_hkey = GetClusterNodeKey(Hnode(), MAXIMUM_ALLOWED); if (Hkey() == NULL) ThrowStaticException(GetLastError(), IDS_GET_NODE_KEY_ERROR, lpszName); ASSERT(Pcnk() != NULL); Trace(g_tagClusItemNotify, _T("CClusterNode::Init() - Registering for node notifications (%08.8x) for '%s'"), Pcnk(), StrName()); // Register for node notifications. lResult = RegisterClusterNotify( GetClusterAdminApp()->HchangeNotifyPort(), (CLUSTER_CHANGE_NODE_STATE | CLUSTER_CHANGE_NODE_DELETED | CLUSTER_CHANGE_NODE_PROPERTY), Hnode(), (DWORD_PTR) Pcnk() ); if (lResult != ERROR_SUCCESS) { dwStatus = lResult; ThrowStaticException(dwStatus, IDS_NODE_NOTIF_REG_ERROR, lpszName); } // if: error registering for node notifications // Register for registry notifications. if (Hkey() != NULL) { lResult = RegisterClusterNotify( GetClusterAdminApp()->HchangeNotifyPort(), (CLUSTER_CHANGE_REGISTRY_NAME | CLUSTER_CHANGE_REGISTRY_ATTRIBUTES | CLUSTER_CHANGE_REGISTRY_VALUE | CLUSTER_CHANGE_REGISTRY_SUBTREE), Hkey(), (DWORD_PTR) Pcnk() ); if (lResult != ERROR_SUCCESS) { dwStatus = lResult; ThrowStaticException(dwStatus, IDS_NODE_NOTIF_REG_ERROR, lpszName); } // if: error registering for registry notifications } // if: there is a key // Allocate lists. m_plpcigrpOnline = new CGroupList; if ( m_plpcigrpOnline == NULL ) { AfxThrowMemoryException(); } // if: error allocating the group list m_plpciresOnline = new CResourceList; if ( m_plpciresOnline == NULL ) { AfxThrowMemoryException(); } // if: error allocating the resource list m_plpciNetInterfaces = new CNetInterfaceList; if ( m_plpciNetInterfaces == NULL ) { AfxThrowMemoryException(); } // if: error allocating the net interface list // Read the initial state. UpdateState(); } // try catch (CException *) { if (Hkey() != NULL) { ClusterRegCloseKey(Hkey()); m_hkey = NULL; } // if: registry key opened if (Hnode() != NULL) { CloseClusterNode(Hnode()); m_hnode = NULL; } // if: node opened m_bReadOnly = TRUE; throw; } // catch: CException } //*** CClusterNode::Init() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterNode::ReadItem // // Description: // Read the item parameters from the cluster database. // // Arguments: // None. // // Return Values: // None. // // Exceptions Thrown: // Any exceptions from CClusterItem::ReadItem(). // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterNode::ReadItem(void) { DWORD dwStatus; DWORD dwRetStatus = ERROR_SUCCESS; CWaitCursor wc; ASSERT(Hnode() != NULL); if (Hnode() != NULL) { m_rgProps[epropDescription].m_value.pstr = &m_strDescription; // Call the base class method. CClusterItem::ReadItem(); // Read and parse the common properties. { CClusPropList cpl; dwStatus = cpl.ScGetNodeProperties( Hnode(), CLUSCTL_NODE_GET_COMMON_PROPERTIES ); if (dwStatus == ERROR_SUCCESS) dwStatus = DwParseProperties(cpl); if (dwStatus != ERROR_SUCCESS) dwRetStatus = dwStatus; } // Read and parse the common properties // Read and parse the read-only common properties. if (dwRetStatus == ERROR_SUCCESS) { CClusPropList cpl; dwStatus = cpl.ScGetNodeProperties( Hnode(), CLUSCTL_NODE_GET_RO_COMMON_PROPERTIES ); if (dwStatus == ERROR_SUCCESS) dwStatus = DwParseProperties(cpl); if (dwStatus != ERROR_SUCCESS) dwRetStatus = dwStatus; } // if: no error yet // Read extension lists. ReadExtensions(); } // if: node is avaialble // Read the initial state. UpdateState(); // ConstructActiveGroupList(); // ConstructActiveResourceList(); // If any errors occurred, throw an exception. if (dwRetStatus != ERROR_SUCCESS) { m_bReadOnly = TRUE; ThrowStaticException(dwRetStatus, IDS_READ_NODE_PROPS_ERROR, StrName()); } // if: error reading properties MarkAsChanged(FALSE); } //*** CClusterNode::ReadItem() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterNode::PlstrExtensions // // Description: // Return the list of admin extensions. // // Arguments: // None. // // Return Values: // plstr List of extensions. // NULL No extension associated with this object. // // Exceptions Thrown: // None. // //-- ///////////////////////////////////////////////////////////////////////////// const CStringList * CClusterNode::PlstrExtensions(void) const { return &Pdoc()->PciCluster()->LstrNodeExtensions(); } //*** CClusterNode::PlstrExtensions() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterNode::ReadExtensions // // Description: // Read extension lists. // // Arguments: // None. // // Return Values: // None. // // Exceptions Thrown: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterNode::ReadExtensions(void) { } //*** CClusterNode::ReadExtensions() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterNode::AddActiveGroup // // Description: // Add a group to the list of active groups. // // Arguments: // pciGroup [IN OUT] New active group. // // Return Values: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterNode::AddActiveGroup(IN OUT CGroup * pciGroup) { POSITION posPci; Trace(g_tagNode, _T("Adding active group '%s' (%x) to node '%s"), (pciGroup ? pciGroup->StrName() : _T("")), pciGroup, StrName()); // Make sure the group is not already in the list. VERIFY((posPci = LpcigrpOnline().Find(pciGroup)) == NULL); if (posPci == NULL) { POSITION posPtiNode; CTreeItem * ptiNode; CTreeItem * ptiGroups; // Loop through each tree item to update the Active Groups list. posPtiNode = LptiBackPointers().GetHeadPosition(); while (posPtiNode != NULL) { ptiNode = LptiBackPointers().GetNext(posPtiNode); ASSERT_VALID(ptiNode); // Find the Active Groups child tree item and add the new group. ptiGroups = ptiNode->PtiChildFromName(IDS_TREEITEM_ACTIVEGROUPS); ASSERT_VALID(ptiGroups); VERIFY(ptiGroups->PliAddChild(pciGroup) != NULL); } // while: more tree items for this node m_plpcigrpOnline->AddTail(pciGroup); } // if: group not in the list yet } //*** CClusterNode::AddActiveGroup() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterNode::AddActiveResource // // Description: // Add a resource to the list of active resources. // // Arguments: // pciRes [IN OUT] New active resource. // // Return Values: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterNode::AddActiveResource(IN OUT CResource * pciRes) { POSITION posPci; Trace(g_tagNode, _T("Adding active resource '%s' (%x) to node '%s"), (pciRes ? pciRes->StrName() : _T("")), pciRes, StrName()); // Make sure the resource is not already in the list. VERIFY((posPci = LpciresOnline().Find(pciRes)) == NULL); if (posPci == NULL) { POSITION posPtiNode; CTreeItem * ptiNode; CTreeItem * ptiResources; // Loop through each tree item to update the Active Resources list. posPtiNode = LptiBackPointers().GetHeadPosition(); while (posPtiNode != NULL) { ptiNode = LptiBackPointers().GetNext(posPtiNode); ASSERT_VALID(ptiNode); // Find the Active Resources child tree item and add the new resource. ptiResources = ptiNode->PtiChildFromName(IDS_TREEITEM_ACTIVERESOURCES); ASSERT_VALID(ptiResources); VERIFY(ptiResources->PliAddChild(pciRes) != NULL); } // while: more tree items for this node m_plpciresOnline->AddTail(pciRes); } // if: resource not in the list yet } //*** CClusterNode::AddActiveResource() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterNode::AddNetInterface // // Description: // Add a network interface to the list of interaces installed in this node. // // Arguments: // pciNetIFace [IN OUT] New network interface. // // Return Values: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterNode::AddNetInterface(IN OUT CNetInterface * pciNetIFace) { POSITION posPci; ASSERT_VALID(pciNetIFace); Trace(g_tagNode, _T("(%s) (%s (%x)) - Adding network interface '%s'"), Pdoc()->StrNode(), StrName(), this, pciNetIFace->StrName()); // Make sure the resource is not already in the list. VERIFY((posPci = LpciNetInterfaces().Find(pciNetIFace)) == NULL); if (posPci == NULL) { POSITION posPtiNode; CTreeItem * ptiNode; CTreeItem * ptiNetIFace; // Loop through each tree item to update the Network Interfaces list. posPtiNode = LptiBackPointers().GetHeadPosition(); while (posPtiNode != NULL) { ptiNode = LptiBackPointers().GetNext(posPtiNode); ASSERT_VALID(ptiNode); // Find the Active Resources child tree item and add the new resource. ptiNetIFace = ptiNode->PtiChildFromName(IDS_TREEITEM_NETIFACES); ASSERT_VALID(ptiNetIFace); VERIFY(ptiNetIFace->PliAddChild(pciNetIFace) != NULL); } // while: more tree items for this node m_plpciNetInterfaces->AddTail(pciNetIFace); } // if: network interface not in the list yet } //*** CClusterNode::AddNetInterface() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterNode::RemoveActiveGroup // // Description: // Remove a group from the list of active groups. // // Arguments: // pciGroup [IN OUT] Group that is no longer active on this node. // // Return Values: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterNode::RemoveActiveGroup(IN OUT CGroup * pciGroup) { POSITION posPci; Trace(g_tagNode, _T("Removing active group '%s' (%x) from node '%s"), (pciGroup ? pciGroup->StrName() : _T("")), pciGroup, StrName()); // Make sure the group is in the list. VERIFY((posPci = LpcigrpOnline().Find(pciGroup)) != NULL); if (posPci != NULL) { POSITION posPtiNode; CTreeItem * ptiNode; CTreeItem * ptiGroups; // Loop through each tree item to update the Active Groups list. posPtiNode = LptiBackPointers().GetHeadPosition(); while (posPtiNode != NULL) { ptiNode = LptiBackPointers().GetNext(posPtiNode); ASSERT_VALID(ptiNode); // Find the Active Groups child tree item and remove the group. ptiGroups = ptiNode->PtiChildFromName(IDS_TREEITEM_ACTIVEGROUPS); ASSERT_VALID(ptiGroups); ptiGroups->RemoveChild(pciGroup); } // while: more tree items for this node m_plpcigrpOnline->RemoveAt(posPci); } // if: group in the list } //*** CClusterNode::RemoveActiveGroup() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterNode::RemoveActiveResource // // Description: // Remove a resource from the list of active resources. // // Arguments: // pciRes [IN OUT] Resource that is no longer active on this node. // // Return Values: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterNode::RemoveActiveResource(IN OUT CResource * pciRes) { POSITION posPci; Trace(g_tagNode, _T("Removing active resource '%s' (%x) from node '%s"), (pciRes ? pciRes->StrName() : _T("")), pciRes, StrName()); // Make sure the resource is in the list. VERIFY((posPci = LpciresOnline().Find(pciRes)) != NULL); if (posPci != NULL) { POSITION posPtiNode; CTreeItem * ptiNode; CTreeItem * ptiResources; // Loop through each tree item to update the Active Resources list. posPtiNode = LptiBackPointers().GetHeadPosition(); while (posPtiNode != NULL) { ptiNode = LptiBackPointers().GetNext(posPtiNode); ASSERT_VALID(ptiNode); // Find the Active Resources child tree item and remove the resource. ptiResources = ptiNode->PtiChildFromName(IDS_TREEITEM_ACTIVERESOURCES); ASSERT_VALID(ptiResources); ptiResources->RemoveChild(pciRes); } // while: more tree items for this node m_plpciresOnline->RemoveAt(posPci); } // if: resource in the list } //*** CClusterNode::RemoveActiveResource() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterNode::RemoveNetInterface // // Description: // Remove a network interface from the list of interaces installed in this node. // // Arguments: // pciNetIFace [IN OUT] Network interface that is no longer // connected to this network. // // Return Values: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterNode::RemoveNetInterface(IN OUT CNetInterface * pciNetIFace) { POSITION posPci; ASSERT_VALID(pciNetIFace); Trace(g_tagNode, _T("(%s) (%s (%x)) - Removing network interface '%s'"), Pdoc()->StrNode(), StrName(), this, pciNetIFace->StrName()); // Make sure the network interface is in the list. VERIFY((posPci = LpciNetInterfaces().Find(pciNetIFace)) != NULL); if (posPci != NULL) { POSITION posPtiNode; CTreeItem * ptiNode; CTreeItem * ptiNetIFace; // Loop through each tree item to update the Network Interfaces list. posPtiNode = LptiBackPointers().GetHeadPosition(); while (posPtiNode != NULL) { ptiNode = LptiBackPointers().GetNext(posPtiNode); ASSERT_VALID(ptiNode); // Find the Network Interfaces child tree item and remove the resource. ptiNetIFace = ptiNode->PtiChildFromName(IDS_TREEITEM_NETIFACES); ASSERT_VALID(ptiNetIFace); ptiNetIFace->RemoveChild(pciNetIFace); } // while: more tree items for this network m_plpciNetInterfaces->RemoveAt(posPci); } // if: network interface in the list } //*** CClusterNode::RemoveNetInterface() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterNode::SetDescription // // Description: // Set the description in the cluster database. // // Arguments: // rstrDesc [IN] Description to set. // bValidateOnly [IN] Only validate the data. // // Return Values: // None. // // Exceptions Thrown: // Any exceptions thrown by WriteItem(). // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterNode::SetDescription( IN const CString & rstrDesc, IN BOOL bValidateOnly ) { CNTException nte(ERROR_SUCCESS, 0, NULL, NULL, FALSE /*bAutoDelete*/); m_rgProps[epropDescription].m_value.pstr = (CString *) &rstrDesc; try { CClusterItem::SetCommonProperties(bValidateOnly); } // try catch (CNTException * pnte) { nte.SetOperation( pnte->Sc(), pnte->IdsOperation(), pnte->PszOperArg1(), pnte->PszOperArg2() ); } // catch: CNTException m_rgProps[epropDescription].m_value.pstr = &m_strDescription; if (nte.Sc() != ERROR_SUCCESS) ThrowStaticException( nte.Sc(), nte.IdsOperation(), nte.PszOperArg1(), nte.PszOperArg2() ); } //*** CClusterNode::SetDescription() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterNode::DwSetCommonProperties // // Description: // Set the common properties for this resource in the cluster database. // // Arguments: // rcpl [IN] Property list to set. // bValidateOnly [IN] Only validate the data. // // Return Values: // Any status returned by ClusterResourceControl(). // //-- ///////////////////////////////////////////////////////////////////////////// DWORD CClusterNode::DwSetCommonProperties( IN const CClusPropList & rcpl, IN BOOL bValidateOnly ) { DWORD dwStatus; CWaitCursor wc; ASSERT(Hnode()); if ((rcpl.PbPropList() != NULL) && (rcpl.CbPropList() > 0)) { DWORD cbProps; DWORD dwControl; if (bValidateOnly) dwControl = CLUSCTL_NODE_VALIDATE_COMMON_PROPERTIES; else dwControl = CLUSCTL_NODE_SET_COMMON_PROPERTIES; // Set private properties. dwStatus = ClusterNodeControl( Hnode(), NULL, // hNode dwControl, rcpl.PbPropList(), rcpl.CbPropList(), NULL, // lpOutBuffer 0, // nOutBufferSize &cbProps ); } // if: there is data to set else dwStatus = ERROR_SUCCESS; return dwStatus; } //*** CClusterNode::DwSetCommonProperties() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterNode::BCanBeDropTarget // // Description: // Determine if the specified item can be dropped on this item. // // Arguments: // pci [IN OUT] Item to be dropped on this item. // // Return Values: // TRUE Can be drop target. // FALSE Can NOT be drop target. // //-- ///////////////////////////////////////////////////////////////////////////// BOOL CClusterNode::BCanBeDropTarget(IN const CClusterItem * pci) const { BOOL bCan; // This node can be a drop target only if the specified item // is a group and it is not already a running on this node. if ((Cns() == ClusterNodeUp) && (pci->IdsType() == IDS_ITEMTYPE_GROUP)) { CGroup * pciGroup = (CGroup *) pci; ASSERT_KINDOF(CGroup, pciGroup); if (pciGroup->StrOwner() != StrName()) bCan = TRUE; else bCan = FALSE; Trace(g_tagNodeDrag, _T("BCanBeDropTarget() - Dragging group '%s' (%x) (owner = '%s') over node '%s' (%x)"), pciGroup->StrName(), pciGroup, pciGroup->StrOwner(), StrName(), this); } // if: node is up and dropping group item else bCan = FALSE; return bCan; } //*** CClusterNode::BCanBeDropTarget() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterNode::DropItem // // Description: // Process an item being dropped on this item. // // Arguments: // pci [IN OUT] Item dropped on this item. // // Return Values: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterNode::DropItem(IN OUT CClusterItem * pci) { // Do this in case this object is deleted while we are operating on it. AddRef(); do // do-while to prevent goto's { if (BCanBeDropTarget(pci)) { POSITION pos; UINT imenu; UINT idMenu; CClusterNode * pciNode; CGroup * pciGroup; // Calculate the ID of this node. pos = Pdoc()->LpciNodes().GetHeadPosition(); for (imenu = 0, idMenu = ID_FILE_MOVE_GROUP_1 ; pos != NULL ; idMenu++) { pciNode = (CClusterNode *) Pdoc()->LpciNodes().GetNext(pos); ASSERT_VALID(pciNode); if (pciNode == this) break; } // for: each group ASSERT(imenu < (UINT) Pdoc()->LpciNodes().GetCount()); // Change the group of the specified resource. pciGroup = (CGroup *) pci; ASSERT_KINDOF(CGroup, pci); ASSERT_VALID(pciGroup); // Verify that the resource should be moved. { CString strMsg; strMsg.FormatMessage(IDS_VERIFY_MOVE_GROUP, pciGroup->StrName(), pciGroup->StrOwner(), StrName()); if (AfxMessageBox(strMsg, MB_YESNO | MB_ICONEXCLAMATION) != IDYES) break; } // Verify that the resource should be moved // Move the group. pciGroup->OnCmdMoveGroup(idMenu); } // if: item can be dropped on this item else if (pci->IdsType() == IDS_ITEMTYPE_GROUP) { CString strMsg; #ifdef _DEBUG CGroup * pciGroup = (CGroup *) pci; ASSERT_KINDOF(CGroup, pci); ASSERT_VALID(pciGroup); #endif // _DEBUG // Format the proper message. if (Cns() != ClusterNodeUp) strMsg.FormatMessage(IDS_CANT_MOVE_GROUP_TO_DOWN_NODE, pci->StrName(), StrName()); else { ASSERT(pciGroup->StrOwner() == StrName()); strMsg.FormatMessage(IDS_CANT_MOVE_GROUP_TO_SAME_NODE, pci->StrName(), StrName()); } // else: problem is not that the node is not up AfxMessageBox(strMsg, MB_OK | MB_ICONSTOP); } // else if: dropped item is a group else CClusterItem::DropItem(pci); } while (0); // do-while to prevent goto's Release(); } //*** CClusterNode::DropItem() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterNode::UpdateState // // Description: // Update the current state of the item. // // Arguments: // None. // // Return Values: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterNode::UpdateState( void ) { CClusterAdminApp * papp = GetClusterAdminApp(); CLUSTER_NODE_STATE cnsPrev = m_cns; // Get the current state of the node. if ( Hnode() == NULL ) { m_cns = ClusterNodeStateUnknown; } // if: node is not valid else { CWaitCursor wc; m_cns = GetClusterNodeState( Hnode() ); } // else: node is valid // Save the current state image index. switch ( Cns() ) { case ClusterNodeStateUnknown: m_iimgState = papp->Iimg( IMGLI_NODE_UNKNOWN ); break; case ClusterNodeUp: m_iimgState = papp->Iimg( IMGLI_NODE ); if ( cnsPrev == ClusterNodeDown ) { UpdateResourceTypePossibleOwners(); } // if: node was previously down break; case ClusterNodeDown: m_iimgState = papp->Iimg( IMGLI_NODE_DOWN ); break; case ClusterNodePaused: m_iimgState = papp->Iimg( IMGLI_NODE_PAUSED ); break; case ClusterNodeJoining: m_iimgState = papp->Iimg( IMGLI_NODE_UNKNOWN ); break; default: Trace( g_tagNode, _T("(%s (%x)) - UpdateState: Unknown state '%d' for node '%s'"), StrName(), this, Cns(), StrName() ); m_iimgState = (UINT) -1; break; } // switch: Cns() // Call the base class method. CClusterItem::UpdateState(); } //*** CClusterNode::UpdateState() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterNode::UpdateResourceTypePossibleOwners // // Description: // Update the possible owner lists of any resource types that have // faked them because of nodes being down. // // Arguments: // None. // // Return Values: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterNode::UpdateResourceTypePossibleOwners( void ) { POSITION pos; CResourceType * pciResType; pos = Pdoc()->LpciResourceTypes().GetHeadPosition(); while ( pos != NULL ) { pciResType = (CResourceType *) Pdoc()->LpciResourceTypes().GetNext( pos ); ASSERT_VALID( pciResType ); if ( pciResType->BPossibleOwnersAreFake() ) { pciResType->CollectPossibleOwners(); } // if: possible owners have been faked } // while: more resource types } //*** CClusterNode::UpdateResourceTypePossibleOwners() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterNode::OnFinalRelease // // Description: // Called when the last OLE reference to or from the object is released. // // Arguments: // None. // // Return Values: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterNode::OnFinalRelease(void) { // When the last reference for an automation object is released // OnFinalRelease is called. The base class will automatically // deletes the object. Add additional cleanup required for your // object before calling the base class. CClusterItem::OnFinalRelease(); } //*** CClusterNode::OnFinalRelease() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterNode::BGetColumnData // // Description: // Returns a string with the column data. // // Arguments: // colid [IN] Column ID. // rstrText [OUT] String in which to return the text for the column. // // Return Values: // TRUE Column data returned. // FALSE Column ID not recognized. // //-- ///////////////////////////////////////////////////////////////////////////// BOOL CClusterNode::BGetColumnData(IN COLID colid, OUT CString & rstrText) { BOOL bSuccess; switch (colid) { case IDS_COLTEXT_STATE: GetStateName(rstrText); bSuccess = TRUE; break; default: bSuccess = CClusterItem::BGetColumnData(colid, rstrText); break; } // switch: colid return bSuccess; } //*** CClusterNode::BGetColumnData() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterNode::GetTreeName // // Description: // Returns a string to be used in a tree control. // // Arguments: // rstrName [OUT] String in which to return the name. // // Return Values: // None. // //-- ///////////////////////////////////////////////////////////////////////////// #ifdef _DISPLAY_STATE_TEXT_IN_TREE void CClusterNode::GetTreeName(OUT CString & rstrName) const { CString strState; GetStateName(strState); rstrName.Format(_T("%s (%s)"), StrName(), strState); } //*** CClusterNode::GetTreeName() #endif ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterNode::GetStateName // // Description: // Returns a string with the name of the current state. // // Arguments: // rstrState [OUT] String in which to return the name of the current state. // // Return Values: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterNode::GetStateName(OUT CString & rstrState) const { switch (Cns()) { case ClusterNodeStateUnknown: rstrState.LoadString(IDS_UNKNOWN); break; case ClusterNodeUp: rstrState.LoadString(IDS_UP); break; case ClusterNodeDown: rstrState.LoadString(IDS_DOWN); break; case ClusterNodePaused: rstrState.LoadString(IDS_PAUSED); break; case ClusterNodeJoining: rstrState.LoadString(IDS_JOINING); break; default: rstrState.Empty(); break; } // switch: Cns() } //*** CClusterNode::GetStateName() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterNode::OnUpdatePauseNode // // Description: // Determines whether menu items corresponding to ID_FILE_PAUSE_NODE // should be enabled or not. // // Arguments: // pCmdUI [IN OUT] Command routing object. // // Return Values: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterNode::OnUpdatePauseNode(CCmdUI * pCmdUI) { if (Cns() == ClusterNodeUp) pCmdUI->Enable(TRUE); else pCmdUI->Enable(FALSE); } //*** CClusterNode::OnUpdatePauseNode() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterNode::OnUpdateResumeNode // // Description: // Determines whether menu items corresponding to ID_FILE_RESUME_NODE // should be enabled or not. // // Arguments: // pCmdUI [IN OUT] Command routing object. // // Return Values: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterNode::OnUpdateResumeNode(CCmdUI * pCmdUI) { if (Cns() == ClusterNodePaused) pCmdUI->Enable(TRUE); else pCmdUI->Enable(FALSE); } //*** CClusterNode::OnUpdateResumeNode() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterNode::OnUpdateEvictNode // // Description: // Determines whether menu items corresponding to ID_FILE_EVICT_NODE // should be enabled or not. // // Arguments: // pCmdUI [IN OUT] Command routing object. // // Return Values: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterNode::OnUpdateEvictNode( CCmdUI * pCmdUI ) { BOOL fCanEvict; fCanEvict = FCanBeEvicted(); pCmdUI->Enable( fCanEvict ); } //*** CClusterNode::OnUpdateEvictNode() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterNode::OnUpdateStartService // // Description: // Determines whether menu items corresponding to ID_FILE_START_SERVICE // should be enabled or not. // // Arguments: // pCmdUI [IN OUT] Command routing object. // // Return Values: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterNode::OnUpdateStartService(CCmdUI * pCmdUI) { if ((Cns() == ClusterNodeStateUnknown) || (Cns() == ClusterNodeDown)) pCmdUI->Enable(TRUE); else pCmdUI->Enable(FALSE); } //*** CClusterNode::OnUpdateStartService() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterNode::OnUpdateStopService // // Description: // Determines whether menu items corresponding to ID_FILE_STOP_SERVICE // should be enabled or not. // // Arguments: // pCmdUI [IN OUT] Command routing object. // // Return Values: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterNode::OnUpdateStopService(CCmdUI * pCmdUI) { if ((Cns() == ClusterNodeStateUnknown) || (Cns() == ClusterNodeUp)) pCmdUI->Enable(TRUE); else pCmdUI->Enable(FALSE); } //*** CClusterNode::OnUpdateStopService() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterNode::OnCmdPauseNode // // Description: // Processes the ID_FILE_PAUSE_NODE menu command. // // Arguments: // None. // // Return Values: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterNode::OnCmdPauseNode(void) { DWORD dwStatus; CWaitCursor wc; ASSERT(Hnode() != NULL); dwStatus = PauseClusterNode(Hnode()); if (dwStatus != ERROR_SUCCESS) { CNTException nte(dwStatus, IDS_PAUSE_NODE_ERROR, StrName(), NULL, FALSE /*bAutoDelete*/); nte.ReportError(); } // if: error pausing node UpdateState(); } //*** CClusterNode::OnCmdPauseNode() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterNode::OnCmdResumeNode // // Description: // Processes the ID_FILE_RESUME_NODE menu command. // // Arguments: // None. // // Return Values: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterNode::OnCmdResumeNode(void) { DWORD dwStatus; CWaitCursor wc; ASSERT(Hnode() != NULL); dwStatus = ResumeClusterNode(Hnode()); if (dwStatus != ERROR_SUCCESS) { CNTException nte(dwStatus, IDS_RESUME_NODE_ERROR, StrName(), NULL, FALSE /*bAUtoDelete*/); nte.ReportError(); } // if: error resuming node UpdateState(); } //*** CClusterNode::OnCmdResumeNode() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterNode::OnCmdEvictNode // // Description: // Processes the ID_FILE_EVICT_NODE menu command. // // Arguments: // None. // // Return Values: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterNode::OnCmdEvictNode(void) { ASSERT(Hnode() != NULL); // Do this in case this object is deleted while we are operating on it. AddRef(); if ( ! FCanBeEvicted() ) { TCHAR szMsg[1024]; CNTException nte(ERROR_CANT_EVICT_ACTIVE_NODE, 0, NULL, NULL, FALSE /*bAutoDelete*/); nte.FormatErrorMessage(szMsg, sizeof(szMsg) / sizeof(TCHAR), NULL, FALSE /*bIncludeID*/); AfxMessageBox(szMsg); } // if: node can not be evicted else { DWORD dwStatus; DWORD dwCleanupStatus; HRESULT hrCleanupStatus; CString strMsg; CWaitCursor wc; try { // Verify that the user really wants to evict this node. strMsg.FormatMessage(IDS_VERIFY_EVICT_NODE, StrName()); if (AfxMessageBox(strMsg, MB_YESNO | MB_ICONEXCLAMATION | MB_DEFBUTTON2) == IDYES) { // Evict the node. dwStatus = EvictClusterNodeEx(Hnode(), INFINITE, &hrCleanupStatus); // convert any cleanup error from an hresult to a win32 error code dwCleanupStatus = HRESULT_CODE( hrCleanupStatus ); if( ERROR_CLUSTER_EVICT_WITHOUT_CLEANUP == dwStatus ) { // // Eviction was successful, but cleanup failed. dwCleanupStatus contains // the cleanup error code. // CNTException nte( dwCleanupStatus, IDS_EVICT_NODE_ERROR_UNAVAILABLE, StrName(), NULL, FALSE /*bAutoDelete*/ ); nte.ReportError(); } else if( ERROR_SUCCESS != dwStatus ) { // // Eviction was not successful. Display the error. // CNTException nte(dwStatus, IDS_EVICT_NODE_ERROR, StrName(), NULL, FALSE /*bAutoDelete*/); nte.ReportError(); } // if: error evicting the node // else: eviction and cleanup successful UpdateState(); } // if: user selected yes from message box (to online resource) } // try catch (CException * pe) { pe->ReportError(); pe->Delete(); } // catch: CException } // else: node is down Release(); } //*** CClusterNode::OnCmdEvictNode() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterNode::OnCmdStartService // // Description: // Processes the ID_FILE_START_SERVICE menu command. // // Arguments: // None. // // Return Values: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterNode::OnCmdStartService(void) { HRESULT hr; BOOL bRefresh = FALSE; CWaitCursor wc; // If all nodes are down or unavailable, we need to refresh. if ( Cns() == ClusterNodeStateUnknown ) { bRefresh = TRUE; } else { int cNodesUp = 0; POSITION pos; CClusterNode * pciNode; pos = Pdoc()->LpciNodes().GetHeadPosition(); while ( pos != NULL ) { pciNode = (CClusterNode *) Pdoc()->LpciNodes().GetNext( pos ); ASSERT_VALID( pciNode ); if ( pciNode->Cns() == ClusterNodeStateUnknown ) { cNodesUp++; } } // while: more items in the list if ( cNodesUp > 0 ) { bRefresh = TRUE; } } // else: node state is available // Start the service. hr = HrStartService( CLUSTER_SERVICE_NAME, StrName() ); if ( FAILED( hr ) ) { CNTException nte( hr, IDS_START_CLUSTER_SERVICE_ERROR, StrName(), NULL, FALSE /*bAutoDelete*/ ); nte.ReportError(); } // if: error starting the service else if ( bRefresh ) { Sleep( 2000 ); Pdoc()->Refresh(); } // else if: we need to refresh } //*** CClusterNode::OnCmdStartService() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterNode::OnCmdStopService // // Description: // Processes the ID_FILE_STOP_SERVICE menu command. // // Arguments: // None. // // Return Values: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterNode::OnCmdStopService(void) { HRESULT hr; // Do this in case this object is deleted while we are operating on it. AddRef(); // Stop the service. hr = HrStopService( CLUSTER_SERVICE_NAME, StrName() ); if ( FAILED( hr ) ) { CNTException nte( hr, IDS_STOP_CLUSTER_SERVICE_ERROR, StrName(), NULL, FALSE /*bAutoDelete*/ ); nte.ReportError(); } Release(); } //*** CClusterNode::OnCmdStopService() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterNode::OnUpdateProperties // // Description: // Determines whether menu items corresponding to ID_FILE_PROPERTIES // should be enabled or not. // // Arguments: // pCmdUI [IN OUT] Command routing object. // // Return Values: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterNode::OnUpdateProperties(CCmdUI * pCmdUI) { pCmdUI->Enable(TRUE); } //*** CClusterNode::OnUpdateProperties() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterNode::BDisplayProperties // // Description: // Display properties for the object. // // Arguments: // bReadOnly [IN] Don't allow edits to the object properties. // // Return Values: // TRUE OK pressed. // FALSE OK not pressed. // //-- ///////////////////////////////////////////////////////////////////////////// BOOL CClusterNode::BDisplayProperties(IN BOOL bReadOnly) { BOOL bChanged = FALSE; CNodePropSheet sht(AfxGetMainWnd()); // Do this in case this object is deleted while we are operating on it. AddRef(); // If the object has changed, read it. if (BChanged()) ReadItem(); // Display the property sheet. try { sht.SetReadOnly(bReadOnly); if (sht.BInit(this, IimgObjectType())) bChanged = ((sht.DoModal() == IDOK) && !bReadOnly); } // try catch (CException * pe) { pe->Delete(); } // catch: CException Release(); return bChanged; } //*** CClusterNode::BDisplayProperties() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterNode::OnClusterNotify // // Description: // Handler for the WM_CAM_CLUSTER_NOTIFY message. // Processes cluster notifications for this object. // // Arguments: // pnotify [IN OUT] Object describing the notification. // // Return Values: // Value returned from the application method. // //-- ///////////////////////////////////////////////////////////////////////////// LRESULT CClusterNode::OnClusterNotify(IN OUT CClusterNotify * pnotify) { ASSERT(pnotify != NULL); ASSERT_VALID(this); try { switch (pnotify->m_dwFilterType) { case CLUSTER_CHANGE_NODE_STATE: Trace(g_tagNodeNotify, _T("(%s) - Node '%s' (%x) state changed (%s)"), Pdoc()->StrNode(), StrName(), this, pnotify->m_strName); UpdateState(); break; case CLUSTER_CHANGE_NODE_DELETED: Trace(g_tagNodeNotify, _T("(%s) - Node '%s' (%x) deleted (%s)"), Pdoc()->StrNode(), StrName(), this, pnotify->m_strName); if (Pdoc()->BClusterAvailable()) Delete(); break; case CLUSTER_CHANGE_NODE_PROPERTY: Trace(g_tagNodeNotify, _T("(%s) - Node '%s' (%x) properties changed (%s)"), Pdoc()->StrNode(), StrName(), this, pnotify->m_strName); if (Pdoc()->BClusterAvailable()) ReadItem(); break; case CLUSTER_CHANGE_REGISTRY_NAME: Trace(g_tagNodeRegNotify, _T("(%s) - Registry namespace '%s' changed (%s %s (%x))"), Pdoc()->StrNode(), pnotify->m_strName, StrType(), StrName(), this); MarkAsChanged(); break; case CLUSTER_CHANGE_REGISTRY_ATTRIBUTES: Trace(g_tagNodeRegNotify, _T("(%s) - Registry attributes for '%s' changed (%s %s (%x))"), Pdoc()->StrNode(), pnotify->m_strName, StrType(), StrName(), this); MarkAsChanged(); break; case CLUSTER_CHANGE_REGISTRY_VALUE: Trace(g_tagNodeRegNotify, _T("(%s) - Registry value '%s' changed (%s %s (%x))"), Pdoc()->StrNode(), pnotify->m_strName, StrType(), StrName(), this); MarkAsChanged(); break; default: Trace(g_tagNodeNotify, _T("(%s) - Unknown node notification (%x) for '%s' (%x) (%s)"), Pdoc()->StrNode(), pnotify->m_dwFilterType, StrName(), this, pnotify->m_strName); } // switch: dwFilterType } // try catch (CException * pe) { // Don't display anything on notification errors. // If it's really a problem, the user will see it when // refreshing the view. //pe->ReportError(); pe->Delete(); } // catch: CException delete pnotify; return 0; } //*** CClusterNode::OnClusterNotify() /* ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterNode::Delete // // Description: // Do the CClusterItem::Delete processing unique to this class. // // Arguments: // None. // // Return Values: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterNode::Delete(void) { POSITION _pos = NULL; CResourceType * _ptype = NULL; CResource * _pres = NULL; // // Remove this node from the resource types possible owners list // _pos = Pdoc()->LpciResourceTypes().GetHeadPosition(); while (_pos != NULL) { _ptype = dynamic_cast(Pdoc()->LpciResourceTypes().GetNext(_pos)); if (_ptype != NULL) { _ptype->RemoveNodeFromPossibleOwners(NULL, this); } // if: _ptype != NULL } // while: _pos != NULL // // Remove this node from the resources possible owners list // _pos = Pdoc()->LpciResources().GetHeadPosition(); while (_pos != NULL) { _pres = dynamic_cast(Pdoc()->LpciResources().GetNext(_pos)); if (_pres != NULL) { _pres->RemoveNodeFromPossibleOwners(NULL, this); } // if: _pres != NULL } // while: _pos != NULL CClusterItem::Delete(); // do the old processing } //*** CClusterNode::Delete() */ ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterNode::FCanBeEvicted // // Description: // Determine if the node can be evicted. // // Arguments: // None. // // Return Values: // TRUE Node can be evicted. // FALSE Node can not be evicted. // //-- ///////////////////////////////////////////////////////////////////////////// BOOL CClusterNode::FCanBeEvicted( void ) { BOOL fCanEvict; if ( ( m_nMajorVersion < 5 ) || ( ( m_nMajorVersion == 5 ) && ( m_nMinorVersion < 1 ) ) ) { if ( ( Cns() == ClusterNodeDown ) || ( Pdoc()->LpciNodes().GetCount() > 1 ) ) { fCanEvict = TRUE; } else { fCanEvict = FALSE; } } // if: pre-Whistler node else { if ( ( Cns() == ClusterNodeDown ) || ( Pdoc()->LpciNodes().GetCount() == 1 ) ) { fCanEvict = TRUE; } else { fCanEvict = FALSE; } } // else: Whistler or higher node return fCanEvict; } //*** CClusterNode::FCanBeEvicted() //*************************************************************************// ///////////////////////////////////////////////////////////////////////////// // Global Functions ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// //++ // // DeleteAllItemData // // Description: // Deletes all item data in a CList. // // Arguments: // rlp [IN OUT] List whose data is to be deleted. // // Return Values: // None. // //-- ///////////////////////////////////////////////////////////////////////////// #ifdef NEVER void DeleteAllItemData(IN OUT CNodeList & rlp) { POSITION pos; CClusterNode * pci; // Delete all the items in the Contained list. pos = rlp.GetHeadPosition(); while (pos != NULL) { pci = rlp.GetNext(pos); ASSERT_VALID(pci); // Trace(g_tagClusItemDelete, _T("DeleteAllItemData(rlpcinode) - Deleting node cluster item '%s' (%x)"), pci->StrName(), pci); pci->Delete(); } // while: more items in the list } //*** DeleteAllItemData() #endif