// Copyright (c)  Microsoft.  All rights reserved.
//
// This is unpublished source code of Microsoft.
// The copyright notice above does not evidence any
// actual or intended publication of such source code.

// OneLiner  :  Implementation of MNLBMachine
// DevUnit   :  wlbstest
// Author    :  Murtaza Hakim

// History:
// --------
// 
// Revised by : mhakim
// Date       : 02-09-01
// Reason     : clustersetting class no longer has igmpjoininterval
//              property.
//
// Revised by : mhakim
// Date       : 02-14-01
// Reason     : The order in which win2k and whistler settings
//              is done depends on previous state of cluster.

// include files
#include "MNLBMachine.h"
#include "MNLBExe.h"

#include "MTrace.h"
#include "MNicInfo.h"
#include "WTokens.h"

#include <iostream>

using namespace std;

// constructor
//
MNLBMachine::MNLBMachine( const _bstr_t&    ip,
                          const _bstr_t&    clusterIP )
        : mIP( ip ),
          mClusterIP( clusterIP ),
          p_machine( 0 )
{
    connectToMachine();
    TRACE(MTrace::INFO, L"mnlbmachine constructor\n");
}

// constructor for local machine
//
MNLBMachine::MNLBMachine( const _bstr_t&    clusterIP )
        : mIP( L"self" ),
          mClusterIP( clusterIP ), 
          p_machine( 0 )
{
    connectToMachine();
    TRACE(MTrace::INFO, L"mnlbmachine constructor\n");
}


// default constructor
// 
// note that default constructor is purposely left undefined.  
// NO one should be using it.  It is declared just for vector class usage.


// copy constructor
//
MNLBMachine::MNLBMachine(const MNLBMachine& objToCopy )
        : mIP( objToCopy.mIP ),
          mClusterIP( objToCopy.mClusterIP ),
          p_machine( auto_ptr<MWmiObject>( new MWmiObject( *objToCopy.p_machine ) ) )
{
    TRACE(MTrace::INFO, L"mnlbsmachine copy constructor\n");
}


// assignment operator
//
MNLBMachine&
MNLBMachine::operator=( const MNLBMachine& rhs )
{
    mIP = rhs.mIP;
    mClusterIP = rhs.mClusterIP;
    p_machine = auto_ptr<MWmiObject>( new MWmiObject( *rhs.p_machine ) );

    TRACE(MTrace::INFO, L"mnlbsmachine assignment operator\n");

    return *this;
}


// destructor
//
MNLBMachine::~MNLBMachine()
{
    TRACE(MTrace::INFO, L"mnlbsmachine destructor\n");
}

// getClusterProperties
//
MNLBMachine::MNLBMachine_Error
MNLBMachine::getClusterProperties( ClusterProperties* cp )
{
    // set parameters to get for win2k
    //
    vector<MWmiParameter* >   parameterStoreWin2k;

    MWmiParameter ClusterIPAddress(L"ClusterIPAddress");
    parameterStoreWin2k.push_back( &ClusterIPAddress );

    MWmiParameter ClusterNetworkMask(L"ClusterNetworkMask");
    parameterStoreWin2k.push_back( &ClusterNetworkMask );

    MWmiParameter ClusterName(L"ClusterName");
    parameterStoreWin2k.push_back( &ClusterName );

    MWmiParameter ClusteMACAddress(L"ClusterMACAddress");
    parameterStoreWin2k.push_back( &ClusteMACAddress );

    MWmiParameter MulticastSupportEnabled(L"MulticastSupportEnabled");
    parameterStoreWin2k.push_back( &MulticastSupportEnabled );

    MWmiParameter RemoteControlEnabled(L"RemoteControlEnabled");
    parameterStoreWin2k.push_back( &RemoteControlEnabled );

    // set parameters to get for whistler
    // these properties only present in whistler.
    //
    vector<MWmiParameter* >   parameterStoreWhistler;

    MWmiParameter IgmpSupport(L"IgmpSupport");
    parameterStoreWhistler.push_back( &IgmpSupport );

    MWmiParameter ClusterIPToMulticastIP(L"ClusterIPToMulticastIP");
    parameterStoreWhistler.push_back( &ClusterIPToMulticastIP );

    MWmiParameter MulticastIPAddress(L"MulticastIPAddress");
    parameterStoreWhistler.push_back( &MulticastIPAddress );

    // Edited( mhakim 02-09-01)
    // now no longer do we have any igmpjoininterval property.
    // 

    // MWmiParameter IgmpJoinInterval(L"IgmpJoinInterval");
    // parameterStoreWhistler.push_back( &IgmpJoinInterval );


    // get instances of clustersetting class.    
    //
    vector< MWmiInstance >    instanceStore;

    _bstr_t relPath = L"MicrosoftNLB_ClusterSetting.Name=\"" + mClusterIP + L":" + mHostID + L"\"";
        
    p_machine->getSpecificInstance( L"MicrosoftNLB_ClusterSetting",
                                    relPath,
                                    &instanceStore );

    // get win2k parameters
    instanceStore[0].getParameters( parameterStoreWin2k );

    // get whistler parameters
    bool machineIsWhistler = true;

    try
    {
        // this will fail on win2k
        // as adapterguid, igmpsupport etc are not present.
        instanceStore[0].getParameters( parameterStoreWhistler );
    }
    catch( _com_error e )
    {
        // maybe it was a win2k machine.  This exception is expected, thus catch.
        // for better safety, need to find exact error code returned and then only do the 
        // folllowing otherwise need to rethrow.
        //
        TRACE( MTrace::INFO, L"tried whistler operation on win2k, this is expected\n");
        machineIsWhistler = false;
    }

    cp->cIP = ClusterIPAddress.getValue();
    
    cp->cSubnetMask = ClusterNetworkMask.getValue();
            
    cp->cFullInternetName = ClusterName.getValue();
            
    cp->cNetworkAddress = ClusteMACAddress.getValue();
            
    cp->multicastSupportEnabled = MulticastSupportEnabled.getValue();
        
    cp->remoteControlEnabled  = RemoteControlEnabled.getValue();

    if( machineIsWhistler == true )
    {
        cp->igmpSupportEnabled = IgmpSupport.getValue();
                
        cp->clusterIPToMulticastIP = ClusterIPToMulticastIP.getValue();

        cp->multicastIPAddress = MulticastIPAddress.getValue();

        // Edited( mhakim 02-09-01)
        // now no longer do we have any igmpjoininterval property.
        // 

        // cp->igmpJoinInterval = IgmpJoinInterval.getValue();
    }

    return MNLBMachine_SUCCESS;
}    

// getHostProperties
//
MNLBMachine::MNLBMachine_Error
MNLBMachine::getHostProperties( HostProperties* hp )
{
    // get node properties
    //
    vector<MWmiParameter* >    parameterStoreWin2k;

    // the status code is to be got from the Node class.
    //
    MWmiParameter  StatusCode(L"StatusCode");
    parameterStoreWin2k.push_back( &StatusCode );    

    vector< MWmiInstance >    instanceStore;

    _bstr_t relPath = L"MicrosoftNLB_Node.Name=\"" + mClusterIP + L":" + mHostID + L"\"";

    p_machine->getSpecificInstance( L"MicrosoftNLB_Node",
                                    relPath,
                                    &instanceStore );
    instanceStore[0].getParameters( parameterStoreWin2k );

    hp->hostStatus = long( StatusCode.getValue() );

    parameterStoreWin2k.erase( parameterStoreWin2k.begin(), parameterStoreWin2k.end() );

    instanceStore.erase( instanceStore.begin(), instanceStore.end() );
    
    MWmiParameter  DedicatedIPAddress(L"DedicatedIPAddress");
    parameterStoreWin2k.push_back( &DedicatedIPAddress );

    MWmiParameter  DedicatedNetworkMask(L"DedicatedNetworkMask");
    parameterStoreWin2k.push_back( &DedicatedNetworkMask );

    MWmiParameter  HostPriority(L"HostPriority");
    parameterStoreWin2k.push_back( &HostPriority );

    MWmiParameter  ClusterModeOnStart(L"ClusterModeOnStart");
    parameterStoreWin2k.push_back( &ClusterModeOnStart );

    MWmiParameter Server("__Server");
    parameterStoreWin2k.push_back( &Server);

    // set parameters to get for whistler
    // these properties only present in whistler.
    //
    vector<MWmiParameter* >   parameterStoreWhistler;

    MWmiParameter AdapterGuid(L"AdapterGuid");
    parameterStoreWhistler.push_back( &AdapterGuid );


    // get instances of nodesetting class.
    //
    relPath = L"MicrosoftNLB_NodeSetting.Name=\"" + mClusterIP + L":" + mHostID + L"\"";
    p_machine->getSpecificInstance( L"MicrosoftNLB_NodeSetting",
                                    relPath,
                                    &instanceStore );

    // get win2k parameters.
    instanceStore[0].getParameters( parameterStoreWin2k );

    // get whistler parameters
    bool machineIsWhistler = true;

    try
    {
        // this will fail on win2k
        // as adapterguid, igmpsupport etc are not present.
        instanceStore[0].getParameters( parameterStoreWhistler );
    }
    catch( _com_error e )
    {
        // maybe it was a win2k machine.  This exception is expected, thus catch.
        // for better safety, need to find exact error code returned and then only do the 
        // folllowing otherwise need to rethrow.
        //
        TRACE( MTrace::INFO, L"tried whistler operation on win2k, this is expected\n");
        machineIsWhistler = false;
    }


    hp->hIP = DedicatedIPAddress.getValue();

    hp->hSubnetMask = DedicatedNetworkMask.getValue();
            
    hp->hID         = HostPriority.getValue();
            
    hp->initialClusterStateActive = ClusterModeOnStart.getValue();

    hp->machineName = Server.getValue();

    if( machineIsWhistler == true )
    {
        Common::getNLBNicInfoForWhistler( mIP,
                                          _bstr_t( AdapterGuid.getValue() ), 
                                          hp->nicInfo );
    }
    else
    {
        Common::getNLBNicInfoForWin2k(  mIP,
                                        hp->nicInfo );
    }

    return MNLBMachine_SUCCESS;
}


// setClusterProperties    
//
MNLBMachine::MNLBMachine_Error
MNLBMachine::setClusterProperties( const ClusterProperties& cp,
                                    unsigned long* retVal 
                                    )
{
    // set parameters to set for win2k
    vector<MWmiParameter* >   parameterStoreWin2k;

    MWmiParameter ClusterIPAddress(L"ClusterIPAddress");
    ClusterIPAddress.setValue( cp.cIP );
    parameterStoreWin2k.push_back( &ClusterIPAddress );
    

    MWmiParameter ClusterNetworkMask(L"ClusterNetworkMask");
    ClusterNetworkMask.setValue( cp.cSubnetMask );
    parameterStoreWin2k.push_back( &ClusterNetworkMask );

    MWmiParameter ClusterName(L"ClusterName");
    ClusterName.setValue( cp.cFullInternetName );
    parameterStoreWin2k.push_back( &ClusterName );

    // mac address cannot be set.

    MWmiParameter MulticastSupportEnabled(L"MulticastSupportEnabled");
    MulticastSupportEnabled.setValue( cp.multicastSupportEnabled );
    parameterStoreWin2k.push_back( &MulticastSupportEnabled );

    MWmiParameter RemoteControlEnabled(L"RemoteControlEnabled");
    RemoteControlEnabled.setValue( cp.remoteControlEnabled );
    parameterStoreWin2k.push_back( &RemoteControlEnabled );

    // set parameters for whistler.
    // these properties only for whistler
    vector<MWmiParameter* >   parameterStoreWhistler;

    MWmiParameter IgmpSupport(L"IgmpSupport");
    IgmpSupport.setValue( cp.igmpSupportEnabled );
    parameterStoreWhistler.push_back( &IgmpSupport );

    MWmiParameter ClusterIPToMulticastIP(L"ClusterIPToMulticastIP");
    ClusterIPToMulticastIP.setValue( cp.clusterIPToMulticastIP );
    parameterStoreWhistler.push_back( &ClusterIPToMulticastIP );

    MWmiParameter MulticastIPAddress(L"MulticastIPAddress");
    MulticastIPAddress.setValue( cp.multicastIPAddress );
    parameterStoreWhistler.push_back( &MulticastIPAddress );

    // Edited( mhakim 02-09-01)
    // now no longer do we have any igmpjoininterval property.
    // 

    // MWmiParameter IgmpJoinInterval(L"IgmpJoinInterval");
    // IgmpJoinInterval.setValue( cp.igmpJoinInterval );
    // parameterStoreWhistler.push_back( &IgmpJoinInterval );

    vector< MWmiInstance >    instanceStore;

    _bstr_t relPath = L"MicrosoftNLB_ClusterSetting.Name=\"" + mClusterIP + L":" + mHostID + L"\"";
    p_machine->getSpecificInstance( L"MicrosoftNLB_ClusterSetting",
                                    relPath,
                                    &instanceStore );

    // Edited( mhakim 02-14-01)
    //
    // The order in which win2k and whistler operations
    // need to be done depends on previous state of cluster.
    // If the previous mode of cluster was unicast, then
    // you have to do win2k first, whistler next.
    // If the previous mode was mcast+igmp you have to 
    // do whistler first, win2k next.
    // If the previous mode was mcast, the order does not
    // matter.
    // All this is because you cant have unicast+igmp 
    // together.

    // find previous cluster mode.
    ClusterProperties cpPrevious;
    getClusterProperties( &cpPrevious );

    if( cpPrevious.multicastSupportEnabled == false ) 
    {
        // mode is unicast

        // set win2k parameters.
        instanceStore[0].setParameters( parameterStoreWin2k );
        
        // set whistler parameters.
        try
        {
            instanceStore[0].setParameters( parameterStoreWhistler );
        }
        catch( _com_error e )
        {
            // maybe it was a win2k machine.  This exception is expected, thus catch.
            // For better safety, need to match error code returned with expected and then only do the 
            // folllowing otherwise need to rethrow.
            //
            TRACE( MTrace::INFO, L"tried whistler operation on win2k, this is expected\n");
        }
    }
    else
    {
        // set whistler parameters.
        try
        {
            instanceStore[0].setParameters( parameterStoreWhistler );
        }
        catch( _com_error e )
        {
            // maybe it was a win2k machine.  This exception is expected, thus catch.
            // For better safety, need to match error code returned with expected and then only do the 
            // folllowing otherwise need to rethrow.
            //
            TRACE( MTrace::INFO, L"tried whistler operation on win2k, this is expected\n");
        }

        // set win2k parameters.
        instanceStore[0].setParameters( parameterStoreWin2k );
    }

    // sync up the driver with these changes.
    reload( retVal );

    mClusterIP = cp.cIP;

    return MNLBMachine_SUCCESS;
}


// setHostProperties
//
MNLBMachine::MNLBMachine_Error
MNLBMachine::setHostProperties( const HostProperties& hp,
                                 unsigned long* retVal )
{
    // set node properties
    //
    vector<MWmiParameter* >    parameterStore;
    
    MWmiParameter  DedicatedIPAddress(L"DedicatedIPAddress");
    DedicatedIPAddress.setValue( hp.hIP );
    parameterStore.push_back( &DedicatedIPAddress );

    MWmiParameter  DedicatedNetworkMask(L"DedicatedNetworkMask");
    DedicatedNetworkMask.setValue( hp.hSubnetMask );
    parameterStore.push_back( &DedicatedNetworkMask );

    MWmiParameter  HostPriority(L"HostPriority");
    HostPriority.setValue( hp.hID );
    parameterStore.push_back( &HostPriority );

    MWmiParameter  ClusterModeOnStart(L"ClusterModeOnStart");
    ClusterModeOnStart.setValue( hp.initialClusterStateActive );
    parameterStore.push_back( &ClusterModeOnStart );

    // get instances of nodesetting class.
    //
    vector< MWmiInstance >     instanceStore;

    _bstr_t relPath = L"MicrosoftNLB_NodeSetting.Name=\"" + mClusterIP + L":" + mHostID + L"\"";
    p_machine->getSpecificInstance( L"MicrosoftNLB_NodeSetting",
                                    relPath,
                                    &instanceStore );

    instanceStore[0].setParameters( parameterStore );

    // convert hp.hID into string
    wchar_t hIDString[Common::BUF_SIZE];

    swprintf( hIDString, L"%d", hp.hID );
    
    // sync up the driver with these changes.
    reload( retVal );


    mHostID = hIDString;

    return MNLBMachine_SUCCESS;
}

// getPortRulesLoadBalanced
//
MNLBMachine::MNLBMachine_Error
MNLBMachine::getPortRulesLoadBalanced( vector<MNLBPortRuleLoadBalanced>* portsLB )
{
    vector<MWmiInstance> instanceStore;

    return getPortRulesLoadBalanced_private( portsLB, 
                                             &instanceStore );

}

// getPortRulesLoadBalanced_private
//
MNLBMachine::MNLBMachine_Error
MNLBMachine::getPortRulesLoadBalanced_private( vector<MNLBPortRuleLoadBalanced>* portsLB,
                                                vector<MWmiInstance>*          instances )
{
    _bstr_t myName = mClusterIP + L":" + mHostID;

    // get port properties.
    //
    vector<MWmiParameter* >    parameterStore;

    MWmiParameter   Name(L"Name");
    parameterStore.push_back( &Name );

    MWmiParameter   StartPort(L"StartPort");
    parameterStore.push_back( &StartPort );

    MWmiParameter EndPort(L"EndPort");
    parameterStore.push_back( &EndPort );

    MWmiParameter ProtocolProp(L"Protocol");
    parameterStore.push_back( &ProtocolProp );

    MWmiParameter EqualLoad(L"EqualLoad");
    parameterStore.push_back( &EqualLoad );

    MWmiParameter LoadWeight(L"LoadWeight");
    parameterStore.push_back( &LoadWeight );

    MWmiParameter AffinityProp(L"Affinity");
    parameterStore.push_back( &AffinityProp );    

    MNLBPortRule::Affinity    affinity;
    MNLBPortRule::Protocol    trafficToHandle;

    p_machine->getInstances( L"MicrosoftNLB_PortRuleLoadBalanced",
                             instances );
    for( int i = 0; i < instances->size(); ++i )
    {
        (*instances)[i].getParameters( parameterStore );
            
        if( _bstr_t( Name.getValue() ) != myName )
        {
            // this portrule is not for this cluster
            continue;
        }

        switch( long (ProtocolProp.getValue()) )
        {
            case 1:
                trafficToHandle = MNLBPortRule::tcp;
                break;

            case 2:
                trafficToHandle = MNLBPortRule::udp;
                break;

            case 3:
                trafficToHandle = MNLBPortRule::both;
                break;

            default:
                // bug.
                TRACE( MTrace::SEVERE_ERROR, "unidentified protocol\n" );
                throw _com_error( WBEM_E_UNEXPECTED );
                break;
        }


        switch(  long(AffinityProp.getValue()) )
        {
            case 0:
                affinity = MNLBPortRule::none;
                break;

            case 1:
                affinity = MNLBPortRule::single;
                break;

            case 2:
                affinity = MNLBPortRule::classC;
                break;

            default:
                // bug.
                TRACE( MTrace::SEVERE_ERROR, "unidentified affinity\n" );
                throw _com_error( WBEM_E_UNEXPECTED );
                break;
        }

                
        portsLB->push_back( MNLBPortRuleLoadBalanced(long (StartPort.getValue()),
                                                  long( EndPort.getValue()),
                                                  trafficToHandle,
                                                  bool( EqualLoad.getValue()),
                                                  long( LoadWeight.getValue()),
                                                  affinity) );
                                
    }

    return MNLBMachine_SUCCESS;
}


// getPortRulesFailover
//       
MNLBMachine::MNLBMachine_Error
MNLBMachine::getPortRulesFailover( vector<MNLBPortRuleFailover>* portsF )
{
    vector<MWmiInstance> instanceStore;

    return getPortRulesFailover_private( portsF, 
                                         &instanceStore );
}
    

// getPortRulesFailover_private
//
MNLBMachine::MNLBMachine_Error
MNLBMachine::getPortRulesFailover_private( vector<MNLBPortRuleFailover>* portsF,
                                            vector<MWmiInstance>*          instances )
{    
    _bstr_t myName = mClusterIP + L":" + mHostID;

    // get port properties.
    //
    vector<MWmiParameter* >    parameterStore;

    MWmiParameter   Name(L"Name");
    parameterStore.push_back( &Name );

    MWmiParameter   StartPort(L"StartPort");
    parameterStore.push_back( &StartPort );

    MWmiParameter EndPort(L"EndPort");
    parameterStore.push_back( &EndPort );

    MWmiParameter ProtocolProp(L"Protocol");
    parameterStore.push_back( &ProtocolProp );

    MWmiParameter Priority(L"Priority");
    parameterStore.push_back( &Priority );

    MNLBPortRule::Protocol    trafficToHandle;

    p_machine->getInstances( L"MicrosoftNLB_PortRuleFailover",
                             instances );

    for( int i = 0; i < instances->size(); ++i )
    {
        (*instances)[i].getParameters( parameterStore );
            
        if( _bstr_t( Name.getValue() ) != myName )
        {
            // this portrule is not for this cluster
            continue;
        }

        switch( long (ProtocolProp.getValue()) )
        {
            case 1:
                trafficToHandle = MNLBPortRule::tcp;
                break;

            case 2:
                trafficToHandle = MNLBPortRule::udp;
                break;

            case 3:
                trafficToHandle = MNLBPortRule::both;
                break;

            default:
                // bug.
                TRACE( MTrace::SEVERE_ERROR, "unidentified protocol\n" );
                throw _com_error( WBEM_E_UNEXPECTED );
                break;
        }

        portsF->push_back( MNLBPortRuleFailover(long (StartPort.getValue()),
                                             long( EndPort.getValue()),
                                             trafficToHandle,
                                             long( Priority.getValue()) )
                           );
    }

    return MNLBMachine_SUCCESS;
}


// getPortRulesDisabled
//
MNLBMachine::MNLBMachine_Error
MNLBMachine::getPortRulesDisabled( vector<MNLBPortRuleDisabled>* portsD )
{

    vector<MWmiInstance> instanceStore;

    return getPortRulesDisabled_private( portsD, 
                                         &instanceStore );

}    


// getPortRulesDisabled_private
//
MNLBMachine::MNLBMachine_Error
MNLBMachine::getPortRulesDisabled_private( vector<MNLBPortRuleDisabled>*         portsD,
                                            vector<MWmiInstance>*          instances )
{
    _bstr_t myName = mClusterIP + L":" + mHostID;

    // get port properties.
    //
    vector<MWmiParameter* >    parameterStore;

    MWmiParameter   Name(L"Name");
    parameterStore.push_back( &Name );

    MWmiParameter   StartPort(L"StartPort");
    parameterStore.push_back( &StartPort );

    MWmiParameter EndPort(L"EndPort");
    parameterStore.push_back( &EndPort );

    MWmiParameter ProtocolProp(L"Protocol");
    parameterStore.push_back( &ProtocolProp );

    MNLBPortRule::Protocol    trafficToHandle;

    p_machine->getInstances( L"MicrosoftNLB_PortRuleDisabled",
                             instances );
    for( int i = 0; i < instances->size(); ++i )
    {
        (*instances)[i].getParameters( parameterStore );
            
        if( _bstr_t( Name.getValue() ) != myName )
        {
            // this portrule is not for this cluster
            continue;
        }

        switch( long (ProtocolProp.getValue()) )
        {
            case 1:
                trafficToHandle = MNLBPortRule::tcp;
                break;

            case 2:
                trafficToHandle = MNLBPortRule::udp;
                break;

            case 3:
                trafficToHandle = MNLBPortRule::both;
                break;

            default:
                // bug.
                TRACE( MTrace::SEVERE_ERROR, "unidentified protocol\n" );
                throw _com_error( WBEM_E_UNEXPECTED );
                break;
        }

        portsD->push_back( MNLBPortRuleDisabled( long (StartPort.getValue() ),
                                              long( EndPort.getValue() ),
                                              trafficToHandle ) );
    }

    return MNLBMachine_SUCCESS;
}


// addPortRuleLoadBalanced
//
MNLBMachine::MNLBMachine_Error
MNLBMachine::addPortRuleLoadBalanced( const MNLBPortRuleLoadBalanced& portRuleLB )
{

    // set the name as "clusterip:hostid"
    _bstr_t str;
    str = mClusterIP + L":" + mHostID;

    vector<MWmiParameter *> instanceParameter;

    MWmiParameter  name(L"Name");
    name.setValue( str );
    instanceParameter.push_back( &name );

    MWmiParameter  sp(L"StartPort");
    sp.setValue( portRuleLB._startPort );
    instanceParameter.push_back( &sp );

    MWmiParameter  EndPort(L"EndPort");
    EndPort.setValue( portRuleLB._endPort );
    instanceParameter.push_back( &EndPort );

    long protocolValue;
    MWmiParameter  protocol(L"Protocol");
    switch( portRuleLB._trafficToHandle )
    {
        case MNLBPortRule::both:
            protocolValue = 3;
            protocol.setValue( protocolValue );
            break;
            
        case MNLBPortRule::tcp:
            protocolValue = 1;
            protocol.setValue( protocolValue );
            break;

        case MNLBPortRule::udp:
            protocolValue = 2;
            protocol.setValue( protocolValue );
            break;
                    
        default:
            // bug.
            TRACE( MTrace::SEVERE_ERROR, "unidentified protocol\n" );
            throw _com_error( WBEM_E_UNEXPECTED );
            break;
    }

    instanceParameter.push_back( &protocol );

    MWmiParameter  el(L"EqualLoad");
    el.setValue( portRuleLB._isEqualLoadBalanced );
    instanceParameter.push_back( &el );    

    MWmiParameter  lw(L"LoadWeight");
    lw.setValue( portRuleLB._load );
    instanceParameter.push_back( &lw );    

    MWmiParameter  affinity(L"Affinity");
    long affinityValue;
    switch(  portRuleLB._affinity )
    {
        case MNLBPortRule::none:
            affinityValue = 0;
            affinity.setValue(affinityValue);
            break;

        case MNLBPortRule::single:
            affinityValue = 1;
            affinity.setValue(affinityValue);
            break;

        case MNLBPortRule::classC:
            affinityValue = 2;
            affinity.setValue(affinityValue);
            break;

        default:
            // bug.
            TRACE( MTrace::SEVERE_ERROR, "unidentified affinity\n" );
            throw _com_error( WBEM_E_UNEXPECTED );
            break;
    }
    instanceParameter.push_back( &affinity );    

    p_machine->createInstance( L"MicrosoftNLB_PortRuleLoadBalanced",
                               instanceParameter
                               );
    // sync up the driver with these changes.
    unsigned long retVal;
    reload( &retVal );

    return MNLBMachine_SUCCESS;
}

// addPortRuleFailover
//
MNLBMachine::MNLBMachine_Error
MNLBMachine::addPortRuleFailover( const MNLBPortRuleFailover& portRuleF )
{

    // set the name as "clusterip:hostid"
    _bstr_t str;
    str = mClusterIP + L":" + mHostID;

    vector<MWmiParameter *> instanceParameter;

    MWmiParameter  name(L"Name");
    name.setValue( str );
    instanceParameter.push_back( &name );

    MWmiParameter  sp(L"StartPort");
    sp.setValue( portRuleF._startPort );
    instanceParameter.push_back( &sp );

    MWmiParameter  EndPort(L"EndPort");
    EndPort.setValue( portRuleF._endPort );
    instanceParameter.push_back( &EndPort );

    long protocolValue;
    MWmiParameter  protocol(L"Protocol");
    switch( portRuleF._trafficToHandle )
    {
        case MNLBPortRule::both:
            protocolValue = 3;
            protocol.setValue( protocolValue );
            break;
            
        case MNLBPortRule::tcp:
            protocolValue = 1;
            protocol.setValue( protocolValue );
            break;

        case MNLBPortRule::udp:
            protocolValue = 2;
            protocol.setValue( protocolValue );
            break;
                    
        default:
            TRACE( MTrace::SEVERE_ERROR, "unidentified protocol\n" );
            throw _com_error( WBEM_E_UNEXPECTED );
            break;
    }
    instanceParameter.push_back( &protocol );

    MWmiParameter p("Priority");
    p.setValue( portRuleF._priority);
    instanceParameter.push_back( &p );

    p_machine->createInstance( L"MicrosoftNLB_PortRuleFailover",
                               instanceParameter
                               );

    // sync up the driver with these changes.
    unsigned long retVal;
    reload( &retVal );

    return MNLBMachine_SUCCESS;
}


// addPortRuleDisabled
//
MNLBMachine::MNLBMachine_Error
MNLBMachine::addPortRuleDisabled( const MNLBPortRuleDisabled& portRuleD )
{

    // set the name as "clusterip:hostid"
    _bstr_t str;
    str = mClusterIP + L":" + mHostID;

    vector<MWmiParameter *> instanceParameter;

    MWmiParameter  name(L"Name");
    name.setValue( str );
    instanceParameter.push_back( &name );

    MWmiParameter  sp(L"StartPort");
    sp.setValue( portRuleD._startPort );
    instanceParameter.push_back( &sp );

    MWmiParameter  ep(L"EndPort");
    ep.setValue( portRuleD._endPort );
    instanceParameter.push_back( &ep );

    long protocolValue;
    MWmiParameter  protocol(L"Protocol");
    switch( portRuleD._trafficToHandle )
    {
        case MNLBPortRule::both:
            protocolValue = 3;
            protocol.setValue( protocolValue );
            break;
            
        case MNLBPortRule::tcp:
            protocolValue = 1;
            protocol.setValue( protocolValue );
            break;

        case MNLBPortRule::udp:
            protocolValue = 2;
            protocol.setValue( protocolValue );
            break;
                    
        default:
            // bug.
            TRACE( MTrace::SEVERE_ERROR, "unidentified protocol\n" );
            throw _com_error( WBEM_E_UNEXPECTED );
            break;
    }
    instanceParameter.push_back( &protocol );

    p_machine->createInstance( L"MicrosoftNLB_PortRuleDisabled",
                               instanceParameter
                               );

    // sync up the driver with these changes.
    unsigned long retVal;
    reload( &retVal );

    return MNLBMachine_SUCCESS;
}

// removePortRuleLoadBalanced
//
MNLBMachine::MNLBMachine_Error
MNLBMachine::removePortRuleLoadBalanced( const MNLBPortRuleLoadBalanced& portRuleLB )
{

    _bstr_t myName = mClusterIP + L":" + mHostID;

    wchar_t startPortBuffer[Common::BUF_SIZE];

    swprintf( startPortBuffer, L"%d", portRuleLB._startPort );

    vector<MWmiInstance >           instanceStorePortRuleLoadBalanced;
    _bstr_t relPath = L"MicrosoftNLB_PortRuleLoadBalanced.Name=\""  
        + mClusterIP + L":" + mHostID + L"\"" + L"," + L"StartPort=" + startPortBuffer;


    p_machine->getSpecificInstance( L"MicrosoftNlb_PortRuleLoadBalanced",
                                    relPath,
                                    &instanceStorePortRuleLoadBalanced );

    p_machine->deleteInstance( instanceStorePortRuleLoadBalanced[0] );

    // sync up the driver with these changes.
    unsigned long retVal;
    reload( &retVal );

    return MNLBMachine_SUCCESS;
}


// removePortRuleFailover
//
MNLBMachine::MNLBMachine_Error
MNLBMachine::removePortRuleFailover( const MNLBPortRuleFailover& portRuleF )
{
    _bstr_t myName = mClusterIP + L":" + mHostID;

    wchar_t startPortBuffer[Common::BUF_SIZE];

    swprintf( startPortBuffer, L"%d", portRuleF._startPort );

    vector<MWmiInstance >           instanceStorePortRuleFailover;
    _bstr_t relPath = L"MicrosoftNLB_PortRuleFailover.Name=\""  
        + mClusterIP + L":" + mHostID + L"\"" + L"," + L"StartPort=" + startPortBuffer;


    p_machine->getSpecificInstance( L"MicrosoftNlb_PortRuleFailover",
                                    relPath,
                                    &instanceStorePortRuleFailover );

    p_machine->deleteInstance( instanceStorePortRuleFailover[0] );

    // sync up the driver with these changes.
    unsigned long retVal;
    reload( &retVal );

    return MNLBMachine_SUCCESS;
}

// removePortRuleDisabled
//
MNLBMachine::MNLBMachine_Error
MNLBMachine::removePortRuleDisabled( const MNLBPortRuleDisabled& portRuleD )
{

    _bstr_t myName = mClusterIP + L":" + mHostID;

    wchar_t startPortBuffer[Common::BUF_SIZE];

    swprintf( startPortBuffer, L"%d", portRuleD._startPort );

    vector<MWmiInstance >           instanceStorePortRuleDisabled;
    _bstr_t relPath = L"MicrosoftNLB_PortRuleDisabled.Name=\""  
        + mClusterIP + L":" + mHostID + L"\"" + L"," + L"StartPort=" + startPortBuffer;


    p_machine->getSpecificInstance( L"MicrosoftNlb_PortRuleDisabled",
                                    relPath,
                                    &instanceStorePortRuleDisabled );

    p_machine->deleteInstance( instanceStorePortRuleDisabled[0] );

    // sync up the driver with these changes.
    unsigned long retVal;
    reload( &retVal );

    return MNLBMachine_SUCCESS;
}




// start
//
MNLBMachine::MNLBMachine_Error
MNLBMachine::start( int hostID, unsigned long* retVal )
{
    // get the specific instance to run method on.
    vector< MWmiInstance >  instanceStore;
    getInstanceToRunMethodOn( hostID, &instanceStore );

    MNLBExe::start( instanceStore[0], retVal );

    return MNLBMachine_SUCCESS;
}



// stop
//
MNLBMachine::MNLBMachine_Error
MNLBMachine::stop( int hostID, unsigned long* retVal )
{
    // get the specific instance to run method on.
    vector< MWmiInstance >  instanceStore;
    getInstanceToRunMethodOn( hostID, &instanceStore );

    MNLBExe::stop( instanceStore[0], retVal );

    return MNLBMachine_SUCCESS;
}


// resume
//
MNLBMachine::MNLBMachine_Error
MNLBMachine::resume( int hostID, unsigned long* retVal )
{
    // get the specific instance to run method on.
    vector< MWmiInstance >  instanceStore;
    getInstanceToRunMethodOn( hostID, &instanceStore );

    MNLBExe::resume( instanceStore[0], retVal );

    return MNLBMachine_SUCCESS;
}


// suspend
//
MNLBMachine::MNLBMachine_Error
MNLBMachine::suspend( int hostID, unsigned long* retVal )
{
    // get the specific instance to run method on.
    vector< MWmiInstance >  instanceStore;
    getInstanceToRunMethodOn( hostID, &instanceStore );

    MNLBExe::suspend( instanceStore[0], retVal );

    return MNLBMachine_SUCCESS;
}


// drainstop
//
MNLBMachine::MNLBMachine_Error
MNLBMachine::drainstop( int hostID, unsigned long* retVal )
{
    // get the specific instance to run method on.
    vector< MWmiInstance >  instanceStore;
    getInstanceToRunMethodOn( hostID, &instanceStore );

    MNLBExe::drainstop( instanceStore[0], retVal );

    return MNLBMachine_SUCCESS;
}


// disable
//
MNLBMachine::MNLBMachine_Error
MNLBMachine::disable( int hostID, unsigned long* retVal, unsigned long portToAffect )
{
    // get the specific instance to run method on.
    vector< MWmiInstance >  instanceStore;

    getInstanceToRunMethodOn( hostID, &instanceStore );

    MNLBExe::disable( instanceStore[0], retVal, portToAffect );

    return MNLBMachine_SUCCESS;
}


// enable
//
MNLBMachine::MNLBMachine_Error
MNLBMachine::enable( int hostID, unsigned long* retVal, unsigned long portToAffect )
{
    // get the specific instance to run method on.
    vector< MWmiInstance >  instanceStore;

    getInstanceToRunMethodOn( hostID, &instanceStore );

    MNLBExe::enable( instanceStore[0], retVal, portToAffect );

    return MNLBMachine_SUCCESS;
}


// drain
//
MNLBMachine::MNLBMachine_Error
MNLBMachine::drain( int hostID, unsigned long* retVal, unsigned long portToAffect )
{
    // get the specific instance to run method on.
    vector< MWmiInstance >  instanceStore;

    getInstanceToRunMethodOn( hostID, &instanceStore );

    MNLBExe::drain( instanceStore[0], retVal, portToAffect );

    return MNLBMachine_SUCCESS;
}


// reload
//
MNLBMachine::MNLBMachine_Error
MNLBMachine::reload( unsigned long* retVal )
{
    vector<MWmiInstance >           instanceStoreClusterSetting;
    _bstr_t relPath = L"MicrosoftNLB_ClusterSetting.Name=\"" + mClusterIP + L":" + mHostID + L"\"";
    p_machine->getSpecificInstance( L"MicrosoftNLB_ClusterSetting",
                                    relPath,
                                    &instanceStoreClusterSetting );

    // input parameters are none.
    //
    vector<MWmiParameter *> inputParameters;
            
    //  output parameters which are of interest.
    //
    vector<MWmiParameter *> outputParameters;
    MWmiParameter    returnValue(L"ReturnValue");
    outputParameters.push_back( &returnValue );
            
    instanceStoreClusterSetting[0].runMethod( L"LoadAllSettings",
                                              inputParameters,
                                              outputParameters );

    *retVal = ( long ) returnValue.getValue();

    return MNLBMachine_SUCCESS;
}    


// getInstanceToRunMethodOn
MNLBMachine::MNLBMachine_Error
MNLBMachine::getInstanceToRunMethodOn( int hID, vector <MWmiInstance>* instanceStore )
{
    vector<MWmiInstance> instanceStoreX;

    _bstr_t relPath;

    // see if we want to run clusterwide or host specific method.
    if( hID == Common::ALL_HOSTS )  
    {
        // cluster wide
        relPath = L"MicrosoftNLB_Cluster.Name=\"" + mClusterIP + L"\"";
        p_machine->getSpecificInstance( L"MicrosoftNLB_Cluster",
                                        relPath,
                                        &instanceStoreX );

        instanceStore->push_back( instanceStoreX[0] );
    }
    else if( hID == Common::THIS_HOST )
    {
        // this host specific
        relPath = L"MicrosoftNLB_Node.Name=\"" + mClusterIP + L":" + mHostID + L"\"";
        p_machine->getSpecificInstance( L"MicrosoftNLB_Node",
                                               relPath,
                                               &instanceStoreX );
        instanceStore->push_back( instanceStoreX[0] );

    }
    else
    { 
        // some other host

        // convert hID into string
        wchar_t hostIDBuf[1000];
        swprintf( hostIDBuf, L"%d", hID );

        relPath = L"MicrosoftNLB_Node.Name=\"" + mClusterIP + L":" + hostIDBuf + L"\"";
        p_machine->getSpecificInstance( L"MicrosoftNLB_Node",
                                               relPath,
                                               &instanceStoreX );

        instanceStore->push_back( instanceStoreX[0] );
    }
                
    return MNLBMachine_SUCCESS;
}
    
    
// refreshConnection
//
MNLBMachine::MNLBMachine_Error
MNLBMachine::refreshConnection()
{
    return connectToMachine();
}


// connectToMachine
//
MNLBMachine::MNLBMachine_Error
MNLBMachine::connectToMachine()
{
    MTrace* trace = MTrace::Instance();

    MTrace::TraceLevel prevLevel = trace->GetLevel();
    vector<HostProperties> hostPropertiesStore;

    // basically two operations need to succeed.
    // 1.  Is machine ip specified usable or not.
    // 2.  Is the nic specified bound to nlbs or not.
    //

    // connect to machine
    //
    if( mIP == _bstr_t( L"self")  )
    {
        // we want to connect to self machine.
        p_machine = auto_ptr< MWmiObject > ( new MWmiObject( L"root\\microsoftnlb") );
    }
    else if( mIP == mClusterIP )
    {
        // connecting to cluster remotely using cluster ip.  
        try
        {
            trace->SetLevel( MTrace::NO_TRACE );

            p_machine = auto_ptr<MWmiObject> ( new MWmiObject(  mIP,
                                                                L"root\\microsoftnlb",
                                                                L"Administrator",
                                                                L"" ) );
        }
        catch( _com_error e )
        {
            trace->SetLevel( prevLevel );
            TRACE(MTrace::INFO, L"connection using clusterip has failed\n" );
                    
            // Now we will be using api call wlbs query as situation may be that all hosts in cluster are stopped, and no connection
            // can be established to cluster ip, or there may be no such cluster and we will throw exception.
            //
            
            // get all hosts participating in cluster.
            DWORD ret = Common::getHostsInCluster( mClusterIP, &hostPropertiesStore );
            if( hostPropertiesStore.size() == 0 )
            {
                // no hosts are in this cluster.  Cannot proceed reliably.
                wstring errString;
                errString = L"cluster " +  wstring( mClusterIP ) + L" reported as having no hosts.  Maybe cluster ip is wrong, or remote control is turned off\n";
                TRACE( MTrace::SEVERE_ERROR, errString );

                throw _com_error( WBEM_E_NOT_FOUND );
            }

            // check if any host has dedicated ip not present
            // if this is so then object may not function reliably.
            bool allZero = true;
            int validHost;
            for( int i = 0; i < hostPropertiesStore.size(); ++i )
            {
                if( hostPropertiesStore[i].hIP == _bstr_t( L"0.0.0.0" ) )
                {
                    // host does not have valid dedicated ip.
                    TRACE( MTrace::SEVERE_ERROR, "a host has no dedicated ip.  Some object operations may not work as expected\n");
                }
                else
                {
                    // not all hosts have dedicated ip not filled in. 
                    // keep a track of this host.
                    allZero = false;
                    validHost = i;
                }
            }

            // check if all hosts had invalid dedicated ip.
            if( allZero == true )
            {
                // all hosts have dedicated ip invalid.
                TRACE( MTrace::SEVERE_ERROR, "all cluster hosts have dedicated ip 0.0.0.0\n");
                throw _com_error( WBEM_E_INVALID_PROPERTY );
            }
        
            p_machine = auto_ptr<MWmiObject>( new MWmiObject( hostPropertiesStore[validHost].hIP,
                                                              L"root\\microsoftnlb",
                                                              L"Administrator",
                                                              L"") );
        }
    }
    else
    {
        // we want to connect to remote machine.
        p_machine = auto_ptr<MWmiObject> ( new MWmiObject(  mIP,
                                                            L"root\\microsoftnlb",
                                                            L"Administrator",
                                                            L"" ) );
    }


    // check if specified cluster ip exists on this machine
    // and find the host id corresponding to this cluster on this machine.
    checkClusterIPAndSetHostID();

    return MNLBMachine_SUCCESS;
}

MNLBMachine::MNLBMachine_Error
MNLBMachine::checkClusterIPAndSetHostID()
{
    // set parameter to get.
    // we are interested in name.
    //
    vector<MWmiParameter* >   parameterStore;

    MWmiParameter Name(L"Name");
    parameterStore.push_back( &Name );

    // get instances of clustersetting class.    
    //
    vector< MWmiInstance >    instanceStore;
    bool found = false;
    int  i;

    p_machine->getInstances( L"Microsoftnlb_ClusterSetting",
                             &instanceStore );
    found = false;
    for( i = 0; i < instanceStore.size(); ++i )
    {
        instanceStore[i].getParameters( parameterStore );

        WTokens        tok;
        vector<wstring> tokens;
        tok.init( wstring( _bstr_t(Name.getValue()) ),
                  L":" );
        tokens = tok.tokenize();
            
        _bstr_t clusterIP = tokens[0].c_str();
        mHostID = tokens[1].c_str();

        if( mClusterIP == clusterIP )
        {
            // right object found
            found = true;
            break;
        }
    }
    
    if( found == false )
    {
        TRACE(
            MTrace::SEVERE_ERROR, 
            L"this machine does not have any cluster with specified cluster ip\n");
        throw _com_error( WBEM_E_NOT_FOUND );
    }

    return MNLBMachine_SUCCESS;
}


// getPresentHostsInfo
//
MNLBMachine::MNLBMachine_Error
MNLBMachine::getPresentHostsInfo( 
    vector< MNLBMachine::HostInfo >* hostInfoStore)
{
    vector<MWmiParameter* >   parameterStore;
    
    MWmiParameter  Name(L"Name");
    parameterStore.push_back( &Name );

    MWmiParameter  DedicatedIPAddress(L"DedicatedIPAddress");
    parameterStore.push_back( &DedicatedIPAddress );

    vector< MWmiInstance >    instanceStore;

    p_machine->getInstances( L"MicrosoftNLB_Node",
                             &instanceStore );
    for( int i = 0; i < instanceStore.size(); ++i )
    {
        instanceStore[i].getParameters( parameterStore );

        // Name is in format of "clusterIP:hostID"
        //
        WTokens        tok;
        vector<wstring> tokens;
        tok.init( wstring( _bstr_t(Name.getValue()) ),
                  L":" );
        tokens = tok.tokenize();
        _bstr_t clusterIP = tokens[0].c_str();

        if( mClusterIP == clusterIP )
        {
            HostInfo hostInfo;
            hostInfo.hostID = _wtoi( tokens[1].c_str() );
            hostInfo.dedicatedIP = DedicatedIPAddress.getValue();
            
            hostInfoStore->push_back( hostInfo );
        }
    }

    return MNLBMachine_SUCCESS;
}


// setPassword
//
MNLBMachine::MNLBMachine_Error
MNLBMachine::setPassword( const _bstr_t& password,
                          unsigned long* retVal 
                          )
{
    // form path
    _bstr_t relPath = L"MicrosoftNLB_ClusterSetting.Name=\"" + mClusterIP + L":" + mHostID + L"\"";

    vector<MWmiInstance >           instanceStoreClusterSetting;
    p_machine->getSpecificInstance( L"MicrosoftNLB_ClusterSetting",
                                    relPath,
                                    &instanceStoreClusterSetting );

    // input parameters is password
    //
    vector<MWmiParameter *> inputParameters;
    MWmiParameter    Password(L"Password");
    Password.setValue( password );
    inputParameters.push_back( &Password );
            
    // output parameters which are of interest.
    // none.
    //
    vector<MWmiParameter *> outputParameters;
            
    instanceStoreClusterSetting[0].runMethod( L"SetPassword",
                                              inputParameters,
                                              outputParameters );

    reload( retVal );

    return MNLBMachine_SUCCESS;
}