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.
 
 
 
 
 
 

8387 lines
231 KiB

//***************************************************************************
//
// ENGINE.CPP
//
// Module: NLB Manager (client-side exe)
//
// Purpose: Implements the engine used to operate on groups of NLB hosts.
// This file has no UI aspects.
//
// Copyright (c)2001 Microsoft Corporation, All Rights Reserved
//
// History:
//
// 07/25/01 JosephJ Created
//
//***************************************************************************
#include "precomp.h"
#pragma hdrstop
#include "AboutDialog.h"
#include "private.h"
#include "engine.tmh"
//
// ENGINEHANDLE incodes the type of the object -- the following constant
// is the number of bits used to encode the type.
//
#define TYPE_BIT_COUNT 0x3
BOOL
validate_extcfg(
const NLB_EXTENDED_CLUSTER_CONFIGURATION &Config
);
BOOL
get_used_port_rule_priorities(
IN const NLB_EXTENDED_CLUSTER_CONFIGURATION &Config,
IN UINT NumRules,
IN const WLBS_PORT_RULE rgRules[],
IN OUT ULONG rgUsedPriorities[] // At least NumRules
);
const WLBS_PORT_RULE *
find_port_rule(
const WLBS_PORT_RULE *pRules,
UINT NumRules,
LPCWSTR szVIP,
UINT StartPort
);
VOID
remove_dedicated_ip_from_nlbcfg(
NLB_EXTENDED_CLUSTER_CONFIGURATION &ClusterCfg
);
NLBERROR
analyze_nlbcfg(
IN const NLB_EXTENDED_CLUSTER_CONFIGURATION &NlbCfg,
IN const NLB_EXTENDED_CLUSTER_CONFIGURATION &OtherNlbCfg,
IN LPCWSTR szOtherDescription,
IN BOOL fClusterProps,
IN BOOL fDisablePasswordCheck,
IN OUT CLocalLogger &logger
);
DWORD
WINAPI
UpdateInterfaceWorkItemRoutine(
LPVOID lpParameter // thread data
);
DWORD
WINAPI
AddClusterMembersWorkItemRoutine(
LPVOID lpParameter // thread data
);
void
CHostSpec::Copy(const CHostSpec &hs)
/*
This is the copy operator. Need to make a copy of strings in embedded
vectors.
*/
{
*this = hs;
}
NLBERROR
CClusterSpec::Copy(const CClusterSpec &cs)
/*
This is the copy operator. Need to munge the m_ClusterNlbCfg field.
TODO: fix this hack.
TODO: if we fail we leave CClusterSpec trashed!
*/
{
NLBERROR nerr = NLBERR_INTERNAL_ERROR;
m_ClusterNlbCfg.Clear();
*this = cs; // non-trivial copy
ZeroMemory(&m_ClusterNlbCfg, sizeof(m_ClusterNlbCfg));
m_ClusterNlbCfg.Clear(); // TODO: please! cleanup NLB_EXTENDED...
//
// Copy over the cluster configuration.
//
{
WBEMSTATUS wStat;
wStat = m_ClusterNlbCfg.Update(&cs.m_ClusterNlbCfg);
if (FAILED(wStat))
{
//
// We've trashed m_ClusterNlbCfg -- set defaults.
//
CfgUtilInitializeParams(&m_ClusterNlbCfg.NlbParams);
if (wStat == WBEM_E_OUT_OF_MEMORY)
{
nerr = NLBERR_RESOURCE_ALLOCATION_FAILURE;
}
else
{
//
// We assume that it's because the cluster spec is invalid.
//
nerr = NLBERR_INVALID_CLUSTER_SPECIFICATION;
}
}
else
{
nerr = NLBERR_OK;
}
}
return nerr;
}
void
CInterfaceSpec::Copy(const CInterfaceSpec &is)
/*
This is the copy operator. Need to munge the m_NlbCfg field.
TODO: fix this hack.
TODO: add return value (m_NlbCfg.Update now returns an error).
*/
{
*this = is;
ZeroMemory(&m_NlbCfg, sizeof(m_NlbCfg));
m_NlbCfg.Update(&is.m_NlbCfg);
}
NLBERROR
CNlbEngine::Initialize(
IN IUICallbacks & ui,
BOOL fDemo,
BOOL fNoPing
)
{
NLBERROR nerr = NLBERR_INTERNAL_ERROR;
TRACE_INFO(L"-> %!FUNC! (bDemo=%lu)", fDemo);
//
// Enable the "SeLoadDriverPrivilege" privilege in the process access token.
// This is needed in the case when the server is local (ie. same machine).
// Do NOT check for the return value since this function will fail when called
// as a non-admin. It is not only ok but also necessary to ignore the failure of
// this function because:
// 1. We already check in the wmi provider that the caller is an administrator on
// the server and if the privilege is enabled. This is why it is ok to ignore
// failures in this function.
// 2. Non-admins can run nlb manager. They only need to be admins on the server.
// This is why it is necessary to ignore failures in this function.
//
CfgUtils_Enable_Load_Unload_Driver_Privilege();
WBEMSTATUS wStat = CfgUtilInitialize(FALSE, fNoPing);
if (!FAILED(wStat))
{
mfn_Lock();
//
// Save away the callback object.
//
m_pCallbacks = &ui;
mfn_Unlock();
if (fDemo)
{
TRACE_CRIT("%!FUNC! RUNNING ENGINE IN DEMO MODE");
NlbHostFake();
}
nerr = NLBERR_OK;
}
TRACE_INFO(L"<- %!FUNC!");
return nerr;
}
void
CNlbEngine::Deinitialize(void)
// TODO: cleanup
{
TRACE_INFO(L"-> %!FUNC!");
ASSERT(m_fPrepareToDeinitialize);
// DummyAction(L"Engine::Deinitialize");
TRACE_INFO(L"<- %!FUNC!");
return;
}
NLBERROR
CNlbEngine::ConnectToHost(
IN PWMI_CONNECTION_INFO pConnInfo,
IN BOOL fOverwriteConnectionInfo,
OUT ENGINEHANDLE &ehHost,
OUT _bstr_t &bstrError
)
/*
Connect the the host specfieid in pConnInfo (includes username and password)
If (fOverwriteConnectionInfo) is true, then it will overwrite
connection-info (connection string, connection IP, credentials)
that pre-exists for this host with the stuff in pConnInfo.
*/
{
NLBERROR nerr = NLBERR_INTERNAL_ERROR;
LPWSTR szWmiMachineName = NULL;
LPWSTR szWmiMachineGuid = NULL;
WBEMSTATUS wStatus;
ULONG uIpAddress;
BOOL fNlbMgrProviderInstalled = FALSE;
TRACE_INFO(L"-> %!FUNC!(%ws)", pConnInfo->szMachine);
ehHost = NULL;
wStatus = NlbHostPing(pConnInfo->szMachine, 2000, &uIpAddress);
if (FAILED(wStatus))
{
nerr = NLBERR_PING_TIMEOUT; // todo more specific error.
bstrError = GETRESOURCEIDSTRING(IDS_PING_FAILED);
goto end;
}
wStatus = NlbHostGetMachineIdentification(
pConnInfo,
&szWmiMachineName,
&szWmiMachineGuid,
&fNlbMgrProviderInstalled
);
if (FAILED(wStatus))
{
GetErrorCodeText(wStatus, bstrError);
if (wStatus == E_ACCESSDENIED)
{
nerr = NLBERR_ACCESS_DENIED;
}
else
{
// TODO: map proper errors.
nerr = NLBERR_NOT_FOUND;
}
TRACE_CRIT(L"Connecting to %ws returns error %ws",
pConnInfo->szMachine, (LPCWSTR) bstrError);
szWmiMachineName = NULL;
szWmiMachineGuid = NULL;
goto end;
}
//
// We use the MachineName (TODO: replace by MachineGuid) as the
// primary key of Hosts.
//
{
CHostSpec* pHost = NULL;
BOOL fIsNew = FALSE;
ehHost = NULL;
mfn_Lock();
nerr = mfn_LookupHostByNameLk(
szWmiMachineName,
TRUE, // create if needed
REF ehHost,
REF pHost,
REF fIsNew
);
if (nerr != NLBERR_OK)
{
mfn_Unlock();
goto end;
}
if (fIsNew)
{
pHost->m_fReal = FALSE; // set to true once the nics are populated.
pHost->m_MachineGuid = _bstr_t(szWmiMachineGuid);
pHost->m_ConnectionString = _bstr_t(pConnInfo->szMachine);
pHost->m_ConnectionIpAddress = uIpAddress;
pHost->m_UserName = _bstr_t(pConnInfo->szUserName);
pHost->m_Password = _bstr_t(pConnInfo->szPassword);
}
mfn_Unlock();
nerr = mfn_RefreshHost(
pConnInfo,
ehHost,
fOverwriteConnectionInfo
);
}
end:
delete szWmiMachineName;
delete szWmiMachineGuid;
return nerr;
}
void
CNlbEngine::DeleteCluster(
IN ENGINEHANDLE ehCluster,
IN BOOL fRemoveInterfaces)
{
NLBERROR nerr = NLBERR_INTERNAL_ERROR;
vector<ENGINEHANDLE> RemovedInterfaces;
TRACE_INFO(L"-> %!FUNC!(ehC=0x%lx)",ehCluster);
mfn_Lock();
do // while false
{
CEngineCluster *pECluster = m_mapIdToEngineCluster[ehCluster];
CClusterSpec *pCSpec = NULL;
BOOL fEmptyCluster = FALSE;
if (pECluster == NULL)
{
// Invalid ehCluster
TRACE_CRIT("%!FUNC! -- invalid ehCluster 0x%lx", ehCluster);
break;
}
pCSpec = &pECluster->m_cSpec;
fEmptyCluster = (pCSpec->m_ehInterfaceIdList.size()==0);
//
// fail if operations are pending on this cluster. We determine
// this indirectly by checking if we're allowed to start a
// cluster-wide operation, which will only succeed if
// there no ongoing operations on the cluster OR its interfaces.
//
BOOL fCanStart = FALSE;
nerr = mfn_ClusterOrInterfaceOperationsPendingLk(
pECluster,
REF fCanStart
);
if (NLBFAILED(nerr) || !fCanStart)
{
TRACE_CRIT("%!FUNC! Not deleting cluster eh0x%lx because of pending activity.",
ehCluster);
nerr = NLBERR_OTHER_UPDATE_ONGOING;
break;
}
if (!fEmptyCluster)
{
if (!fRemoveInterfaces)
{
TRACE_CRIT("%!FUNC! Not deleting cluster eh0x%lx because it's not empty",
ehCluster);
break;
}
RemovedInterfaces = pCSpec->m_ehInterfaceIdList; // vector copy
//
// We unlink all the interfaces from this cluster.
//
while(!pCSpec->m_ehInterfaceIdList.empty())
{
vector <ENGINEHANDLE>::iterator iItem
= pCSpec->m_ehInterfaceIdList.begin();
ENGINEHANDLE ehIF = *iItem;
CInterfaceSpec *pISpec = NULL;
//
// Unlink the interface from the cluster.
// (this is with the lock held)
//
pISpec = m_mapIdToInterfaceSpec[ehIF]; // map
if (pISpec != NULL)
{
if (pISpec->m_ehCluster == ehCluster)
{
pISpec->m_ehCluster = NULL;
//
// Delete the host and its interfaces if
// none of them are managed by nlbmanager (i.e., show
// up as members of a cluster managed by nlbmgr).
//
mfn_DeleteHostIfNotManagedLk(pISpec->m_ehHostId);
}
else
{
TRACE_CRIT(L"ehC(0x%x) points to ehI(0x%x), but ehI points to different cluster ehC(0x%x)",
ehCluster, ehIF, pISpec->m_ehCluster);
ASSERT(!"Cluser/interface handle corruption!");
}
}
pCSpec->m_ehInterfaceIdList.erase(iItem);
}
}
m_mapIdToEngineCluster.erase(ehCluster);
delete pECluster;
pECluster = NULL;
nerr = NLBERR_OK;
} while (FALSE);
mfn_Unlock();
if (nerr == NLBERR_OK)
{
//
// Notify the UI
//
for( int i = 0; i < RemovedInterfaces.size(); ++i )
{
ENGINEHANDLE ehIId = RemovedInterfaces[i];
m_pCallbacks->HandleEngineEvent(
IUICallbacks::OBJ_INTERFACE,
ehCluster,
ehIId,
IUICallbacks::EVT_INTERFACE_REMOVED_FROM_CLUSTER
);
}
m_pCallbacks->HandleEngineEvent(
IUICallbacks::OBJ_CLUSTER,
ehCluster,
ehCluster,
IUICallbacks::EVT_REMOVED
);
}
TRACE_INFO(L"<- %!FUNC!");
return;
}
NLBERROR
CNlbEngine::AutoExpandCluster(
IN ENGINEHANDLE ehClusterId
)
{
TRACE_INFO(L"-> %!FUNC!");
TRACE_INFO(L"<- %!FUNC!");
return NLBERR_OK;
}
NLBERROR
CNlbEngine::AddInterfaceToCluster(
IN ENGINEHANDLE ehClusterId,
IN ENGINEHANDLE ehInterfaceId
)
{
NLBERROR nerr = NLBERR_INTERNAL_ERROR;
ENGINEHANDLE ehIfId = ehInterfaceId;
TRACE_INFO(L"-> %!FUNC!");
mfn_Lock();
CInterfaceSpec *pISpec = NULL;
CClusterSpec *pCSpec = NULL;
do // while false
{
CEngineCluster *pECluster = NULL;
pECluster = m_mapIdToEngineCluster[ehClusterId]; // map
if (pECluster == NULL)
{
nerr = NLBERR_NOT_FOUND;
TRACE_CRIT("%!FUNC! -- could not find cluster associated with id 0x%lx",
(UINT) ehClusterId
);
break;
}
pCSpec = &pECluster->m_cSpec;
pISpec = m_mapIdToInterfaceSpec[ehInterfaceId]; // map
if (pISpec == NULL)
{
nerr = NLBERR_NOT_FOUND;
TRACE_CRIT("%!FUNC! -- could not find interface associated with id 0x%lx",
(UINT) ehInterfaceId
);
break;
}
//
// The interface is valid. Now push in the interface if it is not
// already a part of the this cluster.
//
if (pISpec->m_ehCluster != NULL)
{
if (pISpec->m_ehCluster != ehClusterId)
{
//
// We don't allow the same interface to be part of
// two clusters!
//
nerr = NLBERR_INTERNAL_ERROR;
TRACE_CRIT("%!FUNC! -- Interface eh 0x%lx is a member of an other cluster eh0x%lx.", ehIfId, pISpec->m_ehCluster);
break;
}
}
//
// Note: find is a pre-defined template function.
//
if(find(
pCSpec->m_ehInterfaceIdList.begin(),
pCSpec->m_ehInterfaceIdList.end(),
ehIfId
) != pCSpec->m_ehInterfaceIdList.end())
{
// item already exists.
// for now we'll ignore this.
if (pISpec->m_ehCluster != ehClusterId)
{
TRACE_CRIT("%!FUNC! -- ERROR Interface eh 0x%lx ehCluster doesn't match!", ehIfId);
nerr = NLBERR_INTERNAL_ERROR;
break;
}
}
else
{
pISpec->m_ehCluster = ehClusterId;
pCSpec->m_ehInterfaceIdList.push_back(ehIfId);
}
nerr = NLBERR_OK;
} while (FALSE);
mfn_Unlock();
if (nerr == NLBERR_OK)
{
//
// Inform the UI of the addition of a new interface under the
// specified cluster.
//
m_pCallbacks->HandleEngineEvent(
IUICallbacks::OBJ_INTERFACE,
ehClusterId,
ehInterfaceId,
IUICallbacks::EVT_INTERFACE_ADDED_TO_CLUSTER
);
}
TRACE_INFO(L"<- %!FUNC! returns 0x%lx", nerr);
return nerr;
}
NLBERROR
CNlbEngine::RemoveInterfaceFromCluster(
IN ENGINEHANDLE ehClusterId,
IN ENGINEHANDLE ehIfId
)
{
NLBERROR nerr = NLBERR_INTERNAL_ERROR;
TRACE_INFO(L"-> %!FUNC!");
BOOL fEmptyCluster = FALSE; // TRUE IFF no more interfaces in cluster.
mfn_Lock();
CInterfaceSpec *pISpec = NULL;
CClusterSpec *pCSpec = NULL;
do // while false
{
CEngineCluster *pECluster = m_mapIdToEngineCluster[ehClusterId]; // map
if (pECluster == NULL)
{
nerr = NLBERR_NOT_FOUND;
TRACE_CRIT("%!FUNC! -- could not find cluster associated with id 0x%lx",
(UINT) ehClusterId
);
break;
}
pCSpec = &pECluster->m_cSpec;
pISpec = m_mapIdToInterfaceSpec[ehIfId]; // map
if (pISpec == NULL)
{
nerr = NLBERR_NOT_FOUND;
TRACE_CRIT("%!FUNC! -- could not find interface associated with id 0x%lx",
(UINT) ehIfId
);
break;
}
vector <ENGINEHANDLE>::iterator iFoundItem;
//
// The interface is valid. No push in the interface if it is not
// already a part of the this cluster.
//
//
// Note: find is a pre-defined template function.
//
iFoundItem = find(
pCSpec->m_ehInterfaceIdList.begin(),
pCSpec->m_ehInterfaceIdList.end(),
ehIfId
);
if (iFoundItem != pCSpec->m_ehInterfaceIdList.end())
{
// item exists, remove it.
pCSpec->m_ehInterfaceIdList.erase(iFoundItem);
if (pISpec->m_ehCluster != ehClusterId)
{
// shouldn't get here!
ASSERT(FALSE);
TRACE_CRIT("%!FUNC!: ERROR pISpec->m_ehCluster(0x%lx) != ehCluster(0x%lx)", pISpec->m_ehCluster, ehClusterId);
}
else
{
fEmptyCluster = (pCSpec->m_ehInterfaceIdList.size()==0);
pISpec->m_ehCluster = NULL;
if (pCSpec->m_ehDefaultInterface == ehIfId)
{
//
// We're removing the interface whose properties are
// the basis of the cluser-wide view.
//
pCSpec->m_ehDefaultInterface = NULL;
}
nerr = NLBERR_OK;
}
}
else
{
// item doesn't exist.
}
} while (FALSE);
mfn_Unlock();
if (nerr == NLBERR_OK)
{
//
// Inform the UI of the removal of an interface under the
// specified cluster.
//
m_pCallbacks->HandleEngineEvent(
IUICallbacks::OBJ_INTERFACE,
ehClusterId,
ehIfId,
IUICallbacks::EVT_INTERFACE_REMOVED_FROM_CLUSTER
);
if (fEmptyCluster)
{
//
// The cluster is empty -- delete it.
//
this->DeleteCluster(ehClusterId, FALSE); // FALSE == don't remove IF.
}
}
TRACE_INFO(L"<- %!FUNC!");
return nerr;
}
NLBERROR
CNlbEngine::RefreshAllHosts(
void
)
{
TRACE_INFO(L"-> %!FUNC!");
/*
For each host in host map
Get Adapter List
- Delete all Interfaces that are no longer present
- Add/Update all host infos, one by one.
Do all this in the background.
*/
TRACE_INFO(L"<- %!FUNC!");
return NLBERR_OK;
}
NLBERROR
CNlbEngine::RefreshCluster(
IN ENGINEHANDLE ehCluster
)
{
TRACE_INFO(L"-> %!FUNC!");
/*
For each interface in cluster
- Add/Update interface info, one by one.
*/
TRACE_INFO(L"<- %!FUNC!");
return NLBERR_OK;
}
NLBERROR
CNlbEngine::mfn_RefreshInterface(
IN ENGINEHANDLE ehInterface
)
/*
Add/Update interface info, deleting interface info if it's no longer there.
Take/share code from RefreshHost
Get host connection string.
Get szNicGuid.
Ping host.
GetClusterConfiguration.
Update.
Notify UI of status change.
*/
{
CLocalLogger logger;
NLBERROR nerr = NLBERR_INTERNAL_ERROR;
WBEMSTATUS wStatus = WBEM_E_CRITICAL_ERROR;
ULONG uIpAddress=0;
WMI_CONNECTION_INFO ConnInfo;
NLB_EXTENDED_CLUSTER_CONFIGURATION NlbCfg; // class
LPCWSTR szNic = NULL;
_bstr_t bstrUserName;
_bstr_t bstrPassword;
_bstr_t bstrConnectionString;
_bstr_t bstrNicGuid;
_bstr_t bstrHostName;
BOOL fMisconfigured = FALSE;
LPCWSTR szHostName = NULL;
ENGINEHANDLE ehHost;
BOOL fCheckHost = FALSE;
TRACE_INFO(L"-> %!FUNC! (ehIF=0x%lx)", (UINT) ehInterface);
ZeroMemory(&ConnInfo, sizeof(ConnInfo));
//
// Get connection info from the interface's host.
//
{
mfn_Lock();
CHostSpec *pHSpec = NULL;
CInterfaceSpec *pISpec = NULL;
nerr = this->mfn_GetHostFromInterfaceLk(ehInterface,REF pISpec, REF pHSpec);
if (nerr != NLBERR_OK)
{
TRACE_CRIT("%!FUNC!: ERROR couldn't get info on this if id!");
mfn_Unlock();
goto end;
}
//
// We must make copies here because once we unlock
// we don't know what's going to happen to pHSpec.
//
bstrUserName = pHSpec->m_UserName;
bstrPassword = pHSpec->m_Password;
bstrConnectionString = pHSpec->m_ConnectionString;
bstrNicGuid = pISpec->m_Guid;
bstrHostName = pHSpec->m_MachineName;
ehHost = pISpec->m_ehHostId;
//
// If the host was previously marked unreachable, we'll
// try to check again if it's there (and update it's state).
//
fCheckHost = pHSpec->m_fUnreachable;
mfn_Unlock();
}
ConnInfo.szUserName = (LPCWSTR) bstrUserName;
ConnInfo.szPassword = (LPCWSTR) bstrPassword;
ConnInfo.szMachine = (LPCWSTR) bstrConnectionString;
if (fCheckHost)
{
nerr = mfn_CheckHost(&ConnInfo, ehHost);
if (NLBFAILED(nerr))
{
goto end;
}
}
szNic = (LPCWSTR) bstrNicGuid;
szHostName = (LPCWSTR) bstrHostName;
wStatus = NlbHostPing(ConnInfo.szMachine, 2000, &uIpAddress);
if (FAILED(wStatus))
{
m_pCallbacks->Log(
IUICallbacks::LOG_ERROR,
NULL, // szCluster
szHostName,
IDS_LOG_PING_FAILED,
ConnInfo.szMachine
);
//
// TODO update host
//
fMisconfigured = TRUE;
logger.Log(IDS_LOG_COULD_NOT_PING_HOST);
}
else
{
wStatus = NlbHostGetConfiguration(
&ConnInfo,
szNic,
&NlbCfg
);
if (FAILED(wStatus))
{
TRACE_CRIT(L"%!FUNC! Error reading extended configuration for %ws\n", szNic);
m_pCallbacks->Log(
IUICallbacks::LOG_ERROR,
NULL, // szCluster
szHostName,
IDS_LOG_COULD_NOT_GET_IF_CONFIG,
szNic,
(UINT) wStatus
);
fMisconfigured = TRUE;
logger.Log(IDS_LOG_COULD_NOT_READ_IF_CONFIG);
}
}
//
// Now that we've read the latest cfg info onto NlbCfg, let's update it
// (or mark the IF as misconfigured if there's been an error.)
//
{
CInterfaceSpec *pISpec = NULL;
mfn_Lock();
pISpec = m_mapIdToInterfaceSpec[ehInterface]; // map
if (pISpec != NULL)
{
pISpec->m_fReal = TRUE;
if (!fMisconfigured)
{
wStatus = pISpec->m_NlbCfg.Update(&NlbCfg);
if (FAILED(wStatus))
{
TRACE_CRIT("%!FUNC! error updating nlbcfg for eh%lx", ehInterface);
fMisconfigured = TRUE;
logger.Log(IDS_LOG_FAILED_UPDATE);
}
//
// Update the host's connection IP address
//
if (uIpAddress != 0)
{
CHostSpec *pHSpec = NULL;
CInterfaceSpec *pTmpISpec = NULL;
nerr = this->mfn_GetHostFromInterfaceLk(
ehInterface,
REF pTmpISpec,
REF pHSpec
);
if (nerr == NLBERR_OK)
{
pHSpec->m_ConnectionIpAddress = uIpAddress;
}
}
}
//
// Set/clear the misconfiguration state.
//
{
LPCWSTR szDetails = NULL;
UINT Size = 0;
if (fMisconfigured)
{
logger.ExtractLog(szDetails, Size);
}
mfn_SetInterfaceMisconfigStateLk(pISpec, fMisconfigured, szDetails);
}
}
mfn_Unlock();
}
if (fMisconfigured)
{
//
// We couldn't read the latest settings for some reason --
// check connectivity to the host and update the host status if
// necessary.
//
nerr = mfn_CheckHost(&ConnInfo, ehHost);
if (NLBOK(nerr))
{
//
// We still want to fail this because we couldn't get
// the updated configuration.
//
nerr = NLBERR_INVALID_CLUSTER_SPECIFICATION;
}
}
else
{
nerr = NLBERR_OK;
}
end:
TRACE_INFO(L"<- %!FUNC!");
return nerr;
}
NLBERROR
CNlbEngine::RefreshInterface(
IN ENGINEHANDLE ehInterface,
IN BOOL fNewOperation,
IN BOOL fClusterWide
)
/*
Add/Update interface info, deleting interface info if it's no longer there.
Take/share code from RefreshHost
Get host connection string.
Get szNicGuid.
Ping host.
GetClusterConfiguration.
Update.
Notify UI of status change.
*/
{
CLocalLogger logger;
NLBERROR nerr = NLBERR_INTERNAL_ERROR;
BOOL fMisconfigured = FALSE;
BOOL fRemoveInterfaceFromCluster = FALSE;
BOOL fStopOperationOnExit = FALSE;
ENGINEHANDLE ehCluster = NULL;
TRACE_INFO(L"-> %!FUNC! (ehIF=0x%lx)", (UINT) ehInterface);
if (fNewOperation)
{
//
// This function is to be run in the context of a NEW operation.
// Verify that we can do a refresh at this time, and if so, start an
// operation to track the refresh.
//
CInterfaceSpec *pISpec = NULL;
mfn_Lock();
pISpec = m_mapIdToInterfaceSpec[ehInterface]; // map
if (pISpec == NULL)
{
TRACE_CRIT("%!FUNC!: ERROR couldn't get info on this if id!");
goto end_unlock;
}
if (!fClusterWide)
{
ehCluster = pISpec->m_ehCluster;
if (ehCluster != NULL)
{
//
// Make sure that there's no pending cluster-wide operation
// going on for the cluster that this IF is a part of.
//
CEngineCluster *pECluster = NULL;
pECluster = m_mapIdToEngineCluster[ehCluster]; // map
if (pECluster != NULL)
{
if (pECluster->m_cSpec.m_ehPendingOperation != NULL)
{
nerr = NLBERR_BUSY;
TRACE_CRIT("%!FUNC!: ehIF 0x%lx: Can't proceed because of existing op 0x%lx",
ehInterface,
pECluster->m_cSpec.m_ehPendingOperation
);
goto end_unlock;
}
}
}
}
//
// Now try to start an operation...
//
{
ENGINEHANDLE ExistingOp = NULL;
nerr = mfn_StartInterfaceOperationLk(
ehInterface,
NULL, // pvCtxt
GETRESOURCEIDSTRING(IDS_LOG_REFRESH_INTERFACE),
&ExistingOp
);
if (NLBFAILED(nerr))
{
goto end_unlock;
}
//
// We did start the operation -- so we keep track of this, so that
// we stop the operation on exit.
//
fStopOperationOnExit = TRUE;
}
mfn_Unlock();
}
//
// Here's where we actually refresh the interface.
//
nerr = mfn_RefreshInterface(ehInterface);
if (!NLBOK(nerr))
{
mfn_Lock();
goto end_unlock;
}
//
// Now let's analyze the result ...
//
{
CInterfaceSpec *pISpec = NULL;
mfn_Lock();
pISpec = m_mapIdToInterfaceSpec[ehInterface]; // map
if (pISpec != NULL)
{
fMisconfigured = pISpec->m_fMisconfigured;
ehCluster = pISpec->m_ehCluster;
if (!fMisconfigured)
{
if (ehCluster != NULL)
{
if (!pISpec->m_NlbCfg.IsNlbBound())
{
//
// NLB is not bound on this interface --
// remove this interface from the cluster.
fRemoveInterfaceFromCluster = TRUE;
}
else
{
nerr = this->mfn_AnalyzeInterfaceLk(ehInterface, REF logger);
if (NLBFAILED(nerr))
{
fMisconfigured = TRUE;
}
}
}
//
// Set the new misconfiguration state.
//
{
LPCWSTR szDetails = NULL;
UINT Size = 0;
if (fMisconfigured)
{
logger.ExtractLog(szDetails, Size);
}
mfn_SetInterfaceMisconfigStateLk(pISpec, fMisconfigured, szDetails);
}
}
}
mfn_Unlock();
}
if (fRemoveInterfaceFromCluster)
{
this->RemoveInterfaceFromCluster(ehCluster, ehInterface);
}
else
{
//
// Report state
//
if (fMisconfigured)
{
//
// Log ...
//
LPCWSTR szDetails = NULL;
LPCWSTR szCluster = NULL;
LPCWSTR szHostName = NULL;
LPCWSTR szInterface = NULL;
UINT Size = 0;
ENGINEHANDLE ehHost;
_bstr_t bstrDisplayName;
_bstr_t bstrFriendlyName;
_bstr_t bstrHostName;
_bstr_t bstrIpAddress;
nerr = this->GetInterfaceIdentification(
ehInterface,
REF ehHost,
REF ehCluster,
REF bstrFriendlyName,
REF bstrDisplayName,
REF bstrHostName
);
if (NLBOK(nerr))
{
_bstr_t bstrDomainName;
_bstr_t bstrClusterDisplayName;
nerr = this->GetClusterIdentification(
ehCluster,
REF bstrIpAddress,
REF bstrDomainName,
REF bstrClusterDisplayName
);
if (NLBOK(nerr))
{
szCluster = bstrIpAddress;
}
szHostName = bstrHostName;
szInterface = bstrFriendlyName;
}
logger.ExtractLog(szDetails, Size);
IUICallbacks::LogEntryHeader Header;
Header.szDetails = szDetails;
Header.type = IUICallbacks::LOG_ERROR;
Header.szCluster = szCluster;
Header.szHost = szHostName;
Header.szInterface = szInterface;
m_pCallbacks->LogEx(
&Header,
IDS_LOG_INTERFACE_MISCONFIGURATION
);
}
else
{
ControlClusterOnInterface( ehInterface, WLBS_QUERY, NULL, NULL, 0, FALSE);
}
}
nerr = NLBERR_OK;
mfn_Lock();
//
// Fall through ...
//
end_unlock:
if (fStopOperationOnExit)
{
mfn_StopInterfaceOperationLk(ehInterface);
}
mfn_Unlock();
if (fStopOperationOnExit && !fRemoveInterfaceFromCluster)
{
//
// Notify the UI of this update...
// (but only if we've not already removed it from
// the cluster!)
//
m_pCallbacks->HandleEngineEvent(
IUICallbacks::OBJ_INTERFACE,
ehCluster,
ehInterface,
IUICallbacks::EVT_STATUS_CHANGE
);
}
TRACE_INFO(L"<- %!FUNC!");
return nerr;
}
NLBERROR
CNlbEngine::AnalyzeCluster(
ENGINEHANDLE ehClusterId
)
{
TRACE_INFO(L"-> %!FUNC!");
/*
TODO: consider doing this with a specific host in mind
For each IF I1
AnalyzeHost(host-of(I1))
For each other IF I2
AnalyzeTwoHosts(I1, I2)
AnalyzeTwoHosts(I1, I2):
- check that cluster params match
- check that port rules are compatible
- check that host properties do not collide.
- check dedicated IP subnets match.
AnalyzeHost(H1)
For each IF I1
AnalyzeSingleIf(I1) (including checking dedicated ips)
*/
TRACE_INFO(L"<- %!FUNC!");
return NLBERR_OK;
}
NLBERROR
CNlbEngine::mfn_AnalyzeInterfaceLk(
ENGINEHANDLE ehInterface,
CLocalLogger &logger
)
/*
-- check interface props against cluster properties
-- if cluster props match out,
for each host id NOT marked fMisconfigured,
check host properties.
-- Does NOT mark fMisconfigured if error detected -- caller is expected
to do so.
-- Spews stuff to log regarding any misconfigurations.
*/
{
NLBERROR nerr = NLBERR_INVALID_CLUSTER_SPECIFICATION;
const CEngineCluster *pECluster = NULL;
const CInterfaceSpec *pISpec = NULL;
BOOL fIgnoreRctPassword = FALSE;
TRACE_INFO(L"-> %!FUNC!");
pISpec = m_mapIdToInterfaceSpec[ehInterface]; // map
if (pISpec == NULL)
{
nerr = NLBERR_INTERFACE_NOT_FOUND;
goto end;
}
//
// If interface is NOT part of a cluster, we're done.
//
if (pISpec->m_ehCluster == NULL)
{
TRACE_CRIT("ehIF 0x%lx m_ehCluster member is NULL!", ehInterface);
goto end;
}
//
// Check the interface against itself ...
//
{
nerr = AnalyzeNlbConfiguration(REF pISpec->m_NlbCfg, logger);
if (NLBFAILED(nerr))
{
goto end;
}
}
//
// Get the cluster data that this interface is a part of.
//
pECluster = m_mapIdToEngineCluster[pISpec->m_ehCluster]; // map
if (pECluster == NULL)
{
TRACE_CRIT(L"ehIF 0x%lx m_ehCluster member 0x%lx is INVALID!",
ehInterface, pISpec->m_ehCluster);
nerr = NLBERR_INTERNAL_ERROR;
goto end;
}
//
// Check interface against cluster.
//
fIgnoreRctPassword = pECluster->m_cSpec.m_fNewRctPassword;
nerr = analyze_nlbcfg(
REF pISpec->m_NlbCfg,
REF pECluster->m_cSpec.m_ClusterNlbCfg,
(LPCWSTR) GETRESOURCEIDSTRING(IDS_CLUSTER),
// L"Cluster",
TRUE, // TRUE == ClusterProps
fIgnoreRctPassword, // TRUE==Disable RCT password check
REF logger
);
if (NLBFAILED(nerr))
{
//
// If analyzing against cluster props fails, we don't bother
// analyzing against other hosts...
//
TRACE_CRIT(L"analyze_nlbcfg returns error 0x%lx", nerr);
goto end;
}
//
// Now check against all hosts before us in the list of hosts in the
// cluster -- but only those that are not already marked misconfigured.
//
//
{
const vector<ENGINEHANDLE> &InterfaceList =
pECluster->m_cSpec.m_ehInterfaceIdList;
for( int i = 0; i < InterfaceList.size(); ++i )
{
ENGINEHANDLE ehIOther = InterfaceList[i];
const CInterfaceSpec *pISpecOther = NULL;
WCHAR rgOtherDescription[256];
*rgOtherDescription = 0;
if (ehIOther == ehInterface)
{
//
// We've reached the interface being analyzed -- we don't
// compare with any of the remaining interfaces.
//
break;
}
pISpecOther = m_mapIdToInterfaceSpec[ehIOther]; // map
if (pISpecOther == NULL)
{
TRACE_CRIT("Unexpected: NULL pISpec for ehInterface 0x%lx",
ehIOther);
continue;
}
//
// We don't compare of the other interface is marked misconfigured,
// or is not bound with NLB with valid nlb config data.
//
if ( pISpecOther->m_fMisconfigured
|| !pISpecOther->m_NlbCfg.IsValidNlbConfig())
{
TRACE_VERB("%!FUNC!: Skipping misconfigured ISpec with ehInt 0x%lx",
ehIOther);
continue;
}
// Skip the other interface if it's properties are being updated.
//
if (pISpecOther->m_ehPendingOperation != NULL)
{
TRACE_VERB("%!FUNC!: Skipping ISpec with ehInt 0x%lx because of pending OP on it",
ehIOther);
continue;
}
//
// Create the description string of the other adapter.
//
{
WBEMSTATUS wStat;
LPWSTR szAdapter = NULL;
LPCWSTR szHostName = pISpecOther->m_bstrMachineName;
wStat = pISpecOther->m_NlbCfg.GetFriendlyName(&szAdapter);
if (FAILED(wStat))
{
szAdapter = NULL;
}
StringCbPrintf(
rgOtherDescription,
sizeof(rgOtherDescription),
L"%ws(%ws)",
(szHostName==NULL ? L"" : szHostName),
(szAdapter==NULL ? L"" : szAdapter)
);
delete szAdapter;
}
//
// Let's check this host's config with the other host's
//
NLBERROR nerrTmp;
nerrTmp = analyze_nlbcfg(
REF pISpec->m_NlbCfg,
REF pISpecOther->m_NlbCfg,
rgOtherDescription,
FALSE, // FALSE == Check host-specific props
FALSE, // FALSE == Enable remote-control password check
REF logger
);
if (NLBFAILED(nerrTmp))
{
nerr = nerrTmp; // so we don't overwrite failure with success
}
}
}
end:
TRACE_INFO(L"<- %!FUNC! returns 0x%lx", nerr);
return nerr;
}
NLBERROR
analyze_nlbcfg(
IN const NLB_EXTENDED_CLUSTER_CONFIGURATION &NlbCfg,
IN const NLB_EXTENDED_CLUSTER_CONFIGURATION &OtherNlbCfg,
IN LPCWSTR szOtherDescription, OPTIONAL
IN BOOL fClusterProps,
IN BOOL fDisablePasswordCheck,
IN OUT CLocalLogger &logger
/*
Analyze the NLB configuration NlbCfg against OtherNlbCfg.
If fClusterProps, treat OtherNlbCfg as cluster-wide props, else
treat OtherNlbCfg as the properties of a specific host.
When logging errors to logger, use szOtherDescription to refer to
OtherNlbCfg.
if szOtherDesctiption is NULL, DO NOT log.
Return value:
NLB_OK if the configurations are compatible.
NLBERR_INVALID_CLUSTER_SPECIFICATION if the NlbParams are incompatible.
NLBERR_INVALID_IP_ADDRESS_SPECIFICATION
NLBERR_SUBNET_MISMATCH
NLBERR_NLB_NOT_INSTALLED
or some other NLBERR_XXX error
*/
)
{
NLBERROR nerr = NLBERR_INVALID_CLUSTER_SPECIFICATION;
BOOL fMisconfigured = FALSE; // Start out assuming no misconfig.
#define LOG(_expr) if (szOtherDescription!=NULL) {_expr;}
if (szOtherDescription != NULL)
{
//
// We'll call ourselves RECURSIVELY just to determine up-front
// if any conflicts were detected.
// This is so we can put a log entry saying that conflicts were
// detected with szOtherDescription.
// Subsequent log entries do not specifiy szOtherDescription.
//
CLocalLogger null_logger;
nerr = analyze_nlbcfg( // RECURSIVE CALL
REF NlbCfg,
REF OtherNlbCfg,
NULL, // NULL == don't log.
fClusterProps,
fDisablePasswordCheck,
REF null_logger
);
if (NLBFAILED(nerr))
{
//
// There was a failure -- so we make a log entry saying so
//
logger.Log(IDS_LOG_CONFIG_CONFLICTS_WITH_OTHER, szOtherDescription);
}
else
{
// looks good...
goto end;
}
}
//
// Check cluster properties
//
{
if (NlbCfg.NlbParams.mcast_support != OtherNlbCfg.NlbParams.mcast_support)
{
LOG(logger.Log(IDS_LOG_CLUSTER_MODE_DIFFERS))
fMisconfigured = TRUE;
}
else if (NlbCfg.NlbParams.mcast_support &&
NlbCfg.NlbParams.fIGMPSupport != OtherNlbCfg.NlbParams.fIGMPSupport)
{
LOG(logger.Log(IDS_LOG_CLUSTER_MULTICAST_MODE_DIFFERS))
fMisconfigured = TRUE;
}
if (wcscmp(NlbCfg.NlbParams.cl_ip_addr, OtherNlbCfg.NlbParams.cl_ip_addr))
{
LOG(logger.Log(IDS_LOG_CIP_DIFFERS))
fMisconfigured = TRUE;
}
if (wcscmp(NlbCfg.NlbParams.cl_net_mask, OtherNlbCfg.NlbParams.cl_net_mask))
{
LOG(logger.Log(IDS_LOG_CIPMASK_DIFFERS))
fMisconfigured = TRUE;
}
if (wcscmp(NlbCfg.NlbParams.domain_name, OtherNlbCfg.NlbParams.domain_name))
{
LOG(logger.Log(IDS_LOG_DOMAIN_NAME_DIFFERS))
fMisconfigured = TRUE;
}
//
// Remote control
//
if (NlbCfg.GetRemoteControlEnabled() !=
OtherNlbCfg.GetRemoteControlEnabled())
{
LOG(logger.Log(IDS_LOG_RCT_DIFFERS))
fMisconfigured = TRUE;
}
else if (NlbCfg.GetRemoteControlEnabled() && !fDisablePasswordCheck)
{
//
// Check the password...
//
DWORD dw = CfgUtilGetHashedRemoteControlPassword(&NlbCfg.NlbParams);
DWORD dw1=CfgUtilGetHashedRemoteControlPassword(&OtherNlbCfg.NlbParams);
if (dw!=dw1)
{
LOG(logger.Log(IDS_LOG_RCT_PWD_DIFFERS, dw, dw1 ))
fMisconfigured = TRUE;
}
}
}
//
// Check port rules.
//
{
WLBS_PORT_RULE *pIRules = NULL;
WLBS_PORT_RULE *pCRules = NULL;
UINT NumIRules=0;
UINT NumCRules=0;
WBEMSTATUS wStat;
wStat = CfgUtilGetPortRules(&NlbCfg.NlbParams, &pIRules, &NumIRules);
if (FAILED(wStat))
{
LOG(logger.Log(IDS_LOG_CANT_EXTRACT_PORTRULES))
fMisconfigured = TRUE;
goto end;
}
wStat = CfgUtilGetPortRules(&OtherNlbCfg.NlbParams, &pCRules, &NumCRules);
if (FAILED(wStat))
{
LOG(logger.Log(IDS_LOG_CANT_EXTRACT_OTHER_PORT_RULES, szOtherDescription))
fMisconfigured = TRUE;
goto end;
}
if (NumIRules != NumCRules)
{
LOG(logger.Log(IDS_LOG_PORT_RULE_COUNT_DIFFERS))
// keep going.
fMisconfigured = TRUE;
}
else
{
//
// Let's assume that the order is the same, because I think it's
// returned sorted.
//
for (UINT u = 0; u< NumIRules; u++)
{
WLBS_PORT_RULE IRule = pIRules[u]; // struct copy
WLBS_PORT_RULE CRule = pCRules[u]; // struct copy
if (lstrcmpi(IRule.virtual_ip_addr, CRule.virtual_ip_addr))
{
LOG(logger.Log(IDS_LOG_PORT_RULE_CIP_DIFFERS,u+1))
fMisconfigured = TRUE;
continue;
}
if (IRule.start_port != CRule.start_port)
{
LOG(logger.Log(IDS_LOG_PORT_RULE_START_DIFFERS, u+1))
fMisconfigured = TRUE;
continue;
}
if (IRule.end_port != CRule.end_port)
{
LOG(logger.Log(IDS_LOG_PORT_RULE_END_DIFFERS, u+1))
fMisconfigured = TRUE;
}
if (IRule.protocol != CRule.protocol)
{
LOG(logger.Log(IDS_LOG_PORT_RULE_PROT_DIFFERS, u+1))
fMisconfigured = TRUE;
}
if (IRule.mode != CRule.mode)
{
LOG(logger.Log(IDS_LOG_PORT_RULE_MODE_DIFFERS, u+1))
fMisconfigured = TRUE;
}
if (IRule.mode == CVY_MULTI)
{
// Check that affinity matches -- none/single/class-C
if (IRule.mode_data.multi.affinity != CRule.mode_data.multi.affinity)
{
LOG(logger.Log(IDS_LOG_PORT_RULE_AFFINITY_DIFFERS, u+1))
fMisconfigured = TRUE;
}
}
if (!fClusterProps && IRule.mode == CVY_SINGLE)
{
if (IRule.mode_data.single.priority
== CRule.mode_data.single.priority)
{
LOG(logger.Log(IDS_LOG_PORT_RULE_PRIORITY_CONFLICT, u+1))
fMisconfigured = TRUE;
}
}
}
}
delete[] pIRules;
delete[] pCRules;
}
//
// Interface checks out against the cluster-wide parameters;
// Now check this interface's parameters against itself -- things
// like the dedicated IP address is bound on the nic itself and is the
// the first address on the NIC, etc.
//
if (!fClusterProps)
{
if (!NlbCfg.IsBlankDedicatedIp())
{
if (!wcscmp(NlbCfg.NlbParams.ded_ip_addr, OtherNlbCfg.NlbParams.ded_ip_addr))
{
//
// Same dedicated ip and it's not blank!
//
LOG(logger.Log(IDS_LOG_DIP_CONFLICT))
fMisconfigured = TRUE;
}
}
// Let's check host priority.
if (NlbCfg.NlbParams.host_priority == OtherNlbCfg.NlbParams.host_priority)
{
LOG(logger.Log(IDS_LOG_HOST_PRIORITY_CONFLICT))
fMisconfigured = TRUE;
}
}
nerr = NLBERR_OK;
end:
if (NLBOK(nerr) && fMisconfigured)
{
nerr = NLBERR_INVALID_CLUSTER_SPECIFICATION;
}
return nerr;
}
NLBERROR
CNlbEngine::GetInterfaceSpec(
IN ENGINEHANDLE ehInterfaceId,
OUT CInterfaceSpec& refISpec
)
{
// TRACE_INFO(L"-> %!FUNC!");
NLBERROR err = NLBERR_OK;
mfn_Lock();
CInterfaceSpec *pISpec = m_mapIdToInterfaceSpec[ehInterfaceId]; // map
if (pISpec == NULL)
{
err = NLBERR_INTERFACE_NOT_FOUND;
}
else
{
refISpec.Copy(*pISpec);
}
mfn_Unlock();
// TRACE_INFO(L"<- %!FUNC!");
return err;
}
VOID
CNlbEngine::mfn_GetLogStrings(
IN WLBS_OPERATION_CODES Operation,
IN LPCWSTR szVip,
IN DWORD * pdwPortNum,
IN DWORD dwOperationStatus,
IN DWORD dwClusterOrPortStatus,
OUT IUICallbacks::LogEntryType & LogLevel,
OUT _bstr_t & OperationStr,
OUT _bstr_t & OperationStatusStr,
OUT _bstr_t & ClusterOrPortStatusStr)
{
if (szVip && pdwPortNum)
{
TRACE_INFO(L"-> %!FUNC! Operation : %d, Vip : %ls, Port : %u, Operation Status : %u, Port Status : %u",
Operation, szVip, *pdwPortNum, dwOperationStatus, dwClusterOrPortStatus);
}
else
{
TRACE_INFO(L"-> %!FUNC! Operation : %d, Operation Status : %u, Cluster Status : %u",
Operation, dwOperationStatus, dwClusterOrPortStatus);
}
struct STATUS_TO_DESCR_MAP
{
DWORD dwStatus;
DWORD dwResourceId;
}
AllNlbStatusToDescrMap[] =
{
// Cluster States
{WLBS_CONVERGING, IDS_STATE_CONVERGING},// Converging
{WLBS_CONVERGED, IDS_STATE_CONVERGED}, // Converged as non-default, we do not specify "default"/"non-default" in the description 'cos there is a
{WLBS_DEFAULT, IDS_STATE_CONVERGED}, // Converged as default, transient case where for a short duration, the default node shows up a non-default
{WLBS_DRAINING, IDS_STATE_CONVERGED_DRAINING}, // Converged, but draining
// Port States
{NLB_PORT_RULE_NOT_FOUND, IDS_PORT_RULE_NOT_FOUND},
{NLB_PORT_RULE_ENABLED, IDS_PORT_RULE_ENABLED},
{NLB_PORT_RULE_DISABLED, IDS_PORT_RULE_DISABLED},
{NLB_PORT_RULE_DRAINING, IDS_PORT_RULE_DRAINING},
// Operation Specific errors
{WLBS_SUSPENDED, IDS_HOST_SUSPENDED}, // start/stop/drain_stop/enable/disable/drain/query-failure-host is suspended
{WLBS_STOPPED, IDS_HOST_STOPPED}, // enable/disable/drain/query-failure-host is already stopped
{WLBS_BAD_PARAMS, IDS_BAD_PARAMS}, // start-failure-host is stopped due to bad parameters
{WLBS_NOT_FOUND, IDS_NOT_FOUND}, // enable/disable/drain-failure-not found
{WLBS_DISCONNECTED, IDS_DISCONNECTED}, // query-failure-media disconnected
// Generic errors
{WLBS_BAD_PASSW, IDS_BAD_PASSWORD}, // *-failure-Bad password
{WLBS_FAILURE, IDS_FAILURE}, // *-failure-critical error
{WLBS_REFUSED, IDS_REFUSED}, // *-failure-request refused by BDA
{WLBS_IO_ERROR, IDS_IO_ERROR}, // *-failure-error trying to connect to nlb driver
// Success values
{WLBS_OK, IDS_EMPTY_STRING}, // Success
{WLBS_ALREADY, IDS_ALREADY}, // host is already in this state
{WLBS_DRAIN_STOP, IDS_DRAIN_STOP}, // was draining
};
struct OPERATION_TO_DESCR_MAP
{
WLBS_OPERATION_CODES Operation;
DWORD dwResourceId;
bool bClusterOperation;
}
OperationToDescrMap[] =
{
{WLBS_START, IDS_COMMAND_START, true},
{WLBS_STOP, IDS_COMMAND_STOP, true},
{WLBS_DRAIN, IDS_COMMAND_DRAINSTOP, true},
{WLBS_SUSPEND, IDS_COMMAND_SUSPEND, true},
{WLBS_RESUME, IDS_COMMAND_RESUME, true},
{WLBS_QUERY, IDS_COMMAND_QUERY, true},
{WLBS_PORT_ENABLE, IDS_COMMAND_ENABLE, false},
{WLBS_PORT_DISABLE, IDS_COMMAND_DISABLE, false},
{WLBS_PORT_DRAIN, IDS_COMMAND_DRAIN, false},
{WLBS_QUERY_PORT_STATE, IDS_COMMAND_QUERY_PORT, false}
};
bool bClusterOperation;
DWORD dwResourceId, dwIdx, dwClusterOrPortResourceId, dwMaxOperations;
WCHAR wcTempStr[1024];
// Initialize log level to Informational
LogLevel = IUICallbacks::LOG_INFORMATIONAL;
// Initialize all return strings to empty string
OperationStr = GETRESOURCEIDSTRING(IDS_EMPTY_STRING);
OperationStatusStr = OperationStr;
ClusterOrPortStatusStr = OperationStr;
// Form the "Operation" string
dwMaxOperations = sizeof(OperationToDescrMap)/sizeof(OperationToDescrMap[0]);
for (dwIdx = 0; dwIdx < dwMaxOperations; dwIdx++)
{
if (OperationToDescrMap[dwIdx].Operation == Operation)
{
dwResourceId = OperationToDescrMap[dwIdx].dwResourceId;
bClusterOperation = OperationToDescrMap[dwIdx].bClusterOperation;
break;
}
}
if (dwIdx == dwMaxOperations)
{
dwResourceId = IDS_UNKNOWN;
bClusterOperation = true; // really a don't care
}
// If a cluster operation, merely assign the string, else (ie. port operation),
// put in the vip & port information as well
if (bClusterOperation)
{
ARRAYSTRCPY(wcTempStr, GETRESOURCEIDSTRING(dwResourceId));
}
else // Port operation
{
if (_wcsicmp(szVip, CVY_DEF_ALL_VIP) == 0)
{
StringCbPrintf(
wcTempStr,
sizeof(wcTempStr),
GETRESOURCEIDSTRING(dwResourceId), (LPWSTR)GETRESOURCEIDSTRING(IDS_ALL_VIP_DESCR), *pdwPortNum);
}
else
{
StringCbPrintf(
wcTempStr,
sizeof(wcTempStr),
GETRESOURCEIDSTRING(dwResourceId), szVip, *pdwPortNum);
}
}
OperationStr = wcTempStr;
// Form the "Operation Status" string
// Could take one of the following forms :
// SUCCESS,
// SUCCESS (Note : XXXX),
// FAILURE (Cause : XXXX)
if (dwOperationStatus == WLBS_OK)
{
ARRAYSTRCPY(
wcTempStr,
GETRESOURCEIDSTRING(IDS_SUCCESS_AND_COMMA)
);
}
else if (dwOperationStatus == WLBS_ALREADY)
{
if (bClusterOperation)
{
dwResourceId = IDS_HOST_ALREADY_STATE;
}
else // Port operation
{
dwResourceId = IDS_PORT_ALREADY_STATE;
}
StringCbPrintf(
wcTempStr,
sizeof(wcTempStr),
GETRESOURCEIDSTRING(IDS_SUCCESS_AND_NOTE), (LPWSTR)GETRESOURCEIDSTRING(dwResourceId)
);
}
else if ((dwOperationStatus == WLBS_DRAIN_STOP)
&& ((Operation == WLBS_START) || (Operation == WLBS_STOP) || (Operation == WLBS_SUSPEND))
)
{
LogLevel = IUICallbacks::LOG_WARNING;
StringCbPrintf(
wcTempStr,
sizeof(wcTempStr),
GETRESOURCEIDSTRING(IDS_SUCCESS_AND_NOTE), (LPWSTR)GETRESOURCEIDSTRING(IDS_DRAIN_STOP)
);
}
else if ((dwOperationStatus == WLBS_STOPPED)
&& ((Operation == WLBS_DRAIN) || (Operation == WLBS_SUSPEND))
)
{
LogLevel = IUICallbacks::LOG_WARNING;
if (Operation == WLBS_DRAIN)
{
dwResourceId = IDS_ALREADY_STOPPED;
}
else // Suspend
{
dwResourceId = IDS_HOST_STOPPED;
}
StringCbPrintf(
wcTempStr,
sizeof(wcTempStr),
GETRESOURCEIDSTRING(IDS_SUCCESS_AND_NOTE), (LPWSTR)GETRESOURCEIDSTRING(dwResourceId)
);
}
else // All other operation statuses
{
// We get here only on a failure
LogLevel = IUICallbacks::LOG_ERROR;
dwMaxOperations = sizeof(AllNlbStatusToDescrMap)/sizeof(AllNlbStatusToDescrMap[0]);
// Get the "Cause" string
for (dwIdx = 0; dwIdx < dwMaxOperations; dwIdx++)
{
if (AllNlbStatusToDescrMap[dwIdx].dwStatus == dwOperationStatus)
{
dwResourceId = AllNlbStatusToDescrMap[dwIdx].dwResourceId;
break;
}
}
if (dwIdx == dwMaxOperations)
{
dwResourceId = IDS_UNKNOWN;
}
StringCbPrintf(
wcTempStr,
sizeof(wcTempStr),
GETRESOURCEIDSTRING(IDS_FAILURE_AND_CAUSE), (LPWSTR)GETRESOURCEIDSTRING(dwResourceId)
);
}
OperationStatusStr = wcTempStr;
// If the operation's status is failure, return
if (LogLevel == IUICallbacks::LOG_ERROR)
{
TRACE_INFO(L"<- %!FUNC!, returning operation status : failure");
return;
}
// Get Cluster or Port Status
if (bClusterOperation)
{
dwClusterOrPortResourceId = IDS_HOST_STATE;
}
else // Port Operation
{
dwClusterOrPortResourceId = IDS_PORT_RULE_STATE;
}
// Get the "ClusterOrPortState" string
dwMaxOperations = sizeof(AllNlbStatusToDescrMap)/sizeof(AllNlbStatusToDescrMap[0]);
for (dwIdx = 0; dwIdx < dwMaxOperations; dwIdx++)
{
if (AllNlbStatusToDescrMap[dwIdx].dwStatus == dwClusterOrPortStatus)
{
dwResourceId = AllNlbStatusToDescrMap[dwIdx].dwResourceId;
break;
}
}
if (dwIdx == dwMaxOperations)
{
dwResourceId = IDS_UNKNOWN;
}
StringCbPrintf(
wcTempStr,
sizeof(wcTempStr),
GETRESOURCEIDSTRING(dwClusterOrPortResourceId), (LPWSTR)GETRESOURCEIDSTRING(dwResourceId)
);
ClusterOrPortStatusStr = wcTempStr;
// Determine if the value returned for Cluster/Port state is valid & if not, set the log level to "error"
if (bClusterOperation)
{
switch(dwClusterOrPortStatus)
{
case WLBS_CONVERGING:
case WLBS_CONVERGED:
case WLBS_DEFAULT:
case WLBS_DRAINING:
case WLBS_STOPPED: // really a failure in the sense of not being able to get the host map, but not flagging it here
case WLBS_SUSPENDED:// 'cos it is a "normal" case
break;
default:
LogLevel = IUICallbacks::LOG_ERROR;
break;
}
}
else // Port operation
{
switch(dwClusterOrPortStatus)
{
case NLB_PORT_RULE_ENABLED:
case NLB_PORT_RULE_DISABLED:
case NLB_PORT_RULE_DRAINING:
break;
default:
LogLevel = IUICallbacks::LOG_ERROR;
break;
}
}
TRACE_INFO(L"<- %!FUNC!");
return;
}
NLBERROR
CNlbEngine::ControlClusterOnInterface(
IN ENGINEHANDLE ehInterfaceId,
IN WLBS_OPERATION_CODES Operation,
IN CString szVipArray[],
IN DWORD pdwPortNumArray[],
IN DWORD dwNumOfPortRules,
IN BOOL fNewOperation
)
{
TRACE_INFO(L"-> %!FUNC!");
LPCWSTR szNicGuid, szHostName, szClusterIp;
NLBERROR err = NLBERR_OK;
CHostSpec *pHSpec = NULL;
CInterfaceSpec *pISpec = NULL;
DWORD dwOperationStatus, dwClusterOrPortStatus, dwHostMap, dwNumOfIterations, dwIdx;
WBEMSTATUS Status;
BOOL bClusterOperation;
IUICallbacks::LogEntryType LogLevel;
_bstr_t OperationStr, OperationStatusStr, ClusterOrPortStatusStr;
BOOL fStopOperationOnExit = FALSE;
if (fNewOperation)
{
//
// This function is to be run in the context of a NEW operation.
// Verify that we can do a control op at this time, and if so, start an
// operation to track the control.
//
mfn_Lock();
pISpec = m_mapIdToInterfaceSpec[ehInterfaceId]; // map
if (pISpec == NULL)
{
TRACE_CRIT("%!FUNC!: ERROR couldn't get info on this if id!");
goto end_unlock;
}
//
// Now try to start an operation...
//
{
ENGINEHANDLE ExistingOp = NULL;
err = mfn_StartInterfaceOperationLk(
ehInterfaceId,
NULL, // pvCtxt
GETRESOURCEIDSTRING(IDS_LOG_CONTROL_INTERFACE),
&ExistingOp
);
if (NLBFAILED(err))
{
goto end_unlock;
}
//
// We did start the operation -- so we keep track of this, so that
// we stop the operation on exit.
//
fStopOperationOnExit = TRUE;
}
pISpec = NULL;
mfn_Unlock();
}
mfn_Lock();
err = CNlbEngine::mfn_GetHostFromInterfaceLk(ehInterfaceId, REF pISpec, REF pHSpec);
if (err != NLBERR_OK)
{
TRACE_CRIT(L"%!FUNC! could not get pISpec,pHSpec for ehIF 0x%lx", ehInterfaceId);
goto end_unlock;
}
WMI_CONNECTION_INFO ConnInfo;
ConnInfo.szUserName = (LPCWSTR) pHSpec->m_UserName;
ConnInfo.szPassword = (LPCWSTR) pHSpec->m_Password;
ConnInfo.szMachine = (LPCWSTR) pHSpec->m_ConnectionString;
szNicGuid = (LPCWSTR)(pISpec->m_Guid);
szClusterIp= pISpec->m_NlbCfg.NlbParams.cl_ip_addr;
szHostName = (LPCWSTR)(pHSpec->m_MachineName);
if (szNicGuid == NULL)
{
TRACE_CRIT(L"%!FUNC! ERROR -- NULL szNicGuid!");
goto end_unlock;
}
mfn_Unlock();
if (dwNumOfPortRules == 0)
{
bClusterOperation = TRUE;
dwNumOfIterations = 1;
}
else // Port operation
{
bClusterOperation = FALSE;
dwNumOfIterations = dwNumOfPortRules;
}
m_pCallbacks->HandleEngineEvent(
IUICallbacks::OBJ_INTERFACE,
NULL, // ehClusterId,
ehInterfaceId,
IUICallbacks::EVT_STATUS_CHANGE
);
ProcessMsgQueue();
for (dwIdx = 0 ; dwIdx < dwNumOfIterations ; dwIdx++)
{
LPCWSTR szVip = (szVipArray) ? (LPCTSTR)(szVipArray[dwIdx]) : NULL;
DWORD *pdwPortNum = (pdwPortNumArray) ? &pdwPortNumArray[dwIdx] : NULL;
Status = NlbHostControlCluster(&ConnInfo,
szNicGuid,
szVip,
pdwPortNum,
Operation,
&dwOperationStatus,
&dwClusterOrPortStatus,
&dwHostMap);
if (FAILED(Status))
{
if (Status == WBEM_E_INVALID_PARAMETER)
{
err = NLBERR_INVALID_CLUSTER_SPECIFICATION;
dwOperationStatus = WLBS_BAD_PARAMS;
}
else // Critical Error
{
err = NLBERR_INTERNAL_ERROR;
dwOperationStatus = WLBS_FAILURE;
}
}
// Get the strings to log into log view
mfn_GetLogStrings(Operation,
szVip,
pdwPortNum,
dwOperationStatus,
dwClusterOrPortStatus,
REF LogLevel,
REF OperationStr,
REF OperationStatusStr,
REF ClusterOrPortStatusStr
);
// If operation is NOT Query, Log result in Log View
// We do NOT log results of a Query 'cos this is done for a "Refresh"
// and we do not want to tell what is happening underneath. Instead,
// the change in color coding will reflect any changes to the cluster state.
if (Operation != WLBS_QUERY)
{
m_pCallbacks->Log(LogLevel,
szClusterIp,
szHostName,
IDS_LOG_CONTROL_CLUSTER,
(LPWSTR)OperationStr,
(LPWSTR)OperationStatusStr,
(LPWSTR)ClusterOrPortStatusStr);
ProcessMsgQueue();
}
}
mfn_Lock();
if (bClusterOperation)
{
pISpec->m_fValidClusterState = TRUE;
pISpec->m_dwClusterState = dwClusterOrPortStatus;
}
end_unlock:
if (fStopOperationOnExit)
{
mfn_StopInterfaceOperationLk(ehInterfaceId);
}
mfn_Unlock();
if (fStopOperationOnExit)
{
m_pCallbacks->HandleEngineEvent(
IUICallbacks::OBJ_INTERFACE,
NULL, // ehClusterId,
ehInterfaceId,
IUICallbacks::EVT_STATUS_CHANGE
);
ProcessMsgQueue();
}
TRACE_INFO(L"<- %!FUNC! return : %u", err);
return err;
}
NLBERROR
CNlbEngine::ControlClusterOnCluster(
IN ENGINEHANDLE ehClusterId,
IN WLBS_OPERATION_CODES Operation,
IN CString szVipArray[],
IN DWORD pdwPortNumArray[],
IN DWORD dwNumOfPortRules
)
/*
Perform Cluster wide control operations
*/
{
TRACE_INFO(L"-> %!FUNC!");
NLBERROR nerr = NLBERR_OK;
BOOL fStopOperationOnExit = FALSE;
vector<ENGINEHANDLE> InterfaceListCopy;
//
// First thing we do is to see if we can start the cluster operation...
//
{
BOOL fCanStart = FALSE;
nerr = this->CanStartClusterOperation(ehClusterId, REF fCanStart);
if (NLBFAILED(nerr))
{
goto end;
}
if (!fCanStart)
{
nerr = NLBERR_BUSY;
goto end;
}
}
{
mfn_Lock();
CClusterSpec *pCSpec = NULL;
CEngineCluster *pECluster = m_mapIdToEngineCluster[ehClusterId]; // map
if (pECluster == NULL)
{
nerr = NLBERR_NOT_FOUND;
goto end_unlock;
}
pCSpec = &pECluster->m_cSpec;
//
// Attempt to start the refresh operation -- will fail if there is
// already an operation started on this cluster.
//
{
ENGINEHANDLE ExistingOp= NULL;
CLocalLogger logDescription;
logDescription.Log(
IDS_LOG_CONTROL_CLUSTER_OPERATION_DESCRIPTION,
pCSpec->m_ClusterNlbCfg.NlbParams.cl_ip_addr
);
nerr = mfn_StartClusterOperationLk(
ehClusterId,
NULL, // pvCtxt
logDescription.GetStringSafe(),
&ExistingOp
);
if (NLBFAILED(nerr))
{
goto end_unlock;
}
else
{
fStopOperationOnExit = TRUE;
}
}
InterfaceListCopy = pCSpec->m_ehInterfaceIdList; // vector copy
mfn_Unlock();
}
m_pCallbacks->HandleEngineEvent(
IUICallbacks::OBJ_CLUSTER,
ehClusterId,
ehClusterId,
IUICallbacks::EVT_STATUS_CHANGE
);
ProcessMsgQueue();
mfn_Lock();
{
//
// Perform control operation on each interface in this cluster...
//
for( int i = 0; i < InterfaceListCopy.size(); ++i )
{
ENGINEHANDLE ehIId = InterfaceListCopy[i];
mfn_Unlock();
nerr = this->ControlClusterOnInterface(
ehIId,
Operation,
szVipArray,
pdwPortNumArray,
dwNumOfPortRules,
TRUE
);
mfn_Lock();
}
}
end_unlock:
if (fStopOperationOnExit)
{
//
// We'll stop the operation, assumed to be started in this function.
//
mfn_StopClusterOperationLk(ehClusterId);
}
mfn_Unlock();
if (fStopOperationOnExit)
{
m_pCallbacks->HandleEngineEvent(
IUICallbacks::OBJ_CLUSTER,
ehClusterId,
ehClusterId,
IUICallbacks::EVT_STATUS_CHANGE
);
ProcessMsgQueue();
}
end:
TRACE_INFO(L"<- %!FUNC! return : %u", nerr);
return nerr;
}
NLBERROR
CNlbEngine::FindInterfaceOnHostByClusterIp(
IN ENGINEHANDLE ehHostId,
IN LPCWSTR szClusterIp, // OPTIONAL
OUT ENGINEHANDLE &ehInterfaceId, // first found
OUT UINT &NumFound
)
/*
Return the handle of the interface on the specified host that is
bound to a cluster with the specified cluster IP address.
*/
{
CHostSpec *pHSpec = NULL;
NLBERROR err = NLBERR_OK;
UINT uClusterIp = 0;
WBEMSTATUS wStat;
ENGINEHANDLE ehFoundIID = NULL;
UINT MyNumFound = 0;
ehInterfaceId = NULL;
NumFound = 0;
mfn_Lock();
if (szClusterIp != NULL)
{
wStat = CfgUtilsValidateNetworkAddress(
szClusterIp,
&uClusterIp,
NULL, // puSubnetMask
NULL // puDefaultSubnetMask
);
if (FAILED(wStat))
{
TRACE_CRIT("%!FUNC! invalid szClusterIp (%ws) specified", szClusterIp);
err = NLBERR_INVALID_IP_ADDRESS_SPECIFICATION;
goto end;
}
}
pHSpec = m_mapIdToHostSpec[ehHostId]; // map
if (pHSpec == NULL)
{
err = NLBERR_HOST_NOT_FOUND;
goto end;
}
//
// Now look through the interfaces, searching for a cluster ip.
//
for( int i = 0; i < pHSpec->m_ehInterfaceIdList.size(); ++i )
{
ENGINEHANDLE ehIID = NULL;
ehIID = pHSpec->m_ehInterfaceIdList[i];
CInterfaceSpec *pISpec = NULL;
pISpec = m_mapIdToInterfaceSpec[ehIID]; // map
if (pISpec == NULL)
{
TRACE_CRIT("%!FUNC! unexpected null pISpec for IID 0x%lx", ehIID);
continue;
}
if (szClusterIp == NULL)
{
if (pISpec->m_NlbCfg.IsNlbBound())
{
MyNumFound++;
if (ehFoundIID == NULL)
{
//
// First interface bound to NLB -- we'll save this away.
// and continue.
//
ehFoundIID = ehIID;
}
}
continue;
}
//
// A non-null szClusterIp is specified. We'll see if it matches
// the cluster IP (if any) on this interface.
//
if (pISpec->m_NlbCfg.IsValidNlbConfig())
{
UINT uThisClusterIp = 0;
LPCWSTR szThisClusterIp = pISpec->m_NlbCfg.NlbParams.cl_ip_addr;
wStat = CfgUtilsValidateNetworkAddress(
szThisClusterIp,
&uThisClusterIp,
NULL, // puSubnetMask
NULL // puDefaultSubnetMask
);
if (FAILED(wStat))
{
continue;
}
if (uThisClusterIp == uClusterIp)
{
MyNumFound++;
if (ehFoundIID == NULL)
{
//
// First interface bound to NLB -- we'll save this away.
// and continue.
//
ehFoundIID = ehIID;
}
else
{
//
// Hmm... more than one nlb cluster with the same IP?
// could be a bad config on one or both.
//
TRACE_CRIT("%!FUNC! two clusters on ehHost 0x%lx have cluster ip %ws", ehHostId, szClusterIp);
}
}
}
}
if (MyNumFound != 0)
{
ASSERT(ehFoundIID != NULL);
ehInterfaceId = ehFoundIID;
NumFound = MyNumFound;
err = NLBERR_OK;
}
else
{
err = NLBERR_INTERFACE_NOT_FOUND;
}
end:
mfn_Unlock();
return err;
}
NLBERROR
CNlbEngine::InitializeNewHostConfig(
IN ENGINEHANDLE ehCluster,
OUT NLB_EXTENDED_CLUSTER_CONFIGURATION &NlbCfg
)
/*
Initialize NlbCfg based on the current cluster parameters as well
as good host-specific defaults like host priority, taking into account
other members of the cluster.
*/
{
NLBERROR nerr = NLBERR_INTERNAL_ERROR;
WBEMSTATUS wStatus = WBEM_E_CRITICAL_ERROR;
WLBS_PORT_RULE *pRules = NULL;
BOOL fAvailablePortRulePrioritiesSet = FALSE;
//
// Get the cluster spec and copy over it to NlbCfg.
//
{
mfn_Lock();
CEngineCluster *pECluster = m_mapIdToEngineCluster[ehCluster]; // map
if (pECluster == NULL)
{
nerr = NLBERR_NOT_FOUND;
mfn_Unlock();
goto end;
}
//
// Copy over the cluster spec's params.
//
wStatus = NlbCfg.Update(&pECluster->m_cSpec.m_ClusterNlbCfg);
mfn_Unlock();
}
if (FAILED(wStatus))
{
TRACE_CRIT("%!FUNC! Error copying over cluster params ehC=0x%lx",
ehCluster);
nerr = NLBERR_RESOURCE_ALLOCATION_FAILURE;
goto end;
}
if(!NlbCfg.IsValidNlbConfig()) {
//
// We expect the configuration to be valid -- i.e., NLB bound, with
// valid parameters.
//
TRACE_CRIT("%!FUNC! -- current configuration is unbound/invalid!");
goto end;
}
//
// Set host-id (priority) to the first available host priority.
//
{
ULONG AvailablePriorities = this->GetAvailableHostPriorities(ehCluster);
ULONG HostId = 1;
for(ULONG u=0; u<32; u++)
{
if (AvailablePriorities & (((ULONG)1)<<u))
{
HostId = u+1; // let's pick the first available one.
break;
}
}
NlbCfg.NlbParams.host_priority = HostId;
}
//
// For each single-host-affinity port rule, set the host priority to
// the host-id if that's available, else the first available host priority
//
{
UINT NumRules=0;
ULONG rgAvailablePriorities[WLBS_MAX_RULES];
ZeroMemory(rgAvailablePriorities, sizeof(rgAvailablePriorities));
wStatus = CfgUtilGetPortRules(
&NlbCfg.NlbParams,
&pRules,
&NumRules
);
if (FAILED(wStatus))
{
TRACE_CRIT("%!FUNC! error 0x%08lx extracting port rules!", wStatus);
pRules = NULL;
goto end;
}
nerr = this->GetAvailablePortRulePriorities(
ehCluster,
NumRules,
pRules,
rgAvailablePriorities
);
if (NLBOK(nerr))
{
fAvailablePortRulePrioritiesSet = TRUE;
}
else
{
fAvailablePortRulePrioritiesSet = FALSE;
}
//
// Now for each rule, put defaults
//
for (UINT u=0; u<NumRules;u++)
{
WLBS_PORT_RULE *pRule = pRules+u;
UINT Mode = pRule->mode;
if (Mode != CVY_NEVER)
{
if (Mode == CVY_MULTI)
{
pRule->mode_data.multi.equal_load = TRUE; //default
//
// The equal_load value is set in the cluster
// properties dialog.
//
pRule->mode_data.multi.load = 50;
}
else if (Mode == CVY_SINGLE)
{
ULONG PortPriority = 0;
ULONG AvailablePriorities = 0;
//
// Default is set to this host's host ID (priority)
//
PortPriority = NlbCfg.NlbParams.host_priority;
if (fAvailablePortRulePrioritiesSet)
{
AvailablePriorities = rgAvailablePriorities[u];
}
if (AvailablePriorities != 0
&& 0 == ((1<<(PortPriority-1)) & AvailablePriorities) )
{
//
// There are available priorities, but unfortunately
// the default priority is not available -- pick
// the first available priority.
//
for(ULONG v=0; v<32; v++)
{
if (AvailablePriorities & (((ULONG)1)<<v))
{
PortPriority = v+1; // let's pick this one
break;
}
}
}
pRule->mode_data.single.priority = PortPriority;
}
}
}
//
// Finally, set the port rules..
//
wStatus = CfgUtilSetPortRules(
pRules,
NumRules,
&NlbCfg.NlbParams
);
if (FAILED(wStatus))
{
TRACE_CRIT("%!FUNC! error 0x%08lx setting port rules!", wStatus);
goto end;
}
nerr = NLBERR_OK;
}
end:
delete pRules; // can be NULL
return nerr;
}
NLBERROR
CNlbEngine::UpdateInterface(
IN ENGINEHANDLE ehInterfaceId,
IN NLB_EXTENDED_CLUSTER_CONFIGURATION &refNewConfigIn,
// IN OUT BOOL &fClusterPropertiesUpdated,
OUT CLocalLogger logConflict
)
{
/*
Will MUNGE refNewConfig -- slightly munges refNewConfig.NlbParams,
and will fill in default pIpAddressInfo if it's not set.
*/
NLBERROR err = NLBERR_OK;
BOOL fConnectivityChange = FALSE;
BOOL fStopOperationOnExit= FALSE;
NLB_EXTENDED_CLUSTER_CONFIGURATION
*pPendingConfig = NULL;
LPCWSTR szHostName = NULL;
LPCWSTR szClusterIp = NULL;
LPCWSTR szDisplayName = NULL; // of interface
ENGINEHANDLE ehHost = NULL;
ENGINEHANDLE ehCluster = NULL;
_bstr_t bstrFriendlyName;
_bstr_t bstrDisplayName;
_bstr_t bstrHostName;
//
// Following to decide whether to log that we're not adding the
// dedicated IP because it's already on another interface.
//
BOOL fDedicatedIpOnOtherIf = FALSE;
ENGINEHANDLE ehOtherIf = NULL;
TRACE_INFO(L"-> %!FUNC!");
err = this->GetInterfaceIdentification(
ehInterfaceId,
REF ehHost,
REF ehCluster,
REF bstrFriendlyName,
REF bstrDisplayName,
REF bstrHostName
);
if (NLBFAILED(err))
{
goto end;
}
szDisplayName = bstrDisplayName;
szHostName = bstrHostName;
if (szDisplayName == NULL)
{
szDisplayName = L"";
}
if (szHostName == NULL)
{
szHostName = L"";
}
mfn_Lock();
CInterfaceSpec *pISpec = m_mapIdToInterfaceSpec[ehInterfaceId]; // map
if (pISpec == NULL)
{
err = NLBERR_INTERFACE_NOT_FOUND;
goto end_unlock;
}
//
// Make a copy of refNewConfig, because we'll likely be doing
// the update operation in the background.
//
pPendingConfig = new NLB_EXTENDED_CLUSTER_CONFIGURATION;
if (pPendingConfig == NULL)
{
err = NLBERR_RESOURCE_ALLOCATION_FAILURE;
goto end_unlock;
}
else
{
WBEMSTATUS wStat1;
wStat1 = pPendingConfig->Update(&refNewConfigIn);
if (FAILED(wStat1))
{
delete pPendingConfig;
pPendingConfig = NULL;
err = NLBERR_RESOURCE_ALLOCATION_FAILURE;
goto end_unlock;
}
//
// Copy over the RCT password -- which does NOT get copied over
// by the Update method above. The new password can be in the form
// of a string or hashed-version.
//
//
if (refNewConfigIn.NewRemoteControlPasswordSet())
{
LPCWSTR szNewPassword = NULL;
szNewPassword = refNewConfigIn.GetNewRemoteControlPasswordRaw();
if (szNewPassword != NULL)
{
//
// Note: szNewPassword will be NULL UNLESS the user has explicitly
// specified a new password.
//
pPendingConfig->SetNewRemoteControlPassword(szNewPassword);
}
else
{
//
// This means the hash password is being updated...
// This typically means that this is a new host and we're
// setting up it's remote control password.
//
DWORD dwHash = 0;
BOOL fRet = refNewConfigIn.GetNewHashedRemoteControlPassword(
REF dwHash
);
if (fRet)
{
pPendingConfig->SetNewHashedRemoteControlPassword(
dwHash
);
}
else
{
TRACE_CRIT("refNewConfigIn fNewPassword set; but could not get either szPassword or new hashed password!");
}
}
}
}
NLB_EXTENDED_CLUSTER_CONFIGURATION &refNewConfig = *pPendingConfig;
szClusterIp = refNewConfig.NlbParams.cl_ip_addr;
//
// Attempt to start the update operation -- will fail if there is
// already an operation started on this interface.
//
{
ENGINEHANDLE ExistingOp= NULL;
CLocalLogger logDescription;
logDescription.Log(
IDS_LOG_UPDATE_INTERFACE_OPERATION_DESCRIPTION,
szDisplayName
);
err = mfn_StartInterfaceOperationLk(
ehInterfaceId,
pPendingConfig,
logDescription.GetStringSafe(),
&ExistingOp
);
if (NLBFAILED(err))
{
if (err == NLBERR_BUSY)
{
//
// This means that there was an existing operation.
// Let's get its description and add it to the log.
//
CEngineOperation *pExistingOp;
pExistingOp = mfn_GetOperationLk(ExistingOp);
if (pExistingOp != NULL)
{
LPCWSTR szDescrip = pExistingOp->bstrDescription;
if (szDescrip == NULL)
{
szDescrip = L"";
}
logConflict.Log(
IDS_LOG_OPERATION_PENDING_ON_INTERFACE,
szDescrip
);
}
}
goto end_unlock;
}
else
{
fStopOperationOnExit = TRUE;
}
}
ehHost = pISpec->m_ehHostId;
//
// Validate new config and get out if there's no real updating to
// be done.
//
{
if (refNewConfig.IsNlbBound())
{
if (refNewConfig.NumIpAddresses==0)
{
refNewConfig.fAddClusterIps = TRUE;
refNewConfig.fAddDedicatedIp = TRUE;
}
else
{
//
// refNewConfig has a non-null IP address list.
//
// We interpret this to be the list of cluster IP addresses
// with possibly the dedicated ip address as the
// first IP address.
//
//
// We'll re-order the ip address list to match the
// existing order of IP addresses on the adapter as
// far as possible.
//
// Then we'll add the dedicated IP address list if we
// need to.
//
BOOL fRet = FALSE;
NlbIpAddressList addrList;
//
// Make a copy of the old adddress list in addrList
//
fRet = addrList.Set(pISpec->m_NlbCfg.NumIpAddresses,
pISpec->m_NlbCfg.pIpAddressInfo, 1);
if (!fRet)
{
TRACE_CRIT(L"Unable to copy old IP address list");
err = NLBERR_RESOURCE_ALLOCATION_FAILURE;
goto end_unlock;
}
//
// addrList.Apply will taken on the new ip address list,
// but try to preserve the old order.
//
fRet = addrList.Apply(refNewConfig.NumIpAddresses,
refNewConfig.pIpAddressInfo);
if (!fRet)
{
TRACE_CRIT(L"Unable to apply new IP address list");
err = NLBERR_RESOURCE_ALLOCATION_FAILURE;
goto end_unlock;
}
//
// If there is a dedicated IP address AND it
// does not exist elsewhere on this host, add it to
// the beginning of the list.
//
if (!refNewConfig.IsBlankDedicatedIp())
{
ENGINEHANDLE ehIF = NULL;
err = this->mfn_LookupInterfaceByIpLk(
NULL, // NULL == look through all hosts.
refNewConfig.NlbParams.ded_ip_addr,
REF ehOtherIf
);
if (NLBOK(err) && ehOtherIf != ehInterfaceId)
{
//
// Hmm... another interface already has this
// interface?
//
// We'll log this and NOT add the dedicated IP
// address...
//
fDedicatedIpOnOtherIf = TRUE;
(VOID) addrList.Modify(
refNewConfig.NlbParams.ded_ip_addr,
NULL,
NULL
);
}
else
{
fRet = addrList.Modify(
NULL,
refNewConfig.NlbParams.ded_ip_addr,
refNewConfig.NlbParams.ded_net_mask
);
if (!fRet)
{
TRACE_CRIT(L"Unable to add ded IP to addr list");
err = NLBERR_RESOURCE_ALLOCATION_FAILURE;
goto end_unlock;
}
}
}
//
// Set refNewConfig's ip address list to the newly
// computed values.
//
refNewConfig.SetNetworkAddressesRaw(NULL,0);
addrList.Extract(
REF refNewConfig.NumIpAddresses,
REF refNewConfig.pIpAddressInfo
);
}
}
err = pISpec->m_NlbCfg.AnalyzeUpdate(
&refNewConfig,
&fConnectivityChange
);
if (err == NLBERR_NO_CHANGE)
{
//
// Nothing has changed -- we skip
//
err = NLBERR_OK;
goto end_unlock;
}
//
// err could indicate failure -- we'll deal with that a little
// bit further down.
//
} // validate/munge refNewConfig
if (!NLBOK(err))
{
mfn_Unlock();
//
// Probably a parameter error -- we'll get the latest
// config and display it...
//
m_pCallbacks->Log(
IUICallbacks::LOG_ERROR,
szClusterIp,
szHostName,
IDS_LOG_CANT_UPDATE_BAD_PARAMS
);
(void) this->RefreshInterface(
ehInterfaceId,
FALSE, // FALSE == don't start a new operation
FALSE // FALSE == this is not cluster-wide
);
mfn_Lock();
goto end_unlock;
}
mfn_Unlock();
if (fDedicatedIpOnOtherIf)
{
LPCWSTR szOtherIf = NULL;
_bstr_t bstrOtherFriendlyName;
_bstr_t bstrOtherDisplayName;
_bstr_t bstrOtherHostName;
ENGINEHANDLE ehOtherHost;
ENGINEHANDLE ehOtherCluster;
NLBERROR tmpErr;
IUICallbacks::LogEntryType logType = IUICallbacks::LOG_WARNING;
tmpErr = this->GetInterfaceIdentification(
ehOtherIf,
REF ehOtherHost,
REF ehOtherCluster,
REF bstrOtherFriendlyName,
REF bstrOtherDisplayName,
REF bstrOtherHostName
);
if (NLBOK(tmpErr))
{
if (ehOtherHost != ehHost)
{
// Odd -- ded ip on another host?
logType = IUICallbacks::LOG_ERROR;
szOtherIf = bstrOtherDisplayName; // includes host name
}
else
{
szOtherIf = bstrOtherFriendlyName;
}
}
if (szOtherIf == NULL)
{
szOtherIf = L"";
}
//
// Let's log the fact that the dedicated Ip address is
// on some other interface.
//
TRACE_INFO(L"WARNING: dedicated IP address for eIF 0x%lx is on eIF 0x%lx",
ehInterfaceId, ehOtherIf);
m_pCallbacks->Log(
IUICallbacks::LOG_INFORMATIONAL,
szClusterIp,
szHostName,
IDS_LOG_DEDICATED_IP_ON_OTHER_INTERFACE,
refNewConfig.NlbParams.ded_ip_addr,
szOtherIf
);
}
if (!fConnectivityChange)
{
//
// If there's not going to be a connectivity change, we
// will not try to update the IP address list.
//
refNewConfig.SetNetworkAddresses(NULL, 0);
if (refNewConfig.IsNlbBound())
{
refNewConfig.fAddClusterIps = TRUE;
refNewConfig.fAddDedicatedIp = TRUE;
}
}
//
// Notify the UI that we're going to start the update
//
{
m_pCallbacks->Log(
IUICallbacks::LOG_INFORMATIONAL,
szClusterIp,
szHostName,
IDS_LOG_BEGIN_HOST_UPDATE
);
m_pCallbacks->HandleEngineEvent(
IUICallbacks::OBJ_INTERFACE,
NULL, // ehClusterId,
ehInterfaceId,
IUICallbacks::EVT_STATUS_CHANGE
);
ProcessMsgQueue();
}
#if BUGFIX334243
BOOL fUpdateNow = FALSE;
#else // !BUGFIX334243
BOOL fUpdateNow = TRUE;
#endif // !BUGFIX334243
//
// We are committed to going through with the update now -- either
// sync or async.
//
InterlockedIncrement(&m_WorkItemCount);
fStopOperationOnExit = FALSE;
//
// We MUST call UpdateInterfaceWorItemRoutine now (either sync or async),
// which will stop the operation when done and also decrement
// m_mWorkItemCount.
//
if (!fUpdateNow)
{
BOOL fRet;
//
// We'll attempt to perform the update in the background ...
//
fRet = QueueUserWorkItem(
UpdateInterfaceWorkItemRoutine,
(PVOID) (UINT_PTR) ehInterfaceId,
WT_EXECUTELONGFUNCTION
);
if (fRet)
{
fUpdateNow = FALSE;
}
else
{
fUpdateNow = TRUE; // Update now if QueueUsesrWorkItem fails.
}
}
if (fUpdateNow)
{
//
// Call the work item synchronously
//
UpdateInterfaceWorkItemRoutine((LPVOID) (UINT_PTR) ehInterfaceId);
}
goto end;
end_unlock:
if (fStopOperationOnExit)
{
//
// We'll stop the operation, assumed to be started in this function.
//
mfn_StopInterfaceOperationLk(ehInterfaceId);
fStopOperationOnExit = FALSE;
}
mfn_Unlock();
end:
ASSERT(!fStopOperationOnExit);
TRACE_INFO(L"<- %!FUNC!");
return err;
}
NLBERROR
CNlbEngine::UpdateCluster(
IN ENGINEHANDLE ehClusterId,
IN const NLB_EXTENDED_CLUSTER_CONFIGURATION *pNewConfig OPTIONAL,
IN OUT CLocalLogger &logConflict
)
/*
Attempt to push all the cluster-wide (i.e. non-host-specific)
information on *pNewConfig to each host in the cluster.
Update the cluster's copy of NlbConfig to be *pNewConfig.
Set cluster's pending state on starting and set it
appropriately when done (could be misconfigured).
*/
{
NLBERROR nerr = NLBERR_OK;
_bstr_t bstrClusterIp;
TRACE_INFO(L"-> %!FUNC!");
vector<ENGINEHANDLE> InterfaceListCopy;
BOOL fStopOperationOnExit = FALSE;
//
// First thing we do is to see if we can start the cluster operation...
//
if (pNewConfig != NULL)
{
BOOL fCanStart = FALSE;
nerr = this->CanStartClusterOperation(ehClusterId, REF fCanStart);
if (NLBFAILED(nerr))
{
goto end;
}
if (!fCanStart)
{
nerr = NLBERR_BUSY;
goto end;
}
}
{
mfn_Lock();
CClusterSpec *pCSpec = NULL;
CEngineCluster *pECluster = m_mapIdToEngineCluster[ehClusterId]; // map
if (pECluster == NULL)
{
nerr = NLBERR_NOT_FOUND;
goto end_unlock;
}
pCSpec = &pECluster->m_cSpec;
//
// Attempt to start the update operation -- will fail if there is
// already an operation started on this cluster.
//
{
ENGINEHANDLE ExistingOp= NULL;
CLocalLogger logDescription;
logDescription.Log(
IDS_LOG_UPDATE_CLUSTER_OPERATION_DESCRIPTION,
pCSpec->m_ClusterNlbCfg.NlbParams.cl_ip_addr
);
nerr = mfn_StartClusterOperationLk(
ehClusterId,
NULL, // pvCtxt
logDescription.GetStringSafe(),
&ExistingOp
);
if (NLBFAILED(nerr))
{
if (nerr == NLBERR_BUSY)
{
//
// This means that there was an existing operation.
// Let's get its description and add it to the log.
//
CEngineOperation *pExistingOp;
pExistingOp = mfn_GetOperationLk(ExistingOp);
if (pExistingOp != NULL)
{
LPCWSTR szDescrip = pExistingOp->bstrDescription;
if (szDescrip == NULL)
{
szDescrip = L"";
}
logConflict.Log(
IDS_LOG_OPERATION_PENDING_ON_CLUSTER,
szDescrip
);
}
}
goto end_unlock;
}
else
{
fStopOperationOnExit = TRUE;
}
}
if (pNewConfig != NULL)
{
pCSpec->m_ClusterNlbCfg.Update(pNewConfig); // TODO: error ret
//
// Note: Update above has the side effect of setting
// m_ClusterNlbCfg's szNewPassword field to NULL -- this is what
// we want. However, we do mark the fact that the
// password is new -- because the cluster's version of the
// hashed-password is now obsolete -- it needs to be updated
// by reading one of the hosts' versions.
// pCSpec->m_fNewRctPassword track this state.
//
if (pNewConfig->GetNewRemoteControlPasswordRaw() != NULL)
{
pCSpec->m_fNewRctPassword = TRUE;
//
// The above flag will be cleared (and the hashed password
// value updated) at the end of the
// first update operation for one of the interfaces.
//
// It is also cleared when the cluster properties are
// updated as part of a refresh operation.
//
}
}
bstrClusterIp = _bstr_t(pCSpec->m_ClusterNlbCfg.NlbParams.cl_ip_addr);
InterfaceListCopy = pCSpec->m_ehInterfaceIdList; // vector copy
mfn_Unlock();
}
m_pCallbacks->HandleEngineEvent(
IUICallbacks::OBJ_CLUSTER,
ehClusterId,
ehClusterId,
IUICallbacks::EVT_STATUS_CHANGE
);
ProcessMsgQueue();
mfn_Lock();
BOOL fRetryUpdateCluster = FALSE;
do
{
LPCWSTR szClusterIp = bstrClusterIp;
UINT uNumModeChanges = 0; // number of IF's undergoing mode changes.
UINT uNumUpdateErrors = 0; // number of IF's with update errors.
//
// fClusterPropertiesUpdated keeps track of whether the cluster
// properties were updated as a course of refreshing and or updating
// the interface -- we update the cluster props at most once:
// for the first interface that successfully performs the update
// (or if pNewConfig == NULL) for the first interface that
// successfully refreshes its properties AND is still bound.
//
//
BOOL fClusterPropertiesUpdated = FALSE;
//
// Update each interface in this cluster...
//
for( int i = 0; i < InterfaceListCopy.size(); ++i )
{
CInterfaceSpec TmpISpec;
_bstr_t bstrHostName = L"";
ENGINEHANDLE ehIId = InterfaceListCopy[i];
mfn_GetInterfaceHostNameLk(
ehIId,
REF bstrHostName
);
mfn_Unlock();
//
// Get the latest interface information and (if
// pNewConfig != NULL) merge in the cluster information.
//
{
BOOL fSkipHost = TRUE;
if (pNewConfig == NULL)
{
//
// This is a REFRESH CLUSTER operation
//
nerr = this->RefreshInterface(ehIId, TRUE, TRUE);
if (NLBOK(nerr))
{
//
// Let's update the cluster-wide properties with
// this interface if:
//
// 1. We haven't already updated the props in this
// loop.
//
// 2. The above interface is bound to NLB,
// and the IP address matches
// the clusters' ip address.
//
if (!fClusterPropertiesUpdated)
{
fClusterPropertiesUpdated = mfn_UpdateClusterProps(
ehClusterId,
ehIId
);
}
}
}
else
{
//
// This is a CHANGE CLUSTER CONFIGURATION operation
// Let's first get the latest version of this interface'
// properties.
//
nerr = this->mfn_RefreshInterface(ehIId);
}
//
// If this fails, we don't try to update. Instead
// we send a log message and continue.
// Note: RefreshInterface will send an interface-status
// change machine.
//
if (nerr == NLBERR_OK)
{
nerr = this->GetInterfaceSpec(ehIId, REF TmpISpec);
if (nerr == NLBERR_OK)
{
fSkipHost = FALSE;
}
}
if (!fSkipHost && pNewConfig != NULL)
{
if (pNewConfig->fBound)
{
NLB_EXTENDED_CLUSTER_CONFIGURATION *pOldConfig
= &TmpISpec.m_NlbCfg;
//
// Check if there is a mode change involved...
// because if so, the provider will not add
// any cluster IP addresses.
//
// We keep track of all IFs with mode changes, and
// if any, we'll do this whole cluster-wide update
// process a 2nd time, at which time the IP addresses
// will be added.
//
if (pOldConfig->IsValidNlbConfig())
{
NLB_EXTENDED_CLUSTER_CONFIGURATION::TRAFFIC_MODE tmOld, tmNew;
tmOld = pOldConfig->GetTrafficMode();
tmNew = pNewConfig->GetTrafficMode();
if (tmOld != tmNew)
{
// Mode change!
uNumModeChanges++;
TRACE_INFO(L"%!FUNC!: ehIF 0x%lx: Detected mode change!\n", ehIId);
}
}
//
// Merge in the cluster-specific information
//
nerr = ApplyClusterWideConfiguration(
REF *pNewConfig,
REF TmpISpec.m_NlbCfg
);
if (nerr != NLBERR_OK)
{
fSkipHost = TRUE;
}
}
else
{
//
// We're asked to unbind all hosts!
//
TmpISpec.m_NlbCfg.fAddDedicatedIp = FALSE;
TmpISpec.m_NlbCfg.SetNetworkAddresses(NULL, 0);
TmpISpec.m_NlbCfg.SetNlbBound(FALSE);
if (!TmpISpec.m_NlbCfg.IsBlankDedicatedIp())
{
WCHAR rgBuf[64];
LPCWSTR szAddr = rgBuf;
StringCbPrintf(
rgBuf,
sizeof(rgBuf),
L"%ws/%ws",
TmpISpec.m_NlbCfg.NlbParams.ded_ip_addr,
TmpISpec.m_NlbCfg.NlbParams.ded_net_mask
);
TmpISpec.m_NlbCfg.SetNetworkAddresses(&szAddr, 1);
}
}
}
if (fSkipHost && pNewConfig != NULL)
{
TRACE_CRIT(L"%!FUNC!: Couldn't get latest interface spec when trying to update cluster");
m_pCallbacks->Log(
IUICallbacks::LOG_ERROR,
szClusterIp,
(LPCWSTR) bstrHostName,
IDS_LOG_SKIP_INTERFACE_UPDATE_ON_ERROR, // "...%lx"
nerr // TODO -- replace by textual description.
);
mfn_Lock();
uNumUpdateErrors++;
continue;
}
}
//
// Actually update the interface -- likely it will complete
// in the background.
//
if (pNewConfig != NULL)
{
CLocalLogger logConflict;
nerr = this->UpdateInterface(
ehIId,
REF TmpISpec.m_NlbCfg,
// REF fClusterPropertiesUpdated,
REF logConflict
);
}
mfn_Lock();
}
//
// If there are mode changes for one-or-more nodes, we'll need
// to wait for ALL updates to complete, and then try again.
//
if (uNumUpdateErrors!=0)
{
nerr = NLBERR_OPERATION_FAILED;
}
else
{
if (fRetryUpdateCluster && uNumModeChanges!=0)
{
//
// We're gone through a 2nd time and STILL there are
// mode changes! We bail.
//
TRACE_CRIT(L"%!FUNC! ehC 0x%lx: %lu Mode changes on 2nd phase. Bailing", ehClusterId, uNumModeChanges);
nerr = NLBERR_OPERATION_FAILED;
}
else
{
nerr = NLBERR_OK;
}
}
fRetryUpdateCluster = FALSE;
if (NLBOK(nerr) && uNumModeChanges!=0)
{
BOOL fSameMode = FALSE;
//
// There were one or more mode changes!
// Let's wait for *all* updates to complete successfully.
// Then we'll check to make sure that the modes on all
// hosts match the cluster IP address' modes. If they do
// (and there were no update errors), we'll re-run the
// update process, this time hopefully adding
// the IP addresses that would have been stripped had there been
// mode changes.
//
mfn_Unlock();
nerr = mfn_WaitForInterfaceOperationCompletions(ehClusterId);
mfn_Lock();
if (NLBOK(nerr))
{
nerr = mfn_VerifySameModeLk(ehClusterId, REF fSameMode);
}
if (NLBOK(nerr) && fSameMode)
{
TRACE_CRIT(L"%!FUNC! chC 0x%lx: SECOND PHASE on CHANGE MODE",
ehClusterId);
fRetryUpdateCluster = TRUE;
}
}
if (uNumModeChanges && NLBFAILED(nerr))
{
mfn_Unlock();
//
// There was a problem, log it, as well as the list of
// cluster IP addresses/subnets.
// TODO: add details.
//
m_pCallbacks->Log(
IUICallbacks::LOG_INFORMATIONAL,
szClusterIp,
NULL,
IDS_LOG_ERRORS_DETECTED_DURING_MODE_CHANGE
);
mfn_Lock();
}
} while (NLBOK(nerr) && fRetryUpdateCluster);
//
// We're done -- set the cluster's fPending field to false, and if
// necessary (if no interfaces) delete the cluster.
//
{
BOOL fEmptyCluster = FALSE;
CClusterSpec *pCSpec = NULL;
CEngineCluster *pECluster = m_mapIdToEngineCluster[ehClusterId]; // map
if (pECluster == NULL)
{
nerr = NLBERR_NOT_FOUND;
goto end_unlock;
}
pCSpec = &pECluster->m_cSpec;
fEmptyCluster = (pCSpec->m_ehInterfaceIdList.size()==0);
ASSERT(fStopOperationOnExit);
mfn_StopClusterOperationLk(ehClusterId);
fStopOperationOnExit = FALSE;
mfn_Unlock();
if (fEmptyCluster)
{
//
// The cluster is empty -- delete it.
//
this->DeleteCluster(ehClusterId, FALSE); // FALSE == don't remove IF
}
else
{
m_pCallbacks->HandleEngineEvent(
IUICallbacks::OBJ_CLUSTER,
ehClusterId,
ehClusterId,
IUICallbacks::EVT_STATUS_CHANGE
);
ProcessMsgQueue();
}
mfn_Lock();
}
// fall through ...
end_unlock:
if (fStopOperationOnExit)
{
//
// We'll stop the operation, assumed to be started in this function.
//
mfn_StopClusterOperationLk(ehClusterId);
}
mfn_Unlock();
if (fStopOperationOnExit)
{
m_pCallbacks->HandleEngineEvent(
IUICallbacks::OBJ_CLUSTER,
ehClusterId,
ehClusterId,
IUICallbacks::EVT_STATUS_CHANGE
);
ProcessMsgQueue();
fStopOperationOnExit = FALSE;
}
end:
ASSERT(!fStopOperationOnExit);
TRACE_INFO(L"<- %!FUNC!");
return nerr;
}
NLBERROR
CNlbEngine::GetClusterSpec(
IN ENGINEHANDLE ehClusterId,
OUT CClusterSpec& refClusterSpec
)
{
// TRACE_INFO(L"-> %!FUNC!");
NLBERROR err = NLBERR_OK;
mfn_Lock();
CEngineCluster *pECluster = m_mapIdToEngineCluster[ehClusterId]; // map
if (pECluster == NULL)
{
err = NLBERR_INTERFACE_NOT_FOUND;
}
else
{
//
// This is really an assert condition -- the cluster spec should
// NEVER have a non-blank dedicated IP address.
//
if (!pECluster->m_cSpec.m_ClusterNlbCfg.IsBlankDedicatedIp())
{
err = NLBERR_INTERNAL_ERROR;
TRACE_CRIT(L"%!FUNC! unexpected: cluster eh 0x%lx has non-blank ded-ip!", ehClusterId);
}
else
{
refClusterSpec.Copy(pECluster->m_cSpec);
}
}
mfn_Unlock();
// TRACE_INFO(L"<- %!FUNC!");
return err;
}
NLBERROR
CNlbEngine::GetHostSpec(
IN ENGINEHANDLE ehHostId,
OUT CHostSpec& refHostSpec
)
{
NLBERROR err = NLBERR_OK;
// TRACE_INFO(L"-> %!FUNC!(0x%lx)", (UINT)ehHostId);
mfn_Lock();
CHostSpec *pHSpec = m_mapIdToHostSpec[ehHostId]; // map
if (pHSpec == NULL)
{
err = NLBERR_INTERFACE_NOT_FOUND;
}
else
{
refHostSpec.Copy(*pHSpec);
}
if (err != NLBERR_OK)
{
TRACE_INFO(
L"<- %!FUNC!(0x%lx) Host not found",
ehHostId
);
}
mfn_Unlock();
return err;
}
NLBERROR
CNlbEngine::GetHostConnectionInformation(
IN ENGINEHANDLE ehHost,
OUT ENGINEHANDLE &ehConnectionIF,
OUT _bstr_t &bstrConnectionString,
OUT UINT &uConnectionIp
)
/*
For the specified host ehHost,
Look up it's connection IP, and search all its interfaces
for the specific connection IP.
Return the found interface handle, connection string and connection IP.
*/
{
NLBERROR nerr = NLBERR_INTERNAL_ERROR;
CHostSpec *pHSpec = NULL;
mfn_Lock();
pHSpec = m_mapIdToHostSpec[ehHost]; // map
if (pHSpec == NULL)
{
nerr = NLBERR_NOT_FOUND;
goto end_unlock;
}
//
// Lookup the interface that has the connection IP
//
uConnectionIp = pHSpec->m_ConnectionIpAddress;
if (uConnectionIp != 0)
{
WCHAR rgIp[128];
LPBYTE pb = (LPBYTE) &uConnectionIp;
StringCbPrintf(
rgIp,
sizeof(rgIp),
L"%lu.%lu.%lu.%lu",
pb[0], pb[1], pb[2], pb[3]
);
nerr = mfn_LookupInterfaceByIpLk(ehHost, rgIp, REF ehConnectionIF);
}
if (NLBOK(nerr))
{
bstrConnectionString = pHSpec->m_ConnectionString;
}
else
{
ehConnectionIF = NULL;
uConnectionIp = 0;
}
end_unlock:
mfn_Unlock();
return nerr;
}
NLBERROR
CNlbEngine::EnumerateClusters(
OUT vector <ENGINEHANDLE> & ClusterIdList
)
{
TRACE_INFO(L"-> %!FUNC!");
mfn_Lock();
map< ENGINEHANDLE, CEngineCluster* >::iterator iCluster;
ClusterIdList.clear();
for (iCluster = m_mapIdToEngineCluster.begin();
iCluster != m_mapIdToEngineCluster.end();
iCluster++)
{
ENGINEHANDLE ehClusterId = (*iCluster).first;
if (m_mapIdToEngineCluster[ehClusterId])
ClusterIdList.push_back(ehClusterId);
}
mfn_Unlock();
TRACE_INFO(L"<- %!FUNC!");
return NLBERR_OK;
}
NLBERROR
CNlbEngine::EnumerateHosts(
OUT vector <ENGINEHANDLE> & HostIdList
)
{
TRACE_INFO(L"-> %!FUNC!");
// TODO
TRACE_INFO(L"<- %!FUNC!");
return NLBERR_OK;
}
NLBERROR
CNlbEngine::GetAllHostConnectionStrings(
OUT vector <_bstr_t> & ConnectionStringList
)
//
// Actually only returns the strings for hosts that have at least one
// interface that is part of a cluster that is displayed by NLB Manager.
// (see .net server bug 499068)
//
{
TRACE_INFO(L"-> %!FUNC!");
mfn_Lock();
map< ENGINEHANDLE, CHostSpec* >::iterator iter;
for( iter = m_mapIdToHostSpec.begin();
iter != m_mapIdToHostSpec.end();
++iter)
{
CHostSpec *pHSpec = (CHostSpec *) ((*iter).second);
if (pHSpec != NULL)
{
if (mfn_HostHasManagedClustersLk(pHSpec))
{
ConnectionStringList.push_back(pHSpec->m_ConnectionString);
}
}
}
mfn_Unlock();
TRACE_INFO(L"<- %!FUNC!");
return NLBERR_OK;
}
BOOL
CNlbEngine::GetObjectType(
IN ENGINEHANDLE ehObj,
OUT IUICallbacks::ObjectType &objType
)
{
BOOL fRet = FALSE;
UINT uType;
objType = IUICallbacks::OBJ_INVALID;
if (ehObj == NULL)
{
goto end;
}
//
// Extract object type -- the first TYPE_BIT_COUNT bits of ehObj.
//
uType = ((UINT) ehObj) & (0xffffffff>>(32-TYPE_BIT_COUNT));
mfn_Lock();
switch(uType)
{
case IUICallbacks::OBJ_INTERFACE:
if (m_mapIdToInterfaceSpec[ehObj] != NULL)
{
objType = IUICallbacks::OBJ_INTERFACE;
fRet = TRUE;
}
break;
case IUICallbacks::OBJ_CLUSTER:
if (m_mapIdToEngineCluster[ehObj] != NULL)
{
objType = IUICallbacks::OBJ_CLUSTER;
fRet = TRUE;
}
break;
case IUICallbacks::OBJ_HOST:
if (m_mapIdToHostSpec[ehObj] != NULL)
{
objType = IUICallbacks::OBJ_HOST;
fRet = TRUE;
}
break;
case IUICallbacks::OBJ_OPERATION:
if (m_mapIdToOperation[ehObj] != NULL)
{
objType = IUICallbacks::OBJ_OPERATION;
fRet = TRUE;
}
break;
default:
break;
}
mfn_Unlock();
end:
return fRet;
}
//
// Return a bitmap of available host IDs for the specified cluster.
//
ULONG
CNlbEngine::GetAvailableHostPriorities(
ENGINEHANDLE ehCluster // OPTIONAL
)
{
ULONG UsedPriorities = 0;
mfn_Lock();
do // while false
{
if (ehCluster == NULL) break;
CEngineCluster *pECluster = m_mapIdToEngineCluster[ehCluster]; // map
if (pECluster == NULL) break;
//
// For each interface,
// build up a bitmap of used priorities. Return the inverse of that
// bitmap.
//
for( int i = 0; i < pECluster->m_cSpec.m_ehInterfaceIdList.size(); ++i )
{
ENGINEHANDLE ehIId = pECluster->m_cSpec.m_ehInterfaceIdList[i];
CInterfaceSpec *pISpec = m_mapIdToInterfaceSpec[ehIId]; // map
if (pISpec == NULL)
{
TRACE_CRIT("%!FUNC! no interface in ehC 0x%x for ehI 0x%x",
ehCluster, ehIId);
continue;
}
if (pISpec->m_NlbCfg.IsValidNlbConfig())
{
UINT HostPriority = pISpec->m_NlbCfg.NlbParams.host_priority;
UsedPriorities |= (1<<(HostPriority-1));
}
}
} while (FALSE);
mfn_Unlock();
return ~UsedPriorities;
}
//
// Fill in an array of bitmaps of available priorities for each specified
// port rule. The port rule must be valid.
// If a port rule is not single-host-priority, the bitmap for that
// port rule is undefined.
//
NLBERROR
CNlbEngine::GetAvailablePortRulePriorities(
IN ENGINEHANDLE ehCluster, OPTIONAL
IN UINT NumRules,
IN WLBS_PORT_RULE rgRules[],
IN OUT ULONG rgAvailablePriorities[] // At least NumRules
)
{
//
// If ehCluster==NULL, set all to 0xffffffff
//
// For each interface, locate the specified port rules (based on vip and
// start port) and build up a bitmap of used priorities.
// fill in rgRules with the inverse of the bitmaps.
//
// Todo Account for priorities of pending operations.
//
mfn_Lock();
//
// We initially use rgAvailablePriorities to store USED priorities.
// Intialize to 0.
//
for (UINT u=0; u<NumRules; u++)
{
rgAvailablePriorities[u] = 0;
}
do // while false
{
ULONG *rgUsedPriorities = rgAvailablePriorities;
if (ehCluster == NULL) break;
CEngineCluster *pECluster = m_mapIdToEngineCluster[ehCluster]; // map
if (pECluster == NULL) break;
//
// For each interface, locate the specified port rule and
// build up a bitmap of used priorities. Return the inverse of that
// bitmap.
//
for( int i = 0; i < pECluster->m_cSpec.m_ehInterfaceIdList.size(); ++i )
{
ENGINEHANDLE ehIId = pECluster->m_cSpec.m_ehInterfaceIdList[i];
CInterfaceSpec *pISpec = m_mapIdToInterfaceSpec[ehIId]; // map
if (pISpec == NULL)
{
TRACE_CRIT("%!FUNC! no interface in ehC 0x%x for ehI 0x%x",
ehCluster, ehIId);
continue;
}
if (pISpec->m_NlbCfg.IsValidNlbConfig())
{
//
// get_used_port_rule_priorities will add its priority
// to the bitmap of each single-host port rule.
//
(void) get_used_port_rule_priorities(
REF pISpec->m_NlbCfg,
NumRules,
rgRules,
rgUsedPriorities
);
}
}
} while (FALSE);
//
// We initially used rgAvailablePriorities to store USED priorities.
// So invert each.
//
for (UINT u=0; u<NumRules; u++)
{
rgAvailablePriorities[u] = ~rgAvailablePriorities[u];
}
mfn_Unlock();
return NLBERR_OK;
}
NLBERROR
CNlbEngine::mfn_GetHostFromInterfaceLk(
IN ENGINEHANDLE ehIId,
OUT CInterfaceSpec* &pISpec,
OUT CHostSpec* &pHSpec
)
{
NLBERROR nerr = NLBERR_INTERNAL_ERROR;
pHSpec = NULL;
pISpec = m_mapIdToInterfaceSpec[ehIId]; // map
if (pISpec == NULL)
{
nerr = NLBERR_NOT_FOUND;
}
else
{
ENGINEHANDLE ehHost = pISpec->m_ehHostId;
pHSpec = m_mapIdToHostSpec[ehHost]; // map
nerr = NLBERR_OK;
}
return nerr;
}
void
CNlbEngine::mfn_GetInterfaceHostNameLk(
ENGINEHANDLE ehIId,
_bstr_t &bstrHostName
)
/*
This function returns the host name of the specified interface.
It sets bstrHostName to "" (not NULL) on error.
*/
{
NLBERROR nerr;
_bstr_t *pName = NULL;
CHostSpec *pHSpec = NULL;
CInterfaceSpec *pISpec = NULL;
nerr = CNlbEngine::mfn_GetHostFromInterfaceLk(ehIId,REF pISpec, REF pHSpec);
if (nerr == NLBERR_OK)
{
pName = &pHSpec->m_MachineName;
}
if (pName == NULL)
{
bstrHostName = _bstr_t(L"");
}
else
{
bstrHostName = *pName;
}
}
ENGINEHANDLE
CNlbEngine::mfn_NewHandleLk(IUICallbacks::ObjectType type)
//
// Returns a unique number from 1 to 2^31-1 (1 to ~ 2 billion).
// If it is called more than 2 billion times it will start returning zero
// from then onwards.
//
{
ULONG uVal =0;
if (m_fPrepareToDeinitialize)
{
goto end;
}
if ((UINT)type >= (1<<TYPE_BIT_COUNT))
{
TRACE_CRIT(L"%!FUNC!: Invalid obj type");
goto end;
}
if (!m_fHandleOverflow)
{
uVal = (ULONG) InterlockedIncrement(&m_NewHandleValue);
//
// uVal could be less than 2^(32-TypeBitCount)) to save
// so that it doesn't over flow when we shift it by TypeBitCounts.
//
// Extreme cases: if TypeBitCount==32, uVal should be less
// then 1<<0, or 1 -- so it can only have one value 0.
//
// If TypeBitCount==0, uVal should be less than 1<<32, i.e., any
// valid uint value.
//
if (uVal >= (1<<(32-TYPE_BIT_COUNT)))
{
//
// Overflow!!!
//
TRACE_CRIT(L"%!FUNC!: Handle overflow!");
m_fHandleOverflow = TRUE;
uVal = 0;
goto end;
}
//
// Construct the handle by compositing the type and the counter value.
//
uVal = ((uVal<<TYPE_BIT_COUNT) | (UINT) type);
}
end:
return (ENGINEHANDLE) uVal;
}
NLBERROR
CNlbEngine::ApplyClusterWideConfiguration(
IN const NLB_EXTENDED_CLUSTER_CONFIGURATION &ClusterConfig,
IN OUT NLB_EXTENDED_CLUSTER_CONFIGURATION &ConfigToUpdate
)
/*
Apply only the cluster-wide parameters in ClusterConfig to
ConfigToUpdate.
When updateing port rules, try to preserve the host-specific info
(load weight, host priority).
NOTE: It WILL replace the ConfigToUpdate's list of IP addresses with
the list of IP addresses in ClusterConfig. This will REMOVE the
dedicated IP address from the list of IP addresses. The dedicated IP
address will be added before the interface is actually updated -- in
CNlbEngine::UpdateInterface.
WLBS_REG_PARAMS cluster-wide params...
BOOL fRctEnabled
BOOL fMcastSupport
BOOL fIGMPSupport
TCHAR cl_ip_addr[CVY_MAX_CL_IP_ADDR + 1]
TCHAR cl_net_mask[CVY_MAX_CL_NET_MASK + 1]
TCHAR domain_name[CVY_MAX_DOMAIN_NAME + 1]
bool fChangePassword
TCHAR szPassword[CVY_MAX_RCT_CODE + 1]
DWORD dwNumRules
NETCFG_WLBS_PORT_RULE port_rules[CVY_MAX_RULES]
*/
{
NLBERROR nerr = NLBERR_INTERNAL_ERROR;
WLBS_PORT_RULE *pOldRules = NULL;
WLBS_PORT_RULE *pNewRules = NULL;
if (!ClusterConfig.fBound || !validate_extcfg(ClusterConfig))
{
TRACE_CRIT("%!FUNC! -- cluster configuration is invalid!");
goto end;
}
if(!ConfigToUpdate.IsValidNlbConfig())
{
//
// We expect the configuration to be valid -- i.e., NLB bound, with
// valid parameters.
//
TRACE_CRIT("%!FUNC! -- current configuration is unbound/invalid!");
goto end;
}
ConfigToUpdate.NlbParams.rct_enabled = ClusterConfig.NlbParams.rct_enabled;
ConfigToUpdate.NlbParams.mcast_support = ClusterConfig.NlbParams.mcast_support;
ConfigToUpdate.NlbParams.fIGMPSupport = ClusterConfig.NlbParams.fIGMPSupport;
CopyMemory(
ConfigToUpdate.NlbParams.cl_ip_addr,
ClusterConfig.NlbParams.cl_ip_addr,
sizeof(ConfigToUpdate.NlbParams.cl_ip_addr)
);
CopyMemory(
ConfigToUpdate.NlbParams.cl_net_mask,
ClusterConfig.NlbParams.cl_net_mask,
sizeof(ConfigToUpdate.NlbParams.cl_net_mask)
);
CopyMemory(
ConfigToUpdate.NlbParams.domain_name,
ClusterConfig.NlbParams.domain_name,
sizeof(ConfigToUpdate.NlbParams.domain_name)
);
//
// TODO -- we need to preserve Ip addresses and their order in
// hosts, as far as possible. For now we decide the order.
//
{
BOOL fRet = FALSE;
NlbIpAddressList addrList;
fRet = addrList.Set(ClusterConfig.NumIpAddresses,
ClusterConfig.pIpAddressInfo, 0);
if (!fRet)
{
TRACE_CRIT(L"Could not copy over ip addresses!");
goto end;
}
ConfigToUpdate.SetNetworkAddressesRaw(NULL,0);
addrList.Extract(
REF ConfigToUpdate.NumIpAddresses,
REF ConfigToUpdate.pIpAddressInfo
);
}
// password -- copy and set some flag only if changed.
{
LPCWSTR szNewPassword = NULL;
szNewPassword = ClusterConfig.GetNewRemoteControlPasswordRaw();
//
// Note: szNewPassword will be NULL UNLESS the user has explicitly
// specified a new password.
//
ConfigToUpdate.SetNewRemoteControlPassword(szNewPassword);
}
//
// Fold in port rules.
//
{
UINT NumOldRules=0;
UINT NumNewRules=0;
WBEMSTATUS wStat;
wStat = CfgUtilGetPortRules(
&ConfigToUpdate.NlbParams,
&pOldRules,
&NumOldRules
);
if (FAILED(wStat))
{
TRACE_CRIT("%!FUNC! error 0x%08lx extracting old port rules!", wStat);
pOldRules=NULL;
goto end;
}
wStat = CfgUtilGetPortRules(
&ClusterConfig.NlbParams,
&pNewRules,
&NumNewRules
);
if (FAILED(wStat))
{
TRACE_CRIT("%!FUNC! error 0x%08lx extracting new port rules!", wStat);
pNewRules = NULL;
goto end;
}
//
// Now for each new port rule, if it makes sense, pick up
// host-specific info from old port rules.
//
for (UINT u=0; u<NumNewRules;u++)
{
WLBS_PORT_RULE *pNewRule = pNewRules+u;
const WLBS_PORT_RULE *pOldRule = NULL; // mapStartPortToOldRule[pNewRule->start_port];
UINT NewMode = pNewRule->mode;
pOldRule = find_port_rule(
pOldRules,
NumOldRules,
pNewRule->virtual_ip_addr,
pNewRule->start_port
);
if (NewMode != CVY_NEVER)
{
//
// We need to fill in host-specific stuff
//
if (pOldRule!=NULL && pOldRule->mode == NewMode)
{
//
// We can pick up the old rule's info.
//
if (NewMode == CVY_MULTI)
{
//
// We ignore the cluster's equal_load and
// load fields.
//
pNewRule->mode_data.multi.equal_load =
pOldRule->mode_data.multi.equal_load;
pNewRule->mode_data.multi.load =
pOldRule->mode_data.multi.load;
}
else if (NewMode == CVY_SINGLE)
{
//
// TODO: priorities can potentially clash here.
//
pNewRule->mode_data.single.priority =
pOldRule->mode_data.single.priority;
}
}
else
{
//
// We need to pick defaults.
//
if (NewMode == CVY_MULTI)
{
pNewRule->mode_data.multi.equal_load = TRUE; //default
pNewRule->mode_data.multi.load = 50;
}
else if (NewMode == CVY_SINGLE)
{
//
// TODO: need to pick a new priority here!
// for now we pick the host's priority -- but
// we need to really pick one that
// doesn't clash!
//
pNewRule->mode_data.single.priority =
ConfigToUpdate.NlbParams.host_priority;
}
}
}
}
//
// Finally, set the new port rules..
//
wStat = CfgUtilSetPortRules(
pNewRules,
NumNewRules,
&ConfigToUpdate.NlbParams
);
if (FAILED(wStat))
{
TRACE_CRIT("%!FUNC! error 0x%08lx setting new port rules!", wStat);
goto end;
}
nerr = NLBERR_OK;
}
end:
delete pOldRules; // can be NULL
delete pNewRules; // can be NULL
return nerr;
}
NLBERROR
CNlbEngine::mfn_RefreshHost(
IN PWMI_CONNECTION_INFO pConnInfo,
IN ENGINEHANDLE ehHost,
IN BOOL fOverwriteConnectionInfo
)
{
NLBERROR nerr = NLBERR_INTERNAL_ERROR;
LPWSTR *pszNics = NULL;
UINT NumNics = 0;
UINT NumNlbBound = 0;
WBEMSTATUS wStatus = WBEM_E_CRITICAL_ERROR;
CHostSpec *pHost = NULL;
vector<ENGINEHANDLE> InterfaceListCopy;
wStatus = NlbHostGetCompatibleNics(
pConnInfo,
&pszNics,
&NumNics,
&NumNlbBound
);
if (FAILED(wStatus))
{
pszNics = NULL;
// TODO -- check for authentication failure -- ask for new creds.
}
//
// Update the connection string, IP, and status of the host.
//
{
mfn_Lock();
pHost = m_mapIdToHostSpec[ehHost]; // map
if (pHost != NULL)
{
pHost->m_fReal = TRUE;
if (fOverwriteConnectionInfo)
{
pHost->m_ConnectionString = _bstr_t(pConnInfo->szMachine);
pHost->m_UserName = _bstr_t(pConnInfo->szUserName);
pHost->m_Password = _bstr_t(pConnInfo->szPassword);
}
if (FAILED(wStatus))
{
pHost->m_fUnreachable = TRUE;
}
else
{
pHost->m_fUnreachable = FALSE;
}
}
pHost = NULL;
mfn_Unlock();
}
//
// Update that status of all interfaces in the host.
//
mfn_NotifyHostInterfacesChange(ehHost);
if (FAILED(wStatus))
{
// TODO -- proper return error.
goto end;
}
//
// We first go through and add all the interfaces-
//
mfn_Lock();
pHost = m_mapIdToHostSpec[ehHost]; // map
if (pHost == NULL)
{
mfn_Unlock();
goto end;
}
for (UINT u=0; u<NumNics; u++)
{
LPCWSTR szNic = pszNics[u];
ENGINEHANDLE ehInterface = NULL;
CInterfaceSpec *pISpec = NULL;
BOOL fIsNew = FALSE;
nerr = mfn_LookupInterfaceByGuidLk(
szNic,
TRUE, // TRUE==ok to create
REF ehInterface,
REF pISpec,
REF fIsNew
);
if (nerr == NLBERR_OK)
{
if (fIsNew)
{
//
// We've just created a brand new interface object.
// Add it to the host's list of interfaces, and
// add a reference to this host in the interface object.
//
pISpec->m_ehHostId = ehHost;
pISpec->m_fReal = FALSE; // we'll update this later.
pHost->m_ehInterfaceIdList.push_back(ehInterface);
}
//
// Keep a copy of the machine name in the interface.
// Here we'll update if it's changed -- could happen if
// host's machine name has changed while NLB manager is still
// running.
//
if (pISpec->m_bstrMachineName != pHost->m_MachineName) // bstr
{
pISpec->m_bstrMachineName = pHost->m_MachineName; // bstr
}
}
}
InterfaceListCopy = pHost->m_ehInterfaceIdList; // vector copy
pHost = NULL;
mfn_Unlock();
//
// Having added any new interfaces, we now we go through
// ALL interfaces in the host, refreshing them -- potentially getting
// rid of ones which are no longer in the host.
//
for(u = 0; u < InterfaceListCopy.size(); ++u )
{
ENGINEHANDLE ehIId = InterfaceListCopy[u];
(void) this->RefreshInterface(
ehIId,
TRUE, // TRUE == start a new operation
FALSE // FALSE == this is not cluster-wide
);
}
nerr = NLBERR_OK;
end:
delete pszNics;
return nerr;
}
NLBERROR
CNlbEngine::mfn_LookupInterfaceByGuidLk(
IN LPCWSTR szInterfaceGuid,
IN BOOL fCreate,
OUT ENGINEHANDLE &ehInterface,
OUT CInterfaceSpec* &pISpec,
OUT BOOL &fIsNew
)
{
NLBERROR nerr = NLBERR_INTERNAL_ERROR;
fIsNew = FALSE;
ehInterface = NULL;
pISpec=NULL;
map< ENGINEHANDLE, CInterfaceSpec* >::iterator iter;
for( iter = m_mapIdToInterfaceSpec.begin();
iter != m_mapIdToInterfaceSpec.end();
++iter)
{
CInterfaceSpec* pTmp = (*iter).second;
ENGINEHANDLE ehTmp = (*iter).first;
if (pTmp == NULL || ehTmp == NULL)
{
TRACE_CRIT("%!FUNC! map: unexpected pair(eh=0x%lx, pISpec=%p)",
(UINT) ehTmp, pTmp);
continue;
}
else
{
TRACE_VERB("%!FUNC! map: pair(eh=0x%lx, pISpec=%p), szGuid=%ws",
(UINT) ehTmp, pTmp, (LPCWSTR) pTmp->m_Guid);
}
if (!_wcsicmp((LPCWSTR)pTmp->m_Guid, szInterfaceGuid))
{
// found it!
ehInterface = ehTmp;
pISpec = pTmp;
break;
}
}
if (pISpec!=NULL)
{
if (ehInterface==NULL)
{
TRACE_CRIT("%!FUNC! unexpected null handle for pISpec %ws", szInterfaceGuid);
}
else
{
nerr = NLBERR_OK;
}
goto end;
}
if (!fCreate)
{
nerr = NLBERR_NOT_FOUND;
goto end;
}
//
// Create a new interface
//
{
pISpec = new CInterfaceSpec;
if (pISpec == NULL)
{
nerr = NLBERR_RESOURCE_ALLOCATION_FAILURE;
goto end;
}
fIsNew = TRUE;
pISpec->m_fReal = FALSE;
pISpec->m_Guid = _bstr_t(szInterfaceGuid);
//
// Get us a handle to this interface
//
ehInterface = CNlbEngine::mfn_NewHandleLk(IUICallbacks::OBJ_INTERFACE);
if (ehInterface == NULL)
{
TRACE_CRIT("%!FUNC! could not reserve a new interface handle");
nerr = NLBERR_RESOURCE_ALLOCATION_FAILURE;
delete pISpec;
pISpec=NULL;
goto end;
}
TRACE_VERB("%!FUNC!: map new pair(eh=0x%lx, pISpec=%p), szGuid=%ws",
(UINT) ehInterface, pISpec, (LPCWSTR) pISpec->m_Guid);
m_mapIdToInterfaceSpec[ehInterface] = pISpec;
nerr = NLBERR_OK;
}
end:
return nerr;
}
NLBERROR
CNlbEngine::mfn_LookupInterfaceByIpLk(
IN ENGINEHANDLE ehHost, // OPTIONAL -- if NULL all hosts are looked
IN LPCWSTR szIpAddress,
OUT ENGINEHANDLE &ehInterface
)
{
NLBERROR nerr = NLBERR_INTERNAL_ERROR;
ehInterface = NULL;
map< ENGINEHANDLE, CInterfaceSpec* >::iterator iter;
for( iter = m_mapIdToInterfaceSpec.begin();
iter != m_mapIdToInterfaceSpec.end();
++iter)
{
const CInterfaceSpec* pTmp = (*iter).second;
ENGINEHANDLE ehTmp = (*iter).first;
if (pTmp == NULL || ehTmp == NULL)
{
continue;
}
if (ehHost==NULL || ehHost==pTmp->m_ehHostId)
{
UINT NumIps = pTmp->m_NlbCfg.NumIpAddresses;
const NLB_IP_ADDRESS_INFO *pInfo = pTmp->m_NlbCfg.pIpAddressInfo;
for (UINT u = 0; u<NumIps; u++)
{
if (!wcscmp(pInfo[u].IpAddress, szIpAddress))
{
// found it!
TRACE_VERB(L"%!FUNC! found szIp %ws on ehIF 0x%lx",
szIpAddress, ehTmp);
ehInterface = ehTmp;
break;
}
}
}
}
if (ehInterface == NULL)
{
nerr = NLBERR_NOT_FOUND;
}
else
{
nerr = NLBERR_OK;
}
return nerr;
}
NLBERROR
CNlbEngine::mfn_LookupHostByNameLk(
IN LPCWSTR szHostName,
IN BOOL fCreate,
OUT ENGINEHANDLE &ehHost,
OUT CHostSpec* &pHostSpec,
OUT BOOL &fIsNew
)
{
NLBERROR nerr = NLBERR_INTERNAL_ERROR;
fIsNew = FALSE;
ehHost = NULL;
map< ENGINEHANDLE, CHostSpec* >::iterator iter;
pHostSpec = NULL;
for( iter = m_mapIdToHostSpec.begin();
iter != m_mapIdToHostSpec.end();
++iter)
{
CHostSpec* pTmp= (*iter).second;
ENGINEHANDLE ehTmp = (*iter).first;
if (pTmp == NULL || ehTmp == NULL)
{
TRACE_CRIT("%!FUNC! map: unexpected pair(eh=0x%lx, pHSpec=%p)",
(UINT) ehTmp, pTmp);
continue;
}
else
{
TRACE_VERB("%!FUNC! map: pair(eh=0x%lx, pHSpec=%p), szHost=%ws",
(UINT) ehTmp, pTmp, (LPCWSTR) pTmp->m_MachineName);
}
if (!_wcsicmp(pTmp->m_MachineName, szHostName))
{
// found it!
ehHost = ehTmp;
pHostSpec = pTmp;
break;
}
}
if (pHostSpec!=NULL)
{
if (ehHost==NULL)
{
TRACE_CRIT("%!FUNC! unexpected null handle for pHostSpec %ws", szHostName);
}
else
{
nerr = NLBERR_OK;
}
goto end;
}
if (!fCreate)
{
nerr = NLBERR_NOT_FOUND;
goto end;
}
//
// Create a new host
//
{
pHostSpec = new CHostSpec;
if (pHostSpec == NULL)
{
nerr = NLBERR_RESOURCE_ALLOCATION_FAILURE;
goto end;
}
fIsNew = TRUE;
pHostSpec->m_fReal = FALSE;
pHostSpec->m_MachineName = _bstr_t(szHostName);
//
// Get us a handle to this host
//
ehHost = CNlbEngine::mfn_NewHandleLk(IUICallbacks::OBJ_HOST);
if (ehHost == NULL)
{
TRACE_CRIT("%!FUNC! could not reserve a new host handle");
nerr = NLBERR_RESOURCE_ALLOCATION_FAILURE;
delete pHostSpec;
pHostSpec=NULL;
goto end;
}
m_mapIdToHostSpec[ehHost] = pHostSpec;
TRACE_VERB("%!FUNC!: map new pair(eh=0x%lx, pHost=%p), szName=%ws",
(UINT) ehHost, pHostSpec, (LPCWSTR) pHostSpec->m_MachineName);
nerr = NLBERR_OK;
}
end:
return nerr;
}
VOID
CNlbEngine::mfn_NotifyHostInterfacesChange(ENGINEHANDLE ehHost)
{
vector<ENGINEHANDLE> InterfaceListCopy;
//
// Get copy of interface list (because we can't callback into the UI
// with locks held).
//
{
mfn_Lock();
CHostSpec *pHSpec = NULL;
pHSpec = m_mapIdToHostSpec[ehHost]; // map
if (pHSpec == NULL)
{
TRACE_CRIT("%!FUNC! invalid host handle 0x%lx", (UINT)ehHost);
}
else
{
InterfaceListCopy = pHSpec->m_ehInterfaceIdList; // vector copy
}
mfn_Unlock();
}
for(int i = 0; i < InterfaceListCopy.size(); ++i )
{
ENGINEHANDLE ehIId = InterfaceListCopy[i];
m_pCallbacks->HandleEngineEvent(
IUICallbacks::OBJ_INTERFACE,
NULL, // ehClusterId,
ehIId,
IUICallbacks::EVT_STATUS_CHANGE
);
}
}
NLBERROR
CNlbEngine::LookupClusterByIP(
IN LPCWSTR szIP,
IN const NLB_EXTENDED_CLUSTER_CONFIGURATION *pInitialConfig OPTIONAL,
OUT ENGINEHANDLE &ehCluster,
OUT BOOL &fIsNew
)
//
// if pInitialConfig below is NULL we'll lookup and not try to create.
// if not NULL and we don't find an existing cluster, well create
// a new one and initialize it with the specified configuration.
//
{
NLBERROR nerr = NLBERR_INTERNAL_ERROR;
mfn_Lock();
fIsNew = FALSE;
ehCluster = NULL;
map< ENGINEHANDLE, CEngineCluster* >::iterator iter;
for( iter = m_mapIdToEngineCluster.begin();
iter != m_mapIdToEngineCluster.end();
++iter)
{
CEngineCluster* pTmp = (*iter).second;
ENGINEHANDLE ehTmp = (*iter).first;
LPCWSTR szTmpIp = NULL;
if (pTmp == NULL || ehTmp == NULL)
{
TRACE_CRIT("%!FUNC! map: unexpected pair(eh=0x%lx, pEC=%p)",
(UINT) ehTmp, pTmp);
continue;
}
else
{
szTmpIp = pTmp->m_cSpec.m_ClusterNlbCfg.NlbParams.cl_ip_addr;
TRACE_VERB("%!FUNC! map: pair(eh=0x%lx, pEC=%p), szIP=%ws",
(UINT) ehTmp, pTmp, szTmpIp);
}
if (!_wcsicmp(szTmpIp, szIP))
{
// found it!
ehCluster = ehTmp;
break;
}
}
if (ehCluster!=NULL)
{
nerr = NLBERR_OK;
goto end;
}
if (pInitialConfig == NULL)
{
nerr = NLBERR_NOT_FOUND;
goto end;
}
//
// Create a new cluster
//
{
CEngineCluster* pECluster = new CEngineCluster;
if (pECluster == NULL)
{
TRACE_CRIT("%!FUNC! could not allocate cluster object");
nerr = NLBERR_RESOURCE_ALLOCATION_FAILURE;
goto end;
}
fIsNew = TRUE;
WBEMSTATUS wStatus;
wStatus = pECluster->m_cSpec.m_ClusterNlbCfg.Update(pInitialConfig);
//
// Note: Update above has the side effect of setting
// m_ClusterNlbCfg's szNewPassword field to NULL -- this is what
// we want.
//
if (FAILED(wStatus))
{
TRACE_CRIT("%!FUNC! could not copy cluster spec. Err=0x%lx!",
(UINT) wStatus);
nerr = NLBERR_RESOURCE_ALLOCATION_FAILURE;
goto end;
}
//
// Remove the dedicated IP address, if any from the cluster version of
// NLB Config -- both from the NlbParams and from the IP address list.
//
remove_dedicated_ip_from_nlbcfg(REF pECluster->m_cSpec.m_ClusterNlbCfg);
//
// Get us a handle to this cluster
//
ehCluster = CNlbEngine::mfn_NewHandleLk(IUICallbacks::OBJ_CLUSTER);
if (ehCluster == NULL)
{
TRACE_CRIT("%!FUNC! could not reserve a new cluster handle");
nerr = NLBERR_RESOURCE_ALLOCATION_FAILURE;
delete pECluster;
goto end;
}
m_mapIdToEngineCluster[ehCluster] = pECluster;
TRACE_VERB("%!FUNC!: map new pair(eh=0x%lx, pEC=%p)",
(UINT) ehCluster, pECluster);
mfn_Unlock();
//
// Call the ui to notify it about the new cluster creation.
//
m_pCallbacks->HandleEngineEvent(
IUICallbacks::OBJ_CLUSTER,
ehCluster,
ehCluster,
IUICallbacks::EVT_ADDED
);
ProcessMsgQueue();
mfn_Lock();
nerr = NLBERR_OK;
}
end:
mfn_Unlock();
return nerr;
}
NLBERROR
CNlbEngine::LookupInterfaceByIp(
IN ENGINEHANDLE ehHost, // OPTIONAL -- if NULL all hosts are looked
IN LPCWSTR szIpAddress,
OUT ENGINEHANDLE &ehIf
)
{
NLBERROR nerr;
mfn_Lock();
nerr = mfn_LookupInterfaceByIpLk(ehHost, szIpAddress, REF ehIf);
mfn_Unlock();
return nerr;
}
NLBERROR
CNlbEngine::LookupConnectionInfo(
IN LPCWSTR szConnectionString,
OUT _bstr_t &bstrUsername,
OUT _bstr_t &bstrPassword
)
//
// Look through existing hosts, looking for any which have a matching
// connection string. If found, fill out bstrUsername and bstrPassword
// for that host.
//
{
NLBERROR nerr = NLBERR_NOT_FOUND;
TRACE_VERB(L"-> Lookup: szConnString=%ws", szConnectionString);
bstrUsername = (LPCWSTR) NULL;
bstrPassword = (LPCWSTR) NULL;
mfn_Lock();
map< ENGINEHANDLE, CHostSpec* >::iterator iter;
for( iter = m_mapIdToHostSpec.begin();
iter != m_mapIdToHostSpec.end();
++iter)
{
CHostSpec* pTmp = (*iter).second;
LPCWSTR szHostConnString = NULL;
ENGINEHANDLE ehTmp = (*iter).first;
if (pTmp == NULL || ehTmp == NULL)
{
continue;
}
szHostConnString = (LPCWSTR) pTmp->m_ConnectionString;
if (szHostConnString == NULL)
{
szHostConnString = L"";
}
if (!_wcsicmp(szHostConnString, szConnectionString))
{
// found it! Fill out username and password.
bstrUsername = pTmp->m_UserName;
bstrPassword = pTmp->m_Password;
LPCWSTR szU = (LPCWSTR) bstrUsername;
LPCWSTR szP = (LPCWSTR) bstrPassword;
if (szU == NULL) szU = L"";
if (szP == NULL) szP = L"";
nerr = NLBERR_OK;
TRACE_VERB(L"Found un=%ws pwd=%ws", szU, szP);
break;
}
}
mfn_Unlock();
TRACE_VERB("<- returns 0x%x", nerr);
return nerr;
}
NLBERROR
CNlbEngine::GetInterfaceInformation(
IN ENGINEHANDLE ehInterface,
OUT CHostSpec& hSpec,
OUT CInterfaceSpec& iSpec,
OUT _bstr_t& bstrDisplayName,
OUT INT& iIcon,
OUT _bstr_t& bstrStatus
)
/*
Looks up a bunch of information about the specified interface.
bstrDisplayName -- this is a combination of the host name and interface name
bstrDisplay -- text version of the interface's operational status.
iIcon -- icon version of the interface's operational status.
(one of the Document::IconNames enums)
*/
{
// First get host and interface spec.
//
NLBERROR nerr;
LPCWSTR szHostName = L"";
LPCWSTR szStatus = L"";
bstrDisplayName = (LPCWSTR) NULL;
bstrStatus = (LPCWSTR) NULL;
iIcon = 0;
nerr = this->GetInterfaceSpec(
ehInterface,
REF iSpec
);
if (NLBFAILED(nerr))
{
TRACE_CRIT("%!FUNC! : could not get interface spec! nerr=0x%lx", nerr);
goto end;
}
nerr = this->GetHostSpec(
iSpec.m_ehHostId,
REF hSpec
);
if (NLBOK(nerr))
{
szHostName = (LPCWSTR) hSpec.m_MachineName;
if (szHostName == NULL)
{
szHostName = L"";
}
}
else
{
TRACE_CRIT("%!FUNC! : could not get host spec! nerr=0x%lx", nerr);
goto end;
}
//
// Determine the icon and bstrStatus
//
if (hSpec.m_fUnreachable)
{
szStatus = GETRESOURCEIDSTRING(IDS_HOST_STATE_UNREACHABLE); //"Unreachable";
iIcon = Document::ICON_HOST_UNREACHABLE;
}
else if (iSpec.m_fPending)
{
szStatus = GETRESOURCEIDSTRING(IDS_HOST_STATE_PENDING); //"Pending";
iIcon = Document::ICON_CLUSTER_PENDING;
}
else if (iSpec.m_fMisconfigured)
{
szStatus = GETRESOURCEIDSTRING(IDS_HOST_STATE_MISCONFIGURED); // "Misconfigured"
iIcon = Document::ICON_HOST_MISCONFIGURED;
}
else if (!hSpec.m_fReal)
{
szStatus = GETRESOURCEIDSTRING(IDS_HOST_STATE_UNKNOWN); // "Unknown";
iIcon = Document::ICON_HOST_UNKNOWN;
}
else
{
szStatus = GETRESOURCEIDSTRING(IDS_HOST_STATE_UNKNOWN); // "Unknown";
iIcon = Document::ICON_HOST_OK;
//
// Choose icon based on operational state if we have it
//
if (!iSpec.m_NlbCfg.IsNlbBound())
{
szStatus = GETRESOURCEIDSTRING(IDS_STATE_NLB_NOT_BOUND); // L"NLB not bound";
}
else if (iSpec.m_fValidClusterState)
{
switch(iSpec.m_dwClusterState)
{
case WLBS_CONVERGING:
iIcon = Document::ICON_HOST_CONVERGING;
szStatus = GETRESOURCEIDSTRING(IDS_STATE_CONVERGING);
// szStatus = L"Converging";
break;
case WLBS_CONVERGED:
case WLBS_DEFAULT:
iIcon = Document::ICON_HOST_STARTED;
szStatus = GETRESOURCEIDSTRING(IDS_STATE_CONVERGED);
// szStatus = L"Converged";
break;
case WLBS_STOPPED:
szStatus = GETRESOURCEIDSTRING(IDS_HOST_STATE_STOPPED);
iIcon = Document::ICON_HOST_STOPPED;
// szStatus = L"Stopped";
break;
case WLBS_SUSPENDED:
szStatus = GETRESOURCEIDSTRING(IDS_HOST_STATE_SUSPENDED);
iIcon = Document::ICON_HOST_SUSPENDED;
// szStatus = L"Suspended";
break;
case WLBS_DRAINING:
szStatus = GETRESOURCEIDSTRING(IDS_HOST_DRAINING);
// szStatus = L"Draining";
iIcon = Document::ICON_HOST_DRAINING;
break;
case WLBS_DISCONNECTED:
szStatus = GETRESOURCEIDSTRING(IDS_HOST_DISCONNECTED);
// szStatus = L"Disconnected";
iIcon = Document::ICON_HOST_DISCONNECTED;
break;
default:
//
// Don't know what this is -- default to "OK" icon.
//
szStatus = GETRESOURCEIDSTRING(IDS_HOST_STATE_UNKNOWN);
// szStatus = L"Unknown";
iIcon = Document::ICON_HOST_OK;
break;
}
}
}
bstrStatus = _bstr_t(szStatus);
//
// Fill out the DisplayName
//
{
WBEMSTATUS wStat;
LPWSTR szAdapter = L"";
WCHAR rgText[256];
wStat = iSpec.m_NlbCfg.GetFriendlyName(&szAdapter);
if (FAILED(wStat))
{
szAdapter = NULL;
}
StringCbPrintf(
rgText,
sizeof(rgText),
L"%ws(%ws)",
szHostName,
(szAdapter==NULL ? L"" : szAdapter)
);
delete szAdapter;
bstrDisplayName = _bstr_t(rgText);
}
end:
return nerr;
}
NLBERROR
CNlbEngine::GetInterfaceIdentification(
IN ENGINEHANDLE ehInterface,
OUT ENGINEHANDLE& ehHost,
OUT ENGINEHANDLE& ehCluster,
OUT _bstr_t& bstrFriendlyName,
OUT _bstr_t& bstrDisplayName,
OUT _bstr_t& bstrHostName
)
{
// First get host and interface spec.
//
NLBERROR nerr = NLBERR_INTERNAL_ERROR;
CInterfaceSpec *pISpec = NULL;
LPCWSTR szHostName = L"";
mfn_Lock();
bstrFriendlyName= (LPCWSTR) NULL;
bstrDisplayName = (LPCWSTR) NULL;
ehHost = NULL;
ehCluster = NULL;
pISpec = m_mapIdToInterfaceSpec[ehInterface]; // map
if (pISpec == NULL)
{
TRACE_CRIT("%!FUNC! : could not get interface spec for ehI 0x%lx!",
ehInterface);
nerr = NLBERR_INTERFACE_NOT_FOUND;
goto end;
}
if (pISpec->m_ehHostId == NULL)
{
TRACE_CRIT("%!FUNC! : ehI 0x%lx has NULL ehHost spec!", ehInterface);
goto end;
}
else
{
CHostSpec *pHSpec = NULL;
pHSpec = m_mapIdToHostSpec[pISpec->m_ehHostId]; // map
if (pHSpec == NULL)
{
TRACE_CRIT("%!FUNC! : ehI 0x%lx has invalid ehHost 0x%lx!",
ehInterface, pISpec->m_ehHostId);
goto end;
}
bstrHostName = pHSpec->m_MachineName;
szHostName = (LPCWSTR) pHSpec->m_MachineName;
if (szHostName == NULL)
{
szHostName = L"";
}
}
nerr = NLBERR_OK;
ehHost = pISpec->m_ehHostId;
ehCluster = pISpec->m_ehCluster;
//
// Fill out the bstrFriendlyName and bstrDisplayName
//
{
WBEMSTATUS wStat;
LPWSTR szAdapter = L"";
WCHAR rgText[256];
wStat = pISpec->m_NlbCfg.GetFriendlyName(&szAdapter);
if (FAILED(wStat))
{
szAdapter = NULL;
}
StringCbPrintf(
rgText,
sizeof(rgText),
L"%ws(%ws)",
szHostName,
(szAdapter==NULL ? L"" : szAdapter)
);
bstrFriendlyName = _bstr_t(szAdapter);
delete szAdapter;
bstrDisplayName = _bstr_t(rgText);
}
end:
mfn_Unlock();
return nerr;
}
NLBERROR
CNlbEngine::GetClusterIdentification(
IN ENGINEHANDLE ehCluster,
OUT _bstr_t& bstrIpAddress,
OUT _bstr_t& bstrDomainName,
OUT _bstr_t& bstrDisplayName
)
{
NLBERROR nerr = NLBERR_NOT_FOUND;
WCHAR rgTmp[256];
bstrIpAddress = (LPCWSTR) NULL;
bstrDomainName = (LPCWSTR) NULL;
bstrDisplayName = (LPCWSTR) NULL;
mfn_Lock();
CEngineCluster *pECluster = m_mapIdToEngineCluster[ehCluster]; // map
if (pECluster != NULL)
{
WLBS_REG_PARAMS *pParams =&pECluster->m_cSpec.m_ClusterNlbCfg.NlbParams;
StringCbPrintf(
rgTmp,
sizeof(rgTmp),
L"%ws(%ws)",
pParams->domain_name,
pParams->cl_ip_addr
);
bstrIpAddress = _bstr_t(pParams->cl_ip_addr);
bstrDomainName = _bstr_t(pParams->domain_name);
bstrDisplayName = _bstr_t(rgTmp);
nerr = NLBERR_OK;
}
mfn_Unlock();
return nerr;
}
NLBERROR
CNlbEngine::ValidateNewClusterIp(
IN ENGINEHANDLE ehCluster, // OPTIONAL
IN LPCWSTR szIp,
OUT BOOL &fExistsOnRawIterface,
IN OUT CLocalLogger &logConflict
)
{
NLBERROR nerr = NLBERR_INVALID_IP_ADDRESS_SPECIFICATION;
BOOL fRet = FALSE;
ENGINEHANDLE ehTmp = NULL;
BOOL fIsNew = FALSE;
fExistsOnRawIterface = FALSE;
//
// Check that CIP is not used elsewhere
//
nerr = this->LookupClusterByIP(
szIp,
NULL, // pInitialConfig
REF ehTmp,
REF fIsNew
);
if (NLBOK(nerr) && ehCluster != ehTmp)
{
//
// CIP matches some other cluster!
//
_bstr_t bstrIpAddress;
_bstr_t bstrDomainName;
_bstr_t bstrClusterDisplayName;
LPCWSTR szCluster = NULL;
nerr = this->GetClusterIdentification(
ehTmp,
REF bstrIpAddress,
REF bstrDomainName,
REF bstrClusterDisplayName
);
if (NLBOK(nerr))
{
szCluster = bstrClusterDisplayName;
}
if (szCluster == NULL)
{
szCluster = L"";
}
logConflict.Log(IDS_CLUSTER_XXX, szCluster);
goto end;
}
ehTmp = NULL;
nerr = this->LookupInterfaceByIp(
NULL, // NULL == search for all hosts
szIp,
REF ehTmp
);
if (NLBOK(nerr))
{
ENGINEHANDLE ehExistingCluster;
ENGINEHANDLE ehExistingHost;
_bstr_t bstrDisplayName;
_bstr_t bstrFriendlyName;
_bstr_t bstrHostName;
LPCWSTR szInterface = NULL;
nerr = this->GetInterfaceIdentification(
ehTmp,
ehExistingHost,
ehExistingCluster,
bstrFriendlyName,
bstrDisplayName,
bstrHostName
);
if (NLBOK(nerr))
{
if (ehCluster == NULL || ehCluster != ehExistingCluster)
{
//
// CONFLICT
//
if (ehExistingCluster == NULL)
{
//
// Conflicting interface NOT part of an existing cluster.
//
fExistsOnRawIterface = TRUE;
}
szInterface = bstrDisplayName;
if (szInterface == NULL)
{
szInterface = L"";
}
logConflict.Log(IDS_INTERFACE_XXX, szInterface);
goto end;
}
}
}
fRet = TRUE;
end:
if (fRet)
{
nerr = NLBERR_OK;
}
else
{
nerr = NLBERR_INVALID_IP_ADDRESS_SPECIFICATION;
}
return nerr;
}
NLBERROR
CNlbEngine::ValidateNewDedicatedIp(
IN ENGINEHANDLE ehIF,
IN LPCWSTR szDip,
IN OUT CLocalLogger &logConflict
)
{
NLBERROR nerr = NLBERR_INVALID_IP_ADDRESS_SPECIFICATION;
BOOL fRet = FALSE;
ENGINEHANDLE ehTmp = NULL;
BOOL fIsNew = FALSE;
if (ehIF == NULL)
{
ASSERT(FALSE);
goto end;
}
//
// Check that DIP is not used elsewhere
//
nerr = this->LookupClusterByIP(
szDip,
NULL, // pInitialConfig
REF ehTmp,
REF fIsNew
);
if (NLBOK(nerr))
{
//
// DIP matches some cluster!
//
_bstr_t bstrIpAddress;
_bstr_t bstrDomainName;
_bstr_t bstrClusterDisplayName;
LPCWSTR szCluster = NULL;
nerr = this->GetClusterIdentification(
ehTmp,
REF bstrIpAddress,
REF bstrDomainName,
REF bstrClusterDisplayName
);
if (NLBOK(nerr))
{
szCluster = bstrClusterDisplayName;
}
if (szCluster == NULL)
{
szCluster = L"";
}
logConflict.Log(IDS_CLUSTER_XXX, szCluster);
goto end;
}
ehTmp = NULL;
nerr = this->LookupInterfaceByIp(
NULL, // NULL == search for all hosts
szDip,
REF ehTmp
);
if (NLBOK(nerr))
{
if (ehTmp != ehIF)
{
ENGINEHANDLE ehHost1;
ENGINEHANDLE ehCluster1;
_bstr_t bstrDisplayName1;
_bstr_t bstrFriendlyName;
_bstr_t bstrHostName;
LPCWSTR szInterface = NULL;
nerr = this->GetInterfaceIdentification(
ehTmp,
ehHost1,
ehCluster1,
bstrFriendlyName,
bstrDisplayName1,
bstrHostName
);
szInterface = bstrDisplayName1;
if (szInterface == NULL)
{
szInterface = L"";
}
logConflict.Log(IDS_INTERFACE_XXX, szInterface);
goto end;
}
}
fRet = TRUE;
end:
if (fRet)
{
nerr = NLBERR_OK;
}
else
{
nerr = NLBERR_INVALID_IP_ADDRESS_SPECIFICATION;
}
return nerr;
}
VOID
CNlbEngine::mfn_ReallyUpdateInterface(
IN ENGINEHANDLE ehInterface,
IN NLB_EXTENDED_CLUSTER_CONFIGURATION &refNewConfig
//IN OUT BOOL &fClusterPropertiesUpdated
)
{
#define NLBMGR_MAX_OPERATION_DURATION 120 // 2 minutes
BOOL fCancelled = FALSE;
CHostSpec *pHSpec = NULL;
CInterfaceSpec *pISpec = NULL;
WBEMSTATUS CompletionStatus, wStatus;
_bstr_t bstrNicGuid;
_bstr_t bstrHostName;
_bstr_t bstrUserName;
_bstr_t bstrConnectionString;
_bstr_t bstrPassword;
NLBERROR nerr;
CLocalLogger logClientIdentification;
WCHAR rgMachineName[512];
DWORD cbMachineName = (DWORD) ASIZE(rgMachineName);
BOOL fRet;
DWORD StartTime = GetTickCount();
fRet = GetComputerNameEx(
ComputerNameDnsFullyQualified,
rgMachineName,
&cbMachineName
);
if (!fRet)
{
*rgMachineName = 0;
}
logClientIdentification.Log(IDS_CLIENT_IDENTIFICATION, rgMachineName);
mfn_Lock();
nerr = CNlbEngine::mfn_GetHostFromInterfaceLk(ehInterface,REF pISpec, REF pHSpec);
if (nerr != NLBERR_OK)
{
TRACE_CRIT("%!FUNC! could not get pISpec,pHSpec for ehIF 0x%lx",
ehInterface);
goto end_unlock;
}
//
// We need to keep local bstrs because once we release the lock
// pHSpec may go away (or re-assign it's bstrs) -- .net svr bug 513056.
//
bstrUserName = pHSpec->m_UserName;
bstrConnectionString= pHSpec->m_ConnectionString;
bstrPassword = pHSpec->m_Password;
bstrNicGuid = pISpec->m_Guid;
bstrHostName = pHSpec->m_MachineName;
WMI_CONNECTION_INFO ConnInfo;
ConnInfo.szUserName = (LPCWSTR) bstrUserName;
ConnInfo.szPassword = (LPCWSTR) bstrPassword;
ConnInfo.szMachine = (LPCWSTR) bstrConnectionString;
LPCWSTR szNicGuid = (LPCWSTR) bstrNicGuid;
LPCWSTR szHostName = (LPCWSTR) bstrHostName;
if (szNicGuid == NULL)
{
TRACE_CRIT("%!FUNC! ERROR -- NULL szNicGuid!");
goto end_unlock;
}
mfn_Unlock();
UINT Generation; // TODO track the generation
LPWSTR pLog = NULL;
LPCWSTR szClusterIp = refNewConfig.NlbParams.cl_ip_addr;
ProcessMsgQueue(); // TODO: eliminate when doing this in the background
wStatus = NlbHostDoUpdate(
&ConnInfo,
szNicGuid,
logClientIdentification.GetStringSafe(),
// L"NLB Manager on <this machine>", // TODO: localize
&refNewConfig,
&Generation,
&pLog
);
if (wStatus == WBEM_S_PENDING)
{
m_pCallbacks->Log(
IUICallbacks::LOG_INFORMATIONAL,
szClusterIp,
szHostName,
IDS_LOG_WAITING_FOR_PENDING_OPERATION, // %d
Generation
);
}
while (wStatus == WBEM_S_PENDING)
{
//
// Check if we've exceeded the absolute time for cancellation.
//
{
DWORD CurrentTime = GetTickCount();
UINT DurationInSeconds=0;
if (CurrentTime < StartTime)
{
//
// Timer overflow -- fixup: we do this the "cheap" way
// of re-setting start time, so that we'll end up with at most
// twice the max delay in the event of a timer overflow.
//
StartTime = CurrentTime;
}
DurationInSeconds= (UINT) (CurrentTime - StartTime)/1000;
if ( DurationInSeconds > NLBMGR_MAX_OPERATION_DURATION)
{
TRACE_CRIT("%!FUNC! Operation canceled because max time exceeded");
fCancelled = TRUE;
break;
}
}
//
// Check if this pending operation is cancelled...
//
{
CInterfaceSpec *pTmpISpec = NULL;
ENGINEHANDLE ehOperation = NULL;
mfn_Lock();
pTmpISpec = m_mapIdToInterfaceSpec[ehInterface]; // map
if (pTmpISpec == NULL)
{
ASSERT(FALSE);
wStatus = WBEM_E_CRITICAL_ERROR;
mfn_Unlock();
break;
}
ehOperation = pTmpISpec->m_ehPendingOperation;
if (ehOperation != NULL)
{
CEngineOperation *pOperation;
pOperation = m_mapIdToOperation[ehOperation];
if (pOperation != NULL)
{
if (pOperation->fCanceled)
{
TRACE_CRIT("%!FUNC! ehOp 0x%lx CANCELLED!",
ehOperation);
fCancelled = TRUE;
}
}
}
mfn_Unlock();
if (fCancelled)
{
break;
}
}
if (pLog != NULL)
{
mfn_UpdateInterfaceStatusDetails(ehInterface, pLog);
delete pLog;
pLog = NULL;
}
for (UINT u=0;u<50;u++)
{
ProcessMsgQueue(); // TODO: eliminate when doing this in the background
Sleep(100);
}
ULONG uIpAddress = 0;
wStatus = NlbHostPing(ConnInfo.szMachine, 2000, &uIpAddress);
if (FAILED(wStatus))
{
TRACE_CRIT("%!FUNC!: ping %ws failed!", ConnInfo.szMachine);
wStatus = WBEM_S_PENDING;
continue;
}
wStatus = NlbHostGetUpdateStatus(
&ConnInfo,
szNicGuid,
Generation,
&CompletionStatus,
&pLog
);
if (!FAILED(wStatus))
{
wStatus = CompletionStatus;
}
}
if (fCancelled == TRUE)
{
wStatus = WBEM_S_OPERATION_CANCELLED;
}
else
{
BOOL fNewRctPassword = FALSE;
//
// Get latest information from the host
//
(void) this->RefreshInterface(
ehInterface,
FALSE, // FALSE == don't start a new operation
FALSE // FALSE == this is not cluster-wide
);
//
// If we're doing a RCT password-change, AND the updated operation
// completed successfully AND the cluster's fNewRctPassword flag is
// set, we'll update the cluster's rct hash value and clear the
// fNewRctPassword flag.
//
fNewRctPassword = (refNewConfig.GetNewRemoteControlPasswordRaw()!=NULL);
if (fNewRctPassword && !FAILED(wStatus))
{
CEngineCluster *pECluster = NULL;
mfn_Lock();
pISpec = m_mapIdToInterfaceSpec[ehInterface]; // map
if (pISpec != NULL)
{
ENGINEHANDLE ehCluster = pISpec->m_ehCluster;
if (ehCluster != NULL)
{
pECluster = m_mapIdToEngineCluster[ehCluster]; // map
}
}
if (pECluster != NULL)
{
if (pECluster->m_cSpec.m_fNewRctPassword)
{
//
// Update cluster's rct hash and clear m_fNewRctPassword
//
DWORD dwNewHash;
dwNewHash = CfgUtilGetHashedRemoteControlPassword(
&pISpec->m_NlbCfg.NlbParams
);
TRACE_VERB(L"Updating cluster remote control password to %lu",
dwNewHash
);
CfgUtilSetHashedRemoteControlPassword(
&pECluster->m_cSpec.m_ClusterNlbCfg.NlbParams,
dwNewHash
);
pECluster->m_cSpec.m_fNewRctPassword = FALSE;
}
}
mfn_Unlock();
}
}
//
// Log the final results.
//
{
IUICallbacks::LogEntryHeader Header;
Header.szDetails = pLog;
Header.szCluster = szClusterIp;
Header.szHost = szHostName;
if (FAILED(wStatus))
{
Header.type = IUICallbacks::LOG_ERROR;
if (Generation != 0)
{
m_pCallbacks->LogEx(
&Header,
IDS_LOG_FINAL_STATUS_FAILED,
Generation,
wStatus
);
}
else
{
m_pCallbacks->LogEx(
&Header,
IDS_LOG_FINAL_STATUS_FAILED_NOGEN,
wStatus
);
}
}
else
{
Header.type = IUICallbacks::LOG_INFORMATIONAL;
m_pCallbacks->LogEx(
&Header,
IDS_LOG_FINAL_STATUS_SUCCEEDED,
Generation
);
}
}
if (pLog != NULL)
{
delete pLog;
pLog = NULL;
}
ProcessMsgQueue(); // TODO: eliminate when doing this in the background
mfn_Lock();
end_unlock:
mfn_Unlock();
return;
}
VOID
CNlbEngine::mfn_SetInterfaceMisconfigStateLk(
IN CInterfaceSpec *pIF,
IN BOOL fMisconfig,
IN LPCWSTR szMisconfigDetails
)
/*
Set/clear the interface misconfigured status.
If fMisconfig, save away the szMisconfigDetails, else clear the
internal misconfig details field.
*/
{
pIF->m_fMisconfigured = fMisconfig;
if (fMisconfig)
{
pIF->m_bstrStatusDetails = _bstr_t(szMisconfigDetails);
}
else
{
pIF->m_bstrStatusDetails = LPCWSTR(NULL);
}
}
BOOL
CNlbEngine::mfn_HostHasManagedClustersLk(CHostSpec *pHSpec)
//
// Return true if there exists at least one IF which is part of
// a cluster displayed by NLB Manager.
//
{
BOOL fRet = FALSE;
vector<ENGINEHANDLE> &InterfaceList = pHSpec->m_ehInterfaceIdList;
for(UINT u = 0; u < InterfaceList.size(); ++u )
{
ENGINEHANDLE ehI = InterfaceList[u];
CInterfaceSpec *pISpec = m_mapIdToInterfaceSpec[ehI]; // map
if (pISpec != NULL)
{
if (pISpec->m_ehCluster != NULL)
{
fRet = TRUE;
break;
}
}
}
return fRet;
}
void
CNlbEngine::mfn_UpdateInterfaceStatusDetails(
ENGINEHANDLE ehIF,
LPCWSTR szDetails
)
//
// Update the textual status details field of the interface.
// These details give the detailed information on the current status
// of the interface. For example: if misconfigured, misconfig details,
// or if there is an update operation ongoing, details about that operation.
//
{
CInterfaceSpec *pISpec = NULL;
mfn_Lock();
pISpec = m_mapIdToInterfaceSpec[ehIF]; // map
if (pISpec != NULL)
{
pISpec->m_bstrStatusDetails = szDetails;
}
mfn_Unlock();
}
BOOL
validate_extcfg(
const NLB_EXTENDED_CLUSTER_CONFIGURATION &Config
)
/*
Do some internal checks to make sure that the data is valid.
Does not change internal state.
*/
{
BOOL fRet = FALSE;
if (Config.fBound)
{
WBEMSTATUS Status;
//
// NLB is bound -- let's validate NLB paramaters.
//
WLBS_REG_PARAMS TmpParams = Config.NlbParams; // struct copy.
BOOL fConnectivityChange = FALSE;
Status = CfgUtilsAnalyzeNlbUpdate(
NULL, // OPTIONAL pCurrentParams
&TmpParams,
&fConnectivityChange
);
if (FAILED(Status))
{
goto end;
}
}
fRet = TRUE;
end:
return fRet;
}
VOID
remove_dedicated_ip_from_nlbcfg(
NLB_EXTENDED_CLUSTER_CONFIGURATION &ClusterCfg
)
{
LPCWSTR szDedIp = ClusterCfg.NlbParams.ded_ip_addr;
UINT NumIps = ClusterCfg.NumIpAddresses;
NLB_IP_ADDRESS_INFO
*pIpInfo = ClusterCfg.pIpAddressInfo;
if (*szDedIp == 0) goto end;
//
// Go through address list, looking for this Ip address. If we find it,
// we remove it.
//
for (UINT u=0; u<NumIps; u++)
{
if (!wcscmp(szDedIp, pIpInfo[u].IpAddress))
{
//
// Found it! Move everything ahead up by one.
//
for (UINT v=u+1; v<NumIps; v++)
{
pIpInfo[v-1]=pIpInfo[v]; // Struct copy.
}
ClusterCfg.NumIpAddresses--;
//
// Zero-out last entry just for grins...
//
pIpInfo[NumIps-1].IpAddress[0]=0;
pIpInfo[NumIps-1].SubnetMask[0]=0;
break;
}
}
ARRAYSTRCPY(ClusterCfg.NlbParams.ded_ip_addr, CVY_DEF_DED_IP_ADDR);
ARRAYSTRCPY(ClusterCfg.NlbParams.ded_net_mask, CVY_DEF_DED_NET_MASK);
end:
return;
}
BOOL
get_used_port_rule_priorities(
IN const NLB_EXTENDED_CLUSTER_CONFIGURATION &Config,
IN UINT NumRules,
IN const WLBS_PORT_RULE rgRules[],
IN OUT ULONG rgUsedPriorities[] // At least NumRules
)
/*
Add to the array of bitmaps the priority that
that represents the used priorities for
each specified port rule. If the port rule is not single-host
the bitmap for that port rule is left unmodified
*/
{
const WLBS_REG_PARAMS *pParams = &Config.NlbParams;
WLBS_PORT_RULE *pCfgRules = NULL;
WBEMSTATUS wStatus;
UINT NumCfgRules = 0;
BOOL fRet = FALSE;
//
// Get the list of port rules in Config.
//
wStatus = CfgUtilGetPortRules(
pParams,
&pCfgRules,
&NumCfgRules
);
if (FAILED(wStatus))
{
pCfgRules = NULL;
goto end;
}
//
// For each port rule in rgRules, if single-host mode,
// locate the corresponding port rule in Config, and if found,
// (and the latter port rule is single-host) make a bitmap out of
// the single-host priority.
//
for (UINT u=0; u<NumRules; u++)
{
const WLBS_PORT_RULE *pCfgRule = NULL;
const WLBS_PORT_RULE *pRule = rgRules+u;
if (pRule->mode == CVY_SINGLE)
{
UINT uPriority = 0;
pCfgRule = find_port_rule(
pCfgRules,
NumCfgRules,
rgRules[u].virtual_ip_addr,
rgRules[u].start_port
);
if (pCfgRule != NULL && pCfgRule->mode == CVY_SINGLE)
{
uPriority = pCfgRule->mode_data.single.priority;
}
if (uPriority!=0)
{
rgUsedPriorities[u] |= 1<<(uPriority-1);
}
}
}
end:
delete[] pCfgRules;
return fRet;
}
const WLBS_PORT_RULE *
find_port_rule(
const WLBS_PORT_RULE *pRules,
UINT NumRules,
LPCWSTR szVIP,
UINT StartPort
)
/*
Locate the port rule with the specified vip and start-port.
Return pointer to the found rule or NULL if not found.
*/
{
const WLBS_PORT_RULE *pFoundRule = NULL;
LPCWSTR szAllVip = GETRESOURCEIDSTRING(IDS_REPORT_VIP_ALL);
for (UINT u=0;u<NumRules; u++)
{
const WLBS_PORT_RULE *pRule = pRules+u;
LPCWSTR szRuleVip = pRule->virtual_ip_addr;
//
// Unfortunately, "All" and "255.255.255.255" are synonomous :-(
//
if (!lstrcmpi(szVIP, L"255.255.255.255"))
{
szVIP = szAllVip;
}
if (!lstrcmpi(szRuleVip, L"255.255.255.255"))
{
szRuleVip = szAllVip;
}
if ( !lstrcmpi(szVIP, szRuleVip)
&& StartPort == pRule->start_port)
{
pFoundRule = pRule;
break;
}
}
return pFoundRule;
}
CEngineOperation *
CNlbEngine::mfn_NewOperationLk(ENGINEHANDLE ehObj, PVOID pvCtxt, LPCWSTR szDescription)
{
ENGINEHANDLE ehOperation;
CEngineOperation *pOperation = NULL;
if (m_fPrepareToDeinitialize)
{
goto end;
}
ehOperation = CNlbEngine::mfn_NewHandleLk(IUICallbacks::OBJ_OPERATION);
if (ehOperation == NULL)
{
TRACE_CRIT("%!FUNC! could not reserve a new operation handle");
goto end;
}
pOperation = new CEngineOperation(ehOperation, ehObj, pvCtxt);
if (pOperation == NULL)
{
TRACE_CRIT("%!FUNC!: allocation failure");
goto end;
}
pOperation->bstrDescription = _bstr_t(szDescription);
TRACE_VERB(L"%!FUNC!: map new pair(eh=0x%lx, pISpec=%p), szDescr=%ws",
(UINT) ehOperation, pOperation,
szDescription==NULL? L"<null>" : szDescription);
m_mapIdToOperation[ehOperation] = pOperation;
end:
return pOperation;
}
VOID
CNlbEngine::mfn_DeleteOperationLk(ENGINEHANDLE ehOperation)
{
CEngineOperation *pOperation;
pOperation = m_mapIdToOperation[ehOperation];
if (pOperation == NULL)
{
ASSERT(!"corrupted operation");
TRACE_CRIT("%!FUNC! corrupted operation eh 0x%lx!", ehOperation);
}
else
{
m_mapIdToOperation.erase(ehOperation);
TRACE_VERB("%!FUNC! deleting operation eh 0x%lx pOp 0x%p",
ehOperation, pOperation);
pOperation->ehOperation = NULL;
delete pOperation;
}
}
CEngineOperation *
CNlbEngine::mfn_GetOperationLk(ENGINEHANDLE ehOp)
{
CEngineOperation *pOperation;
pOperation = m_mapIdToOperation[ehOp];
if (pOperation == NULL || pOperation->ehOperation != ehOp)
{
TRACE_CRIT("%!FUNC! invalid or corrupt ehOp 0x%lx (pOp=0x%p)",
ehOp, pOperation);
pOperation = NULL;
}
return pOperation;
}
NLBERROR
CNlbEngine::mfn_StartInterfaceOperationLk(
IN ENGINEHANDLE ehIF,
IN PVOID pvCtxt,
IN LPCWSTR szDescription,
OUT ENGINEHANDLE *pExistingOperation
)
{
NLBERROR nerr = NLBERR_OK;
CInterfaceSpec *pISpec = NULL;
CEngineOperation *pOperation = NULL;
*pExistingOperation = NULL;
pISpec = m_mapIdToInterfaceSpec[ehIF]; // map
if (pISpec == NULL)
{
nerr = NLBERR_NOT_FOUND;
goto end;
}
if (pISpec->m_ehPendingOperation != NULL)
{
TRACE_CRIT("%!FUNC!: Not starting operation on ehIF 0x%lx because operation 0x%lx already pending",
ehIF, pISpec->m_ehPendingOperation);
*pExistingOperation = pISpec->m_ehPendingOperation;
nerr = NLBERR_BUSY;
goto end;
}
pOperation = mfn_NewOperationLk(
ehIF,
pvCtxt,
szDescription
);
if (pOperation == NULL)
{
nerr = NLBERR_RESOURCE_ALLOCATION_FAILURE;
goto end;
}
TRACE_VERB("%!FUNC!: Starting operation eh 0x%lx on ehIF 0x%lx",
pOperation->ehOperation, ehIF);
pISpec->m_ehPendingOperation = pOperation->ehOperation;
pISpec->m_fPending = TRUE;
nerr = NLBERR_OK;
// fall through ...
end:
return nerr;
}
VOID
CNlbEngine::mfn_StopInterfaceOperationLk(
IN ENGINEHANDLE ehIF
)
{
CInterfaceSpec *pISpec = NULL;
ENGINEHANDLE ehOperation = NULL;
pISpec = m_mapIdToInterfaceSpec[ehIF]; // map
if (pISpec == NULL)
{
TRACE_CRIT("%!FUNC!: Invalid ehIF 0x%lx", ehIF);
goto end;
}
ehOperation = pISpec->m_ehPendingOperation;
if (ehOperation != NULL)
{
TRACE_VERB("%!FUNC!: Stopping operation eh 0x%lx on pIspec 0x%p",
ehOperation, pISpec);
pISpec->m_ehPendingOperation = NULL;
pISpec->m_fPending = FALSE;
mfn_DeleteOperationLk(ehOperation);
}
else
{
TRACE_VERB("%!FUNC!: No operation to stop on pISpec 0x%p", pISpec);
}
end:
return;
}
NLBERROR
CNlbEngine::mfn_StartClusterOperationLk(
IN ENGINEHANDLE ehCluster,
IN PVOID pvCtxt,
IN LPCWSTR szDescription,
OUT ENGINEHANDLE *pExistingOperation
)
{
NLBERROR nerr = NLBERR_OK;
CEngineCluster *pECluster = NULL;
CClusterSpec *pCSpec = NULL;
CEngineOperation *pOperation = NULL;
*pExistingOperation = NULL;
pECluster = m_mapIdToEngineCluster[ehCluster]; // map
if (pECluster == NULL)
{
nerr = NLBERR_NOT_FOUND;
goto end;
}
pCSpec = &pECluster->m_cSpec;
if (pCSpec->m_ehPendingOperation != NULL)
{
TRACE_CRIT("%!FUNC!: Not starting operation on ehCluster 0x%lx because operation 0x%lx already pending",
ehCluster, pCSpec->m_ehPendingOperation);
*pExistingOperation = pCSpec->m_ehPendingOperation;
nerr = NLBERR_BUSY;
goto end;
}
pOperation = mfn_NewOperationLk(
ehCluster,
pvCtxt,
szDescription
);
if (pOperation == NULL)
{
nerr = NLBERR_RESOURCE_ALLOCATION_FAILURE;
goto end;
}
TRACE_VERB("%!FUNC!: Starting operation eh 0x%lx on ehC 0x%lx",
pOperation->ehOperation, ehCluster);
pCSpec->m_ehPendingOperation = pOperation->ehOperation;
pCSpec->m_fPending = TRUE;
nerr = NLBERR_OK;
// fall through ...
end:
return nerr;
}
VOID
CNlbEngine::mfn_StopClusterOperationLk(
ENGINEHANDLE ehCluster
)
{
CEngineCluster *pECluster = NULL;
CClusterSpec *pCSpec = NULL;
ENGINEHANDLE ehOperation = NULL;
pECluster = m_mapIdToEngineCluster[ehCluster]; // map
if (pECluster == NULL)
{
TRACE_CRIT("%!FUNC!: Invalid ehC 0x%lx", ehCluster);
goto end;
}
pCSpec = &pECluster->m_cSpec;
ehOperation = pCSpec->m_ehPendingOperation;
if (ehOperation != NULL)
{
TRACE_VERB("%!FUNC!: Stopping operation eh 0x%lx on pCSpec 0x%p",
ehOperation, pCSpec);
pCSpec->m_ehPendingOperation = NULL;
pCSpec->m_fPending = FALSE;
mfn_DeleteOperationLk(ehOperation);
}
else
{
TRACE_VERB("%!FUNC!: No operation to stop on pCSpec 0x%p", pCSpec);
}
end:
return;
}
UINT
CNlbEngine::ListPendingOperations(
CLocalLogger &logOperations
)
//
// List pending operations -- but ONLY list those operations that
// contain non-null, non-blank descriptions.
//
{
UINT uCount = 0;
mfn_Lock();
map< ENGINEHANDLE, CEngineOperation* >::iterator iter;
for( iter = m_mapIdToOperation.begin();
iter != m_mapIdToOperation.end();
++iter)
{
CEngineOperation *pOperation = ((*iter).second);
if (pOperation != NULL)
{
LPCWSTR szDescr = pOperation->bstrDescription;
//
// Only add operations with non-null, non-blank descriptions.
// We don't list "hidden" operations -- specifically
// the transient operation created in the background work item
// to make sure that the app doesn't go away while in the work item.
//
if (szDescr != NULL && *szDescr!=0)
{
logOperations.Log(
IDS_LOG_PENDING_OPERATION,
szDescr
);
uCount++;
}
}
}
mfn_Unlock();
return uCount;
}
VOID
CNlbEngine::UpdateInterfaceWorkItem(ENGINEHANDLE ehIF)
{
ENGINEHANDLE ehOperation = NULL;
ENGINEHANDLE ehClusterId = NULL;
CEngineOperation *pExistingOp = NULL;
CInterfaceSpec *pISpec = NULL;
ENGINEHANDLE ehHostToTryRemove = NULL;
NLB_EXTENDED_CLUSTER_CONFIGURATION
*pNewCfg = NULL;
_bstr_t bstrHostName;
_bstr_t bstrClusterIp;
mfn_Lock();
pISpec = m_mapIdToInterfaceSpec[ehIF]; // map
if (pISpec == NULL)
{
ASSERT(FALSE);
TRACE_CRIT("%!FUNC! Invalid ehIF 0x%lx", ehIF);
goto end_unlock;
}
ehOperation = pISpec->m_ehPendingOperation;
if (ehOperation == NULL)
{
ASSERT(FALSE);
TRACE_CRIT("%!FUNC! ehIF 0x%lx: No pending operation", ehIF);
goto end_unlock;
}
pExistingOp = mfn_GetOperationLk(ehOperation);
if (pExistingOp == NULL)
{
ASSERT(FALSE);
TRACE_CRIT("%!FUNC! ehIF 0x%lx: Invalid ehOp 0x%lx", ehIF, ehOperation);
goto end_unlock;
}
pNewCfg = (NLB_EXTENDED_CLUSTER_CONFIGURATION *) pExistingOp->pvContext;
if (pNewCfg == NULL)
{
ASSERT(FALSE);
TRACE_CRIT("%!FUNC! ehIF 0x%lx: ehOp 0x%lx: NULL pvContext",
ehIF, ehOperation);
goto end_unlock;
}
bstrClusterIp = pNewCfg->NlbParams.cl_ip_addr;
ehClusterId = pISpec->m_ehCluster;
mfn_GetInterfaceHostNameLk(
ehIF,
REF bstrHostName
);
mfn_Unlock();
//
// Actually do the update...
//
// BOOL fClusterPropertiesUpdated = TRUE; // so we won't update...
mfn_ReallyUpdateInterface(
ehIF,
*pNewCfg
// REF fClusterPropertiesUpdated
);
m_pCallbacks->Log(
IUICallbacks::LOG_INFORMATIONAL,
(LPCWSTR) bstrClusterIp,
(LPCWSTR) bstrHostName,
IDS_LOG_END_HOST_UPDATE
);
mfn_Lock();
//
// We'll stop the operation, assumed to be started in this function.
//
mfn_StopInterfaceOperationLk(ehIF);
//
// The operation could have added or removed the IF to a cluster,
// so we need to re-obtain the cluster ID (could be NULL).
//
{
pISpec = m_mapIdToInterfaceSpec[ehIF]; // map
if (pISpec != NULL)
{
ehClusterId = pISpec->m_ehCluster;
//
// If NLB is unbound we need to check if the host can be
// completely removed from nlbmgr (if no interfaces on
// the host are managed by this instance of nlbmgr).
//
if (!pISpec->m_NlbCfg.IsNlbBound())
{
ehHostToTryRemove = pISpec->m_ehHostId;
}
}
}
mfn_Unlock();
//
// Notify the UI about the status change (operation complete)
//
m_pCallbacks->HandleEngineEvent(
IUICallbacks::OBJ_INTERFACE,
ehClusterId,
ehIF,
IUICallbacks::EVT_STATUS_CHANGE
);
mfn_Lock();
//fall through
end_unlock:
//
// pNewCfg (if non null) was allocated before this function was
// called, and saved as the pOperation->pvContext. It's our responsibility
// to delete it here.
//
delete pNewCfg;
if (ehHostToTryRemove != NULL)
{
mfn_DeleteHostIfNotManagedLk(ehHostToTryRemove);
}
mfn_Unlock();
//
// This must be the LAST function call, because the engine may get
// wiped out after this.
//
InterlockedDecrement(&m_WorkItemCount);
}
NLBERROR
CNlbEngine::CanStartInterfaceOperation(
IN ENGINEHANDLE ehIF,
IN BOOL &fCanStart
)
{
NLBERROR nerr = NLBERR_INTERNAL_ERROR;
CInterfaceSpec *pISpec = NULL;
fCanStart = FALSE;
mfn_Lock();
pISpec = m_mapIdToInterfaceSpec[ehIF]; // map
if (pISpec == NULL)
{
nerr = NLBERR_NOT_FOUND;
goto end_unlock;
}
//
// If there is an operation ongoing, we can't start
//
if (pISpec->m_ehPendingOperation != NULL)
{
fCanStart = FALSE;
nerr = NLBERR_OK;
goto end_unlock;
}
//
// If the interface is part of a cluster, and there is a pending operation
// on that cluster, we can't start
//
if (pISpec->m_ehCluster != NULL)
{
CEngineCluster *pECluster = m_mapIdToEngineCluster[pISpec->m_ehCluster]; // map
if (pECluster == NULL)
{
//
// Invalid cluster!
//
TRACE_CRIT("%!FUNC! ehIF:0x%lx; Invalid ehCluster 0x%lx",
ehIF, pISpec->m_ehCluster);
goto end_unlock;
}
if (pECluster->m_cSpec.m_ehPendingOperation != NULL)
{
//
// A cluster-wide operation is pending -- so can't start.
//
fCanStart = FALSE;
nerr = NLBERR_OK;
goto end_unlock;
}
}
//
// Looks like we CAN start at this time (although the moment we
// exit the lock the situation may change).
//
fCanStart = TRUE;
nerr = NLBERR_OK;
end_unlock:
mfn_Unlock();
return nerr;
}
NLBERROR
CNlbEngine::mfn_ClusterOrInterfaceOperationsPendingLk(
IN CEngineCluster *pECluster,
OUT BOOL &fCanStart
)
{
NLBERROR nerr = NLBERR_INTERNAL_ERROR;
fCanStart = FALSE;
//
// If there is an operation ongoing, we can't start
//
if (pECluster->m_cSpec.m_ehPendingOperation != NULL)
{
fCanStart = FALSE;
nerr = NLBERR_OK;
goto end;
}
//
// Lets look at all of our interfaces, checking if there are pending
// operations on each of the interfaces.
//
{
BOOL fOperationPending = FALSE;
vector<ENGINEHANDLE> &InterfaceList =
pECluster->m_cSpec.m_ehInterfaceIdList; // vector reference
for( int i = 0; i < InterfaceList.size(); ++i )
{
ENGINEHANDLE ehIF = InterfaceList[i];
CInterfaceSpec *pISpec = m_mapIdToInterfaceSpec[ehIF]; // map
if (pISpec == NULL)
{
//
// Hmm... invalid interface handle? We'll ignore this one.
//
continue;
}
if (pISpec->m_ehPendingOperation != NULL)
{
fOperationPending = TRUE;
break;
}
}
if (fOperationPending)
{
fCanStart = FALSE;
nerr = NLBERR_OK;
goto end;
}
}
//
// Looks like we CAN start at this time (although the moment we
// exit the lock the situation may change).
//
fCanStart = TRUE;
nerr = NLBERR_OK;
end:
return nerr;
}
NLBERROR
CNlbEngine::CanStartClusterOperation(
IN ENGINEHANDLE ehCluster,
IN BOOL &fCanStart
)
{
CEngineCluster *pECluster = NULL;
NLBERROR nerr = NLBERR_INTERNAL_ERROR;
fCanStart = FALSE;
mfn_Lock();
pECluster = m_mapIdToEngineCluster[ehCluster]; // map
if (pECluster == NULL)
{
nerr = NLBERR_NOT_FOUND;
}
else
{
nerr = mfn_ClusterOrInterfaceOperationsPendingLk(pECluster, REF fCanStart);
}
mfn_Unlock();
return nerr;
}
DWORD
WINAPI
UpdateInterfaceWorkItemRoutine(
LPVOID lpParameter // thread data
)
{
gEngine.UpdateInterfaceWorkItem((ENGINEHANDLE) (UINT_PTR) lpParameter);
return 0;
}
DWORD
WINAPI
AddClusterMembersWorkItemRoutine(
LPVOID lpParameter // thread data
)
{
gEngine.AddOtherClusterMembersWorkItem(
(ENGINEHANDLE) (UINT_PTR) lpParameter
);
return 0;
}
BOOL
CNlbEngine::mfn_UpdateClusterProps(
ENGINEHANDLE ehCluster,
ENGINEHANDLE ehInterface
)
/*
Update the specified cluster properties to be the specified interface
properties PROVIDED:
1. The IF is a member of the cluster
2. The configuration shows that it is bound to the same cluster IP.
Return true iff the cluter props were actually updated.
*/
{
BOOL fClusterUpdated = FALSE;
CEngineCluster *pECluster = NULL;
CInterfaceSpec *pISpec = NULL;
mfn_Lock();
pECluster = m_mapIdToEngineCluster[ehCluster]; // map
pISpec = m_mapIdToInterfaceSpec[ehInterface]; // map
if (pECluster == NULL || pISpec == NULL)
{
goto end_unlock;
}
if (pISpec->m_ehCluster != ehCluster)
{
goto end_unlock;
}
if ( pISpec->m_NlbCfg.IsValidNlbConfig()
&& !_wcsicmp(
pECluster->m_cSpec.m_ClusterNlbCfg.NlbParams.cl_ip_addr,
pISpec->m_NlbCfg.NlbParams.cl_ip_addr
) )
{
pECluster->m_cSpec.m_ehDefaultInterface = ehInterface;
pECluster->m_cSpec.m_ClusterNlbCfg.Update(&pISpec->m_NlbCfg);
TRACE_INFO(L"Updating ehCluster 0x%lx spec -- using ehIF 0x%lx",
ehCluster, ehInterface);
//
// Remove the dedicated IP address from cluster's version of
// the NlbParams and the IP address list.
//
remove_dedicated_ip_from_nlbcfg(REF pECluster->m_cSpec.m_ClusterNlbCfg);
//
// Since we've just read all the config (including remote control hash
// value) we can now clear the pECluster->m_cSpec.m_fNewRctPassword
// flag.
//
TRACE_VERB(L"Clearing pECluster->m_cSpec.m_fNewRctPassword");
pECluster->m_cSpec.m_fNewRctPassword = FALSE;
fClusterUpdated = TRUE;
}
end_unlock:
mfn_Unlock();
if (fClusterUpdated)
{
m_pCallbacks->HandleEngineEvent(
IUICallbacks::OBJ_CLUSTER,
ehCluster,
ehCluster,
IUICallbacks::EVT_STATUS_CHANGE
);
}
return fClusterUpdated;
}
void
CNlbEngine::CancelAllPendingOperations(
BOOL fBlock
)
{
//
// if (fBlock), we wait until BOTH m_WorkItemCount and
// the number of operations goes to zero.
//
// An Operations are created BEFORE the workitemcount is incremented,
// so we don't have to deal with the transient possibility that
// both are zero (and we get out) but soon after the count goes to non
// zero
//
map< ENGINEHANDLE, CEngineOperation* >::iterator iter;
if (!fBlock)
{
mfn_Lock();
for( iter = m_mapIdToOperation.begin();
iter != m_mapIdToOperation.end();
++iter)
{
CEngineOperation *pOperation = ((*iter).second);
if (pOperation != NULL)
{
pOperation->fCanceled = TRUE;
}
}
mfn_Unlock();
}
else
{
//
// If we're asked to block, we assume that this is while
// we're prepairing to deinitialize, at which point we are
// guaranteed that no new operations can be added.
// It is possible that new work items can be added,
// however a work item is created in the context of
// an operation, so once the operation count goes
// to zero, no new work items will be created.
//
ASSERT(m_fPrepareToDeinitialize);
BOOL fPending = FALSE;
do
{
fPending = FALSE;
mfn_Lock();
for( iter = m_mapIdToOperation.begin();
iter != m_mapIdToOperation.end();
++iter)
{
CEngineOperation *pOperation = ((*iter).second);
if (pOperation != NULL)
{
pOperation->fCanceled = TRUE;
fPending = TRUE;
}
}
//
// The additional check below must come AFTER the previous
// loop. If we had this check before the loop, it could be that
// there are no work item's before we check the loop, but the
// instant we check the loop for operations, the work item count
// goes positive but when we actually check the loop here are zero
// operations. Actually that's not possible because we have
// mfn_Lock held, so never mind.
//
//
fPending |= (m_WorkItemCount > 0);
mfn_Unlock();
if (fPending)
{
ProcessMsgQueue();
Sleep(50);
}
} while (fPending);
}
return;
}
NLBERROR
CNlbEngine::mfn_WaitForInterfaceOperationCompletions(
IN ENGINEHANDLE ehCluster
)
{
NLBERROR nerr = NLBERR_INTERNAL_ERROR;
CEngineCluster *pECluster = NULL;
CClusterSpec *pCSpec = NULL;
ENGINEHANDLE ehOperation = NULL;
BOOL fOperationsPending = FALSE;
mfn_Lock();
pECluster = m_mapIdToEngineCluster[ehCluster]; // map
if (pECluster == NULL)
{
TRACE_CRIT("%!FUNC!: Invalid ehC 0x%lx", ehCluster);
goto end_unlock;
}
pCSpec = &pECluster->m_cSpec;
ehOperation = pCSpec->m_ehPendingOperation;
if (ehOperation == NULL)
{
//
// We expect that this function is only called when there is a
// pending cluster-wide operation.
//
TRACE_CRIT("%!FUNC! ehC 0x%lx Failing because no cluster operation pending", ehCluster);
goto end_unlock;
}
//
// Now in a loop, enumerate the interfaces in the cluster,
// cheking for pending operations.
//
TRACE_INFO(L"%!FUNC! Begin wait for cluster ehC 0x%lx operations to complete", ehCluster);
do
{
fOperationsPending = FALSE;
vector<ENGINEHANDLE> &InterfaceList =
pECluster->m_cSpec.m_ehInterfaceIdList; // vector reference
for( int i = 0; i < InterfaceList.size(); ++i )
{
ENGINEHANDLE ehIF = InterfaceList[i];
CInterfaceSpec *pISpec = m_mapIdToInterfaceSpec[ehIF]; // map
if (pISpec == NULL)
{
//
// Hmm... invalid interface handle? We'll ignore this one.
//
continue;
}
if (pISpec->m_ehPendingOperation != NULL)
{
fOperationsPending = TRUE;
break;
}
}
if (fOperationsPending)
{
mfn_Unlock();
for (UINT u=0;u<50;u++)
{
ProcessMsgQueue();
Sleep(100);
}
mfn_Lock();
}
}
while (fOperationsPending);
TRACE_INFO(L"%!FUNC! End wait for cluster ehC 0x%lx operations to complete.", ehCluster);
nerr = NLBERR_OK;
end_unlock:
mfn_Unlock();
return nerr;
}
//
// Verifies that all interfaces and the cluster have the same cluster mode.
//
// Will fail if any interface is marked misconfigured or is
// not bound to NLB.
//
// On returning success, fSameMode is set to TRUE iff all IFs and the
// cluster have the same mode.
//
NLBERROR
CNlbEngine::mfn_VerifySameModeLk(
IN ENGINEHANDLE ehCluster,
OUT BOOL &fSameMode
)
{
NLBERROR nerr = NLBERR_INTERNAL_ERROR;
CEngineCluster *pECluster = NULL;
CClusterSpec *pCSpec = NULL;
fSameMode = FALSE;
mfn_Lock();
pECluster = m_mapIdToEngineCluster[ehCluster]; // map
if (pECluster == NULL)
{
TRACE_CRIT("%!FUNC!: Invalid ehC 0x%lx", ehCluster);
goto end_unlock;
}
pCSpec = &pECluster->m_cSpec;
//
// Let's check for mode changes...
//
{
BOOL fConfigError = FALSE;
NLB_EXTENDED_CLUSTER_CONFIGURATION::TRAFFIC_MODE tmC;
tmC = pCSpec->m_ClusterNlbCfg.GetTrafficMode();
vector<ENGINEHANDLE> &InterfaceList =
pECluster->m_cSpec.m_ehInterfaceIdList; // vector reference
fSameMode = TRUE;
for( int i = 0; i < InterfaceList.size(); ++i )
{
ENGINEHANDLE ehIF = InterfaceList[i];
CInterfaceSpec *pISpec = m_mapIdToInterfaceSpec[ehIF]; // map
if (pISpec == NULL)
{
//
// Hmm... invalid interface handle? We'll ignore this one.
//
continue;
}
//
// Note: we can't check for pISpec->m_fMisconfigured because
// the cluster may be marked misconfig because it doesn't
// match the cluster parameters, which can happen on
// mode changes (the ip addresses could be missing in the
// interface).
//
if (!pISpec->m_NlbCfg.IsValidNlbConfig())
{
fConfigError = TRUE;
break;
}
{
NLB_EXTENDED_CLUSTER_CONFIGURATION::TRAFFIC_MODE tmI;
tmI = pISpec->m_NlbCfg.GetTrafficMode();
if (tmI != tmC)
{
//
// Mode change!
//
fSameMode = FALSE;
break;
}
}
}
if (fConfigError)
{
nerr = NLBERR_INVALID_CLUSTER_SPECIFICATION;
fSameMode = FALSE;
}
else
{
nerr = NLBERR_OK;
}
}
end_unlock:
mfn_Unlock();
return nerr;
}
VOID
CNlbEngine::AddOtherClusterMembers(
IN ENGINEHANDLE ehInterface,
IN BOOL fSync
)
{
BOOL fStopOperationOnExit = FALSE;
ENGINEHANDLE ehCluster = NULL;
NLBERROR nerr = NLBERR_INTERNAL_ERROR;
//
// Get cluster ID, and attempt to start a cluster-wide operation
// on it
//
{
mfn_Lock();
CInterfaceSpec *pISpec = NULL;
CClusterSpec *pCSpec = NULL;
pISpec = m_mapIdToInterfaceSpec[ehInterface]; // map
if (pISpec != NULL)
{
ehCluster = pISpec->m_ehCluster;
if (ehCluster != NULL)
{
CEngineCluster *pECluster = NULL;
pECluster = m_mapIdToEngineCluster[ehCluster]; // map
pCSpec = &pECluster->m_cSpec;
}
}
if (pCSpec == NULL)
{
TRACE_CRIT(L"%!FUNC! Could not get interface or cluster associated with ehIF 0x%08lx", ehInterface);
goto end_unlock;
}
//
// Attempt to start the update operation -- will fail if there is
// already an operation started on this cluster.
//
{
ENGINEHANDLE ExistingOp= NULL;
CLocalLogger logDescription;
logDescription.Log(
IDS_LOG_ADD_CLUSTER_MEMBERS_OPERATION_DESCRIPTION,
pCSpec->m_ClusterNlbCfg.NlbParams.cl_ip_addr
);
nerr = mfn_StartClusterOperationLk(
ehCluster,
NULL, // pvCtxt
logDescription.GetStringSafe(),
&ExistingOp
);
if (NLBFAILED(nerr))
{
//
// TODO: Log the fact that we couldn't do the update because
// of existing activity.
//
goto end_unlock;
}
else
{
//
// At this point we're cleared to do a cluster-wide operation.
//
fStopOperationOnExit = TRUE;
InterlockedIncrement(&m_WorkItemCount);
}
}
mfn_Unlock();
m_pCallbacks->HandleEngineEvent(
IUICallbacks::OBJ_CLUSTER,
ehCluster,
ehCluster,
IUICallbacks::EVT_STATUS_CHANGE
);
}
if (fSync)
{
this->AddOtherClusterMembersWorkItem(
ehInterface
);
fStopOperationOnExit = FALSE; // it'll be stopped by the above func.
}
else
{
BOOL fRet;
//
// We'll perform the operation in the background...
//
fRet = QueueUserWorkItem(
AddClusterMembersWorkItemRoutine,
(PVOID) (UINT_PTR) ehInterface,
WT_EXECUTELONGFUNCTION
);
if (fRet)
{
fStopOperationOnExit = FALSE; // it'll be stopped in the background
}
else
{
TRACE_CRIT(L"%!FUNC! Could not queue work item");
//
// We don't bother to log this evidently low-resource situation.
//
}
}
mfn_Lock();
end_unlock:
if (fStopOperationOnExit)
{
mfn_StopClusterOperationLk(ehCluster);
}
mfn_Unlock();
if (fStopOperationOnExit)
{
m_pCallbacks->HandleEngineEvent(
IUICallbacks::OBJ_CLUSTER,
ehCluster,
ehCluster,
IUICallbacks::EVT_STATUS_CHANGE
);
InterlockedDecrement(&m_WorkItemCount);
}
return;
}
VOID
CNlbEngine::AddOtherClusterMembersWorkItem(
IN ENGINEHANDLE ehInterface
)
{
ENGINEHANDLE ehCluster = NULL;
ENGINEHANDLE ehHost = NULL;
_bstr_t bstrUserName;
_bstr_t bstrPassword;
_bstr_t bstrConnectionString;
_bstr_t bstrNicGuid;
_bstr_t bstrClusterIp;
DWORD NumMembers = 0;
NLB_CLUSTER_MEMBER_INFO *pMembers = NULL;
{
mfn_Lock();
CInterfaceSpec *pISpec = NULL;
CClusterSpec *pCSpec = NULL;
CHostSpec *pHSpec = NULL;
pISpec = m_mapIdToInterfaceSpec[ehInterface]; // map
if (pISpec != NULL)
{
ehCluster = pISpec->m_ehCluster;
if (ehCluster != NULL)
{
CEngineCluster *pECluster = NULL;
pECluster = m_mapIdToEngineCluster[ehCluster]; // map
pCSpec = &pECluster->m_cSpec;
if (pCSpec->m_ClusterNlbCfg.IsValidNlbConfig())
{
bstrClusterIp = pCSpec->m_ClusterNlbCfg.NlbParams.cl_ip_addr;
}
}
}
if (pCSpec == NULL)
{
TRACE_CRIT(L"%!FUNC! Could not get interface or cluster associated with ehIF 0x%08lx", ehInterface);
goto end_unlock;
}
ehHost = pISpec->m_ehHostId;
//
// Get the host ID
//
if (ehHost != NULL)
{
pHSpec = m_mapIdToHostSpec[ehHost]; // map
}
if (pHSpec == NULL)
{
TRACE_CRIT("%!FUNC! Could not get ptr to host spec. Bailing.");
goto end_unlock;
}
//
// Save copies of these in local bstrs before we unlock...
//
bstrUserName = pHSpec->m_UserName;
bstrPassword = pHSpec->m_Password;
bstrConnectionString = pHSpec->m_ConnectionString;
bstrNicGuid = pISpec->m_Guid;
if ((LPCWSTR)bstrNicGuid == (LPCWSTR)NULL)
{
// probably a low-memory situation...
goto end_unlock;
}
mfn_Unlock();
}
//
// Attempt to get the list of other cluster members from the
// interface
//
{
WMI_CONNECTION_INFO ConnInfo;
WBEMSTATUS wStat;
LPCWSTR szNicGuid = bstrNicGuid;
ZeroMemory(&ConnInfo, sizeof(ConnInfo));
ConnInfo.szMachine = (LPCWSTR) bstrConnectionString;
ConnInfo.szUserName = (LPCWSTR) bstrUserName;
ConnInfo.szPassword = (LPCWSTR) bstrPassword;
wStat = NlbHostGetClusterMembers(
&ConnInfo,
szNicGuid,
&NumMembers,
&pMembers // free using delete[]
);
if (FAILED(wStat))
{
NumMembers = 0;
pMembers = NULL;
//
// TODO: Log error
//
mfn_Lock();
goto end_unlock;
}
}
//
// For each member, attempt to connect to that host and add the specific
// cluster.
//
{
WBEMSTATUS wStat;
LPCWSTR szNicGuid = bstrNicGuid;
#if 0
CLocalLogger logger;
for (UINT u=0; u<NumMembers; u++)
{
NLB_CLUSTER_MEMBER_INFO *pMember = pMembers+u;
logger.Log(
IDS_DBG_LOG_ADD_CLUSTER_MEMBER,
pMember->HostId,
pMember->DedicatedIpAddress,
pMember->HostName
);
}
::MessageBox(
NULL,
logger.GetStringSafe(), // contents
L"DEBUGINFO: GOING TO ADD THESE HOSTS...",
MB_ICONINFORMATION | MB_OK
);
#endif // 0
for (UINT u=0; u<NumMembers; u++)
{
NLB_CLUSTER_MEMBER_INFO *pMember = pMembers+u;
WMI_CONNECTION_INFO ConnInfo;
ZeroMemory(&ConnInfo, sizeof(ConnInfo));
ConnInfo.szUserName = (LPCWSTR) bstrUserName;
ConnInfo.szPassword = (LPCWSTR) bstrPassword;
if (*pMember->HostName != 0)
{
ConnInfo.szMachine = pMember->HostName;
} else if (*pMember->DedicatedIpAddress != 0
&& (_wcsicmp(pMember->DedicatedIpAddress,
L"0.0.0.0")))
{
// non-blank dedicated IP -- let's try that...
ConnInfo.szMachine = pMember->DedicatedIpAddress;
}
if (ConnInfo.szMachine == NULL)
{
// Can't connect to this IP
// TODO: inform user in some way
continue;
}
//
// Now actually attempt to add the host
//
this->LoadHost(&ConnInfo, (LPCWSTR) bstrClusterIp);
}
}
mfn_Lock();
end_unlock:
if (ehCluster != NULL)
{
mfn_StopClusterOperationLk(ehCluster);
}
mfn_Unlock();
m_pCallbacks->HandleEngineEvent(
IUICallbacks::OBJ_CLUSTER,
ehCluster,
ehCluster,
IUICallbacks::EVT_STATUS_CHANGE
);
delete [] pMembers; // may be NULL
InterlockedDecrement(&m_WorkItemCount); // Don't touch this after this,
// because it may no longer be valid
return;
}
NLBERROR
CNlbEngine::LoadHost(
IN PWMI_CONNECTION_INFO pConnInfo,
IN LPCWSTR szClusterIp OPTIONAL
)
{
ENGINEHANDLE ehHostId;
_bstr_t bstrConnectError;
CHostSpec hSpec;
NLBERROR err = NLBERR_INTERNAL_ERROR;
TRACE_INFO(L"-> %!FUNC! Host name : %ls", (LPCWSTR)(pConnInfo->szMachine));
if (m_fPrepareToDeinitialize)
{
err = NLBERR_CANCELLED;
goto end;
}
m_pCallbacks->Log(IUICallbacks::LOG_INFORMATIONAL, NULL, NULL,
IDS_LOADFILE_LOOKING_FOR_CLUSTERS, pConnInfo->szMachine);
ProcessMsgQueue();
err = this->ConnectToHost(
pConnInfo,
FALSE, // FALSE == don't overwrite connection info if
// already present
REF ehHostId,
REF bstrConnectError
);
if (err != NLBERR_OK)
{
m_pCallbacks->Log(IUICallbacks::LOG_ERROR, NULL, NULL, IDS_CONNECT_TO_HOST_FAILED, (LPCWSTR)bstrConnectError, (LPCWSTR)(pConnInfo->szMachine));
TRACE_CRIT(L"<- %!FUNC! ConnectToHost returned error (string : %ls, retval : 0x%x)",(LPCWSTR)bstrConnectError, err);
goto end;
}
if ((err = this->GetHostSpec(ehHostId, REF hSpec)) != NLBERR_OK)
{
m_pCallbacks->Log(IUICallbacks::LOG_ERROR, NULL, NULL, IDS_CRITICAL_ERROR_HOST, (LPCWSTR)(pConnInfo->szMachine));
TRACE_CRIT(L"<- %!FUNC! GetHostSpec returned error : 0x%x", err);
goto end;
}
//
// Extract list of interfaces
//
for( int i = 0; i < hSpec.m_ehInterfaceIdList.size(); ++i )
{
ENGINEHANDLE ehIID = hSpec.m_ehInterfaceIdList[i];
CInterfaceSpec iSpec;
ProcessMsgQueue();
if ((err = this->GetInterfaceSpec(ehIID, REF iSpec)) != NLBERR_OK)
{
m_pCallbacks->Log(IUICallbacks::LOG_ERROR, NULL, NULL, IDS_CRITICAL_ERROR_HOST, (LPCWSTR)(pConnInfo->szMachine));
TRACE_CRIT(L"%!FUNC! GetInterfaceSpec returned error : 0x%x", err);
continue;
}
//
// Check if interface has NLB bound to it
// AND it is NOT part of a cluster that nlb manager is already managing
// AND (if szClusterIp NON-NULL, it matches the specified cluster IP
//
//
if (iSpec.m_NlbCfg.IsNlbBound() && (iSpec.m_ehCluster == NULL))
{
LPCWSTR szThisClusterIp;
ENGINEHANDLE ehCluster;
BOOL fIsNew;
szThisClusterIp = iSpec.m_NlbCfg.NlbParams.cl_ip_addr;
if ( szClusterIp != NULL
&& _wcsicmp(szClusterIp, szThisClusterIp))
{
// different cluster ip
TRACE_INFO(L"%!FUNC! Skipping cluster with CIP %ws because it doesn't match passed-in CIP %ws",
szThisClusterIp, szClusterIp);
continue;
}
if ((err = this->LookupClusterByIP(szThisClusterIp, &(iSpec.m_NlbCfg), REF ehCluster, REF fIsNew)) != NLBERR_OK)
{
m_pCallbacks->Log(IUICallbacks::LOG_ERROR, NULL, NULL, IDS_CRITICAL_ERROR_HOST, (LPCWSTR)(pConnInfo->szMachine));
TRACE_CRIT(L"%!FUNC! LookupClusterByIP returned error : 0x%x for cluster ip : %ls", err, szThisClusterIp);
continue;
}
if (this->AddInterfaceToCluster(ehCluster, ehIID) != NLBERR_OK)
{
m_pCallbacks->Log(IUICallbacks::LOG_ERROR, NULL, NULL, IDS_CRITICAL_ERROR_HOST, (LPCWSTR)(pConnInfo->szMachine));
TRACE_CRIT(L"%!FUNC! AddInterfaceToCluster returned error : 0x%x", err);
continue;
}
/* Analyze this interface for misconfiguration */
this->AnalyzeInterface_And_LogResult(ehIID);
}
}
end:
ProcessMsgQueue();
TRACE_INFO(L"<- %!FUNC!");
return err;
}
/*
The following function analyzes the specified NLB interface for misconfiguration and logs
the result. I created this function (as opposed to adding it inline) because this code
needs to run in two cases:
1. CNLBEngine::LoadHost
2. LeftView::OnWorldConnect
--KarthicN, July 31, 2002
*/
VOID
CNlbEngine::AnalyzeInterface_And_LogResult(ENGINEHANDLE ehIID)
{
CLocalLogger logger;
NLBERROR err;
mfn_Lock();
err = this->mfn_AnalyzeInterfaceLk(ehIID, REF logger);
if (NLBFAILED(err))
{
ENGINEHANDLE ehCluster;
LPCWSTR szDetails = NULL;
UINT Size = 0;
logger.ExtractLog(szDetails, Size);
mfn_SetInterfaceMisconfigStateLk(m_mapIdToInterfaceSpec[ehIID], TRUE, szDetails);
mfn_Unlock();
//
// Log ...
//
LPCWSTR szCluster = NULL;
LPCWSTR szHostName = NULL;
LPCWSTR szInterface = NULL;
ENGINEHANDLE ehHost;
_bstr_t bstrDisplayName;
_bstr_t bstrFriendlyName;
_bstr_t bstrHostName;
_bstr_t bstrIpAddress;
err = this->GetInterfaceIdentification(
ehIID,
REF ehHost,
REF ehCluster,
REF bstrFriendlyName,
REF bstrDisplayName,
REF bstrHostName
);
if (NLBOK(err))
{
_bstr_t bstrDomainName;
_bstr_t bstrClusterDisplayName;
err = this->GetClusterIdentification(
ehCluster,
REF bstrIpAddress,
REF bstrDomainName,
REF bstrClusterDisplayName
);
if (NLBOK(err))
{
szCluster = bstrIpAddress;
}
szHostName = bstrHostName;
szInterface = bstrFriendlyName;
}
IUICallbacks::LogEntryHeader Header;
Header.szDetails = szDetails;
Header.type = IUICallbacks::LOG_ERROR;
Header.szCluster = szCluster;
Header.szHost = szHostName;
Header.szInterface = szInterface;
m_pCallbacks->LogEx(
&Header,
IDS_LOG_INTERFACE_MISCONFIGURATION
);
// Change Icon to "Banged out"
m_pCallbacks->HandleEngineEvent(
IUICallbacks::OBJ_INTERFACE,
ehCluster,
ehIID,
IUICallbacks::EVT_STATUS_CHANGE
);
}
else
{
mfn_Unlock();
}
return;
}
VOID
CNlbEngine::mfn_DeleteHostIfNotManagedLk(
ENGINEHANDLE ehHost
)
/*
Checks all the interfaces of host ehHost. If none of them are
members of any cluster, and no pending operations are existing on
them, we will delete the host and all its interfaces.
Called with lock held!
*/
{
CHostSpec *pHSpec = NULL;
BOOL fBusy = FALSE;
UINT u;
pHSpec = m_mapIdToHostSpec[ehHost]; // map
if (pHSpec == NULL) goto end;
// DummyAction(L"DeleteHostIfNotManaged");
//
// Go through the list of interfaces, seeing if ANY interface
// is part of a cluster or there are updates pending on it..
//
for(u = 0; u < pHSpec->m_ehInterfaceIdList.size(); ++u )
{
ENGINEHANDLE ehIId = pHSpec->m_ehInterfaceIdList[u];
CInterfaceSpec *pISpec = NULL;
pISpec = m_mapIdToInterfaceSpec[ehIId]; // map
if (pISpec == NULL) continue;
ASSERT(pISpec->m_ehHostId == ehHost);
if (pISpec->m_ehCluster != NULL)
{
// Found an interface still part of a cluster, bail.
fBusy = TRUE;
break;
}
//
//
//
if (pISpec->m_ehPendingOperation != NULL)
{
//
// We really don't expect this, but it COULD happen.
//
TRACE_CRIT("Ignoring eh(0x%x) because it has pending operation 0x%x even though it's not a part of a cluster.",
ehIId,
pISpec->m_ehPendingOperation
);
fBusy = TRUE;
break;
}
}
if (fBusy) goto end;
TRACE_INFO(L"Deleting all interfaces under host eh(0x%x)", ehHost);
for(u = 0; u < pHSpec->m_ehInterfaceIdList.size(); ++u )
{
ENGINEHANDLE ehIId = pHSpec->m_ehInterfaceIdList[u];
CInterfaceSpec *pISpec = NULL;
pISpec = m_mapIdToInterfaceSpec[ehIId]; // map
if (pISpec == NULL) continue;
ASSERT(pISpec->m_ehHostId == ehHost);
ASSERT(pISpec->m_ehCluster == NULL); // we checked above
ASSERT(pISpec->m_ehPendingOperation == NULL); // we checked above
//
// Kill this interface!
//
TRACE_INFO(L"Deleting Interface eh=0x%x pISpec=0x%p",
ehIId, pISpec);
m_mapIdToInterfaceSpec.erase(ehIId);
delete pISpec;
}
//
// Erase the list of intefaces for this host...
//
pHSpec->m_ehInterfaceIdList.clear();
#if 1
//
// Now delete the host
//
TRACE_INFO(L"Deleting Host eh=0x%x pHSpec=0x%p",
ehHost, pHSpec);
m_mapIdToHostSpec.erase(ehHost);
delete pHSpec;
#endif // 0
end:
return;
}
VOID
CNlbEngine::PurgeUnmanagedHosts(void)
{
vector <ENGINEHANDLE> PurgeHostList;
TRACE_INFO(L"-> %!FUNC!");
mfn_Lock();
map< ENGINEHANDLE, CHostSpec* >::iterator iter;
for( iter = m_mapIdToHostSpec.begin();
iter != m_mapIdToHostSpec.end();
++iter)
{
CHostSpec *pHSpec = (CHostSpec *) ((*iter).second);
ENGINEHANDLE ehHost = (ENGINEHANDLE) ((*iter).first);
if (pHSpec != NULL)
{
if (!mfn_HostHasManagedClustersLk(pHSpec))
{
//
// No managed clusters on this host -- a candidate
// for deleting.
//
PurgeHostList.push_back(ehHost);
}
}
}
//
// Now try to delete the hosts...
// We do this out of the enumeration above because we want to
// avoid modifying the map while we're iterating through it.
//
for(int i = 0; i < PurgeHostList.size(); ++i )
{
ENGINEHANDLE ehHost = PurgeHostList[i];
if (ehHost != NULL)
{
mfn_DeleteHostIfNotManagedLk(ehHost);
}
}
mfn_Unlock();
TRACE_INFO(L"<- %!FUNC!");
}
NLBERROR
CNlbEngine::mfn_CheckHost(
IN PWMI_CONNECTION_INFO pConnInfo,
IN ENGINEHANDLE ehHost // OPTIONAL
)
/*
TODO -- this function shares code with ConnectToHost -- get rid of
the duplicated code somehow.
*/
{
NLBERROR nerr = NLBERR_INTERNAL_ERROR;
LPWSTR szWmiMachineName = NULL;
LPWSTR szWmiMachineGuid = NULL;
WBEMSTATUS wStatus;
ULONG uIpAddress;
BOOL fNlbMgrProviderInstalled = FALSE;
_bstr_t bstrError;
TRACE_INFO(L"-> %!FUNC!(%ws)", pConnInfo->szMachine);
wStatus = NlbHostPing(pConnInfo->szMachine, 2000, &uIpAddress);
if (FAILED(wStatus))
{
nerr = NLBERR_PING_TIMEOUT; // todo more specific error.
bstrError = GETRESOURCEIDSTRING(IDS_PING_FAILED);
}
else
{
wStatus = NlbHostGetMachineIdentification(
pConnInfo,
&szWmiMachineName,
&szWmiMachineGuid,
&fNlbMgrProviderInstalled
);
if (FAILED(wStatus))
{
GetErrorCodeText(wStatus, bstrError);
if (wStatus == E_ACCESSDENIED)
{
nerr = NLBERR_ACCESS_DENIED;
}
else
{
// TODO: map proper errors.
nerr = NLBERR_NOT_FOUND;
}
TRACE_CRIT(L"Connecting to %ws returns error %ws",
pConnInfo->szMachine, (LPCWSTR) bstrError);
szWmiMachineName = NULL;
szWmiMachineGuid = NULL;
}
else
{
nerr = NLBERR_OK;
}
}
delete szWmiMachineName;
delete szWmiMachineGuid;
if (ehHost!=NULL)
{
CHostSpec *pHSpec = NULL;
mfn_Lock();
pHSpec = m_mapIdToHostSpec[ehHost]; // map
if (pHSpec != NULL)
{
if (NLBOK(nerr) || nerr == NLBERR_ACCESS_DENIED)
{
pHSpec->m_fUnreachable = FALSE;
}
else
{
pHSpec->m_fUnreachable = TRUE;
}
}
else
{
}
mfn_Unlock();
//
// Update the status of the specified host...
//
mfn_NotifyHostInterfacesChange(ehHost);
//
// Log error
//
if (NLBFAILED(nerr))
{
m_pCallbacks->Log(
IUICallbacks::LOG_ERROR,
NULL,
(LPCWSTR)(pConnInfo->szMachine),
IDS_CONNECT_TO_HOST_FAILED,
(LPCWSTR)bstrError,
(LPCWSTR)(pConnInfo->szMachine)
);
TRACE_CRIT(L"<- %!FUNC! returning error (string : %ls, retval : 0x%x)",
(LPCWSTR)bstrError, nerr);
}
}
return nerr;
}
VOID
CNlbEngine::mfn_UnlinkHostFromClusters(
IN ENGINEHANDLE ehHost
)
{
CHostSpec *pHSpec = NULL;
BOOL fBusy = FALSE;
UINT u;
vector <ENGINEHANDLE> UnlinkInterfaceList;
mfn_Lock();
pHSpec = m_mapIdToHostSpec[ehHost]; // map
if (pHSpec == NULL) goto end;
//
// Go through the list of interfaces, adding any interfaces
// that are part of a cluster to a temporary list.
//
for(u = 0; u < pHSpec->m_ehInterfaceIdList.size(); ++u )
{
ENGINEHANDLE ehIId = pHSpec->m_ehInterfaceIdList[u];
CInterfaceSpec *pISpec = NULL;
pISpec = m_mapIdToInterfaceSpec[ehIId]; // map
if (pISpec == NULL) continue;
ASSERT(pISpec->m_ehHostId == ehHost);
if (pISpec->m_ehCluster != NULL)
{
//
// Add this to the list of interfaces we're going to unlink
// from its cluster.
//
UnlinkInterfaceList.push_back(ehIId);
}
}
pHSpec = NULL;
TRACE_INFO(L"Unlinking all interfaces under host eh(0x%x)", ehHost);
for(u = 0; u < UnlinkInterfaceList.size(); ++u )
{
ENGINEHANDLE ehIId = UnlinkInterfaceList[u];
CInterfaceSpec *pISpec = NULL;
ENGINEHANDLE ehCluster = NULL;
pISpec = m_mapIdToInterfaceSpec[ehIId]; // map
if (pISpec == NULL) continue;
ASSERT(pISpec->m_ehHostId == ehHost);
ehCluster = pISpec->m_ehCluster;
if (ehCluster != NULL)
{
mfn_Unlock();
(VOID) CNlbEngine::RemoveInterfaceFromCluster(ehCluster, ehIId);
mfn_Lock();
}
}
end:
mfn_Unlock();
return;
}
VOID
CNlbEngine::UnmanageHost(ENGINEHANDLE ehHost)
{
mfn_UnlinkHostFromClusters(ehHost);
mfn_Lock();
mfn_DeleteHostIfNotManagedLk(ehHost);
mfn_Unlock();
}