Monitoring Application Level Health

This example demonstrates monitoring services on Network Load Balancing (NLB) nodes, stopping NLB on any nodes where the monitored service has stopped.

'''''''''''''''''''''''''''''''''''
' NlbMon.vbs
'
' Sample script to monitor NLB clusters.
'
' User can pass any NT service on the NLB cluster nodes for the
' script to check status. If that service is stopped on any of
' NLB nodes, the script will stop NLB on that node.
' 
' Optionally includes capability of passing alternate credentials
' to use when connecting to the cluster nodes.
'
' Note: Windows Server 2003 nodes should have remote control
' enabled. Otherwise, you can extend the -n command-line
' parameter to accept multiple nodes to connect to individually.
'
'
' Suggested additional enhancements/customizations:
'
' This script can be customized for much more complicated health
' monitoring and dynamic load-balancing capabilities. Examples
' include:
'
'    - Loop infinitely, checking for latest service status. This
'      script currently executes one iteration against the cluster
'      nodes.
'    - Automatically start back NLB on a node if the service
'      comes back to the started state. Currently, this script
'      waits for interaction from the administrator to debug the 
'      service problem before restarting NLB on the affected nodes.
'    - Check stress level on nodes (for example, checking CPU,
'      memory, or other performance metrics on the system).
'    - Check application response times. For example, if the
'      application being load balanced is slow in responding on one
'      of the nodes, the script can change NLB weights and/or
'      stop NLB on the affected nodes.
'    - Work on a localized computer.
' 
'''''''''''''''''''''''''''''''''''

' Variables used throughout the script
Dim strUserName,strPassword
Dim objNlb
Dim strServer,strClusterVip
Dim strService
Dim strURLFile


' Parse the command line.
call ParseCommand()
' Display a warning if only the VIP is provided.
call IsVipOnly()

wscript.echo "Using server IP to query for nodes: " & strServer
wscript.echo "Using primary cluster IP: "           & strClusterVip
wscript.echo

' Perform the NT service check.
if strService <> "" then 
  call CheckServiceAndControlNLB(strService , "Stopped")
end if

' Perform the Web GET request check.
if strURLFile <> "" then 
  call CheckWebURL(strServer,strClusterVip,strURLFile)
end if

wscript.quit



Function IsVipOnly()
'
' Check if a VIP was the only connection IP passed.
' If it is, set strClusterVip to strServr.
'
  if strServer = "" then 
    wscript.echo "WARNING: Only the VIP was passed."
    wscript.echo "Using the VIP to manage cluster may result "_
               & "in errors with connecting to individual nodes.  "
    wscript.echo "If you encounter any error, you should specify a "_
               & "host name or host IP to connect to."
    wscript.echo
    wscript.echo "Attempt the initial connection using the VIP."
    IsVipOnly = (strClusterVip = "")
    strServer = strClusterVip
    exit Function
  end if
end Function


Function CheckServiceAndControlNLB( byVal strService, _
                                    byVal strServiceState)
'
' Connects to specified node to get other nodes in cluster and then 
' queries for state on each NLB node and sets NLB state as appropriate.
'
  Dim objCimv2
  Dim objNodes1,objNLBNode

  if ( ConnectProvEx("root\microsoftnlb", strUserName, strPassword, strServer, objNlb, true) ) then 
    wscript.echo "Unable to connect to NLB node."
    CheckServiceAndControlNLB = -1
    exit Function
  end if
    
  if ( ConnectProvEx("root\cimv2", strUserName, strPassword, strServer, objCimv2, true) ) then 
    wscript.echo "Unable to connect to cimv2 namespace."
    CheckServiceAndControlNLB = -1
    exit Function
  end if

  if (GetClusterMembers(objNlb,strClusterVip ,objNodes1)<>0) then 
    CheckServiceAndControlNLB = -1
    exit Function
  end if

  for each objInst in objNodes1
    szName = objInst.name
    szRelPath = objInst.path_.Relpath
    szComputername =  objInst.Computername
    wscript.echo "#### Attempting to connect to server "    _
               & szComputername & " to check that service " _
               & strService & " is " & strServiceState & " ######"
    call ConnectProvEx( "root\microsoftnlb", strUserName, strPassword, szComputername, objNLBNode, true )
    call ConnectProvEx("root\cimv2", strUserName, strPassword, szComputername, objCimv2, true )
    wscript.echo "Connected to " & szComputername
    szActualState = ""
    if ( GetStateOfServiceOnNode( objCimv2, strService, strServiceState, szActualState ) )  then
      wscript.echo("!!!! Attempt to stop NLB on node: " & szComputername)
      call NodeControl(objNLBNode,szRelPath, "stop")   
      wscript.echo
    end if
  next
end Function


Function CheckWebURL(byVal strServer, strClusterVip, byVal strURLFile) 
'
' Connects to specified node to get other nodes in cluster and then 
' runs a simple HTTP test and sets NLB state as appropriate.
'
  Dim strURL
  Dim objNodes1,objNLBNode

  if (ConnectProvEx( "root\microsoftnlb", strUserName, strPassword, strServer, objNlb, true ) ) then 
    wscript.echo "Unable to connect to NLB node."
    exit Function
  end if

  if (GetClusterMembers(objNlb,strClusterVip ,objNodes1) <> 0) then 
      exit Function
  end if

  for each objInst in objNodes1
    szRelPath = objInst.path_.Relpath
    szComputername =  objInst.Computername
    wscript.echo "#### Attempting to get URL " & strURL & " ######"
    strURL= "http://" & szComputername & "/" & strURLFile
    if CheckWeb(strURL) = False then
      wscript.echo ("Page is unavailable")
      wscript.echo("!!!! Attempt to stop NLB on node: "&amp; szComputername)
      if ConnectProvEx("root\microsoftnlb", strUserName, strPassword, szComputername, objNLBNode, true ) <> 0 then 
        wscript.echo "!!!!" & szRelPath
        call NodeControl(objNLBNode,szRelPath, "stop")   
      end if
    end if
  next
end Function


Function GetClusterMembers( byRef objNLBNode, _
                            byVal szVip,      _
                            byRef objMembers)
'
' Enumerate the cluster members given the VIP.
'
  on error resume next
  szQueryString = "select * from MicrosoftNLB_Node where name like '%" & szVip & "%'"
  set objMembers = objNLBNode.ExecQuery(szQueryString)

  if (Err.Number) then
    Wscript.echo "Failed to get cluster members for " & szVip & ": " & err.description
    GetClusterMembers = err.number
  else                
    if objMembers.count = 0 then 
      wscript.echo "NLB is not bound. Make sure that you are using the primary cluster IP address."
      wscript.echo "Using any other VIP will fail to find the cluster instance."
      GetClusterMembers = 1
    else
      GetClusterMembers = 0
    end if
  end if
end function


Function NodeControl(byRef objNLBNode, byVal szPath, byVal szAction)
'
' Control NLB cluster node state. 
' Action is one of: stop, start, suspend, resume.
'
  on error resume next
  wscript.echo "Attempt to get NLB instance: " & szPath
  Set objNode = objNLBNode.Get(szPath)
  if Err.Number <> 0 then
    Wscript.echo "Failed to get NLB instance " & WMIClass & ": " & err.description
  else                
    Set objOutParam = objNode.ExecMethod_(szAction)
    wscript.echo("Execute method returned:")
    call DisplayReturnValue(objOutParam.ReturnValue)
  end if
end function


sub DisplayReturnValue(ReturnValue)
'
' Translate the return value to a string and display it to output.
'
  select case ReturnValue
    case 1000    
      wscript.echo "    Success."
    case 1001    
      wscript.echo "    Cluster mode is already stopped/started, or traffic handling is already" _
                 & " enabled/disabled on specified port."
    case 1002    
      wscript.echo "    Cluster mode stop or start operation interrupted connection draining process."
    case 1003    
      wscript.echo "    Cluster mode could not be started due to configuration problems on the target host."
    case 1004    
      wscript.echo "    Port number not found among port rules."
    case 1005    
      wscript.echo "    Cluster mode is stopped on the host."
    case 1006    
      wscript.echo "    Cluster is converging."
    case 1007    
      wscript.echo "    Cluster or host converged successfully."
    case 1008    
      wscript.echo "    Host is converged as default host."
    case 1009    
      wscript.echo "    Host is draining after drainstop command."
    case else:
      wscript.echo "    Unknown return (" & ReturnValue & ")."
  end select
End Sub


Function Ping(ByVal szIP)
'
' Ping an remote machine using \root\cimv2:Win32_PingStatus
'
  strComputer = "."
  Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
  Set colPings = objWMIService.ExecQuery( "Select * From Win32_PingStatus where Address = '" _
                                         & szIP & "'")

  for Each objStatus in colPings
    if IsNull(objStatus.StatusCode) or objStatus.StatusCode<>0 then
      Ping = false 
      WScript.Echo "Computer did not respond." 
    else
      Ping = true
      Wscript.Echo "Computer responded."
    end If
  next
End Function


Function CheckWeb(byVal strURL)
'
' Validates that a specified Web page is accessible by running an 
' HTTP GET request.
'
  on error resume next
  With CreateObject("MSXML2.XMLHTTP")
    .open "GET", strURL, False
    wscript.echo "Attempt to get: " & strURL
    .send
    if .status <> 200 then
      CheckWeb = false
      wscript.echo "HTTP GET returned " & .status
    else
      CheckWeb = true
      Wscript.echo "GET sucess. Page content as follows:"
      WScript.Echo .responseText
    end if
  End With
end function


Function ConnectProvEX( ByVal strNameSpace, _
                        ByVal strUserName,  _
                        ByVal strPassword,  _
                        ByRef strServer,    _
                        ByRef objService,   _
                        ByVal LOAD_DRIVE)
'
' Connect to WMI provider name space.
'
  on error resume next
  Dim objLocator, objWshNet
  ConnectProvEx = False     'There is no error.  

  'Create Locator object to connect to remote CIM object manager.
  Set objLocator = CreateObject("WbemScripting.SWbemLocator")

  if LOAD_DRIVE <> False then
    objLocator.Security_.Privileges.AddAsString "SeLoadDriverPrivilege", True 
  end if

  if Err.Number then
    wscript.echo("    Error 0x" & CStr(Hex(Err.Number)) & _
                 " occurred in creating a locator object." )
    if Err.Description <> "" then
      wscript.echo("    Error description: " & Err.Description )
    end if
    ConnectProvEx = err.number     'An error occurred
    Err.Clear
    Exit Function
  end if

  Set objService = objLocator.ConnectServer( strServer,    _
                                             strNameSpace, _
                                             strUserName,  _
                                             strPassword)
  if err = -2147023174 then 'HRESULT_FROM_WIN32(RPC_S_SERVER_UNAVAILABLE)
    wscript.echo err.Description 
    wscript.echo "Verify that server name or IP address is correct for " _
               & strServer 
    ConnectProvEx = err.number
    exit function
  end if

    ObjService.Security_.impersonationlevel = 3
    if Err.Number then
      wscript.echo("    Error 0x" & CStr(Hex(Err.Number)) 
                 & " occurred in connecting to server " & strServer & ".")
      if Err.Description <> "" then
        wscript.echo("    Error description: " & Err.Description )
      end if
      Err.Clear
      ConnectProvEx = err.number
  end if
End Function


Function GetStateOfServiceOnNode( byRef objCimvServer, _
                                  byVal strService,    _
                                  byVal chkState,      _
                                  byRef isState)
'
' Query NT service for a given state and return the state.
'
  szQueryString = "Select * from Win32_Service where name = '" & strService & "'"
  Set objService = objCimvServer.ExecQuery(szQueryString)
  if objService.count = 0 then 
    wscript.echo "Service " &  strService & " is not installed"
    GetStateOfServiceOnNode = -1
    exit function
  end if
  for each objInst in objService
    isState = objInst.State
    wscript.echo "Service " &  strService & " is " & isState
    if chkState = isState then 
      GetStateOfServiceOnNode = true
    end if 
  next
end Function


Function ParseCommand()
'
' Parses the command line and fills the script variables 
' with the appropriate values.
'
  Dim ArgCount
  Dim oArgs

  Set oArgs = Wscript.Arguments

  ArgCount = 0
  if oArgs.Count = 0 then
    wscript.echo "No arguments specified."
    wscript.echo
    call Help()
  end if


  While ArgCount < oArgs.Count
    Select Case LCase(oArgs(ArgCount))
      Case "-u"
        ArgCount = ArgCount + 1
        strUserName=LCase(oArgs(ArgCount))
      Case "-p"
        ArgCount = ArgCount + 1
        strPassword=oArgs(ArgCount)
      Case "-s"
        ArgCount = ArgCount + 1
        strService= LCase(oArgs(ArgCount))
      Case "-f"
        ArgCount = ArgCount + 1
        strURLFile = LCase(oArgs(ArgCount))
      Case "-n"
        ArgCount = ArgCount + 1
        strServer= LCase(oArgs(ArgCount))
      Case "-v"
        ArgCount = ArgCount + 1
        strClusterVip= LCase(oArgs(ArgCount))
      Case Else:
        wscript.echo "Invalid command."
        wscript.echo
        call Help()
        wscript.quit
    End Select
    ArgCount = ArgCount + 1
  Wend
End Function


sub Help()
'
' Display command-line syntax for the script.
'
  wscript.echo "Network Load Balancing (NLB) service monitor tool"
  wscript.echo "Checks any Windows service state and stops NLB hosts if that service is stopped."
  wscript.echo "Service Monitor command Line help"
  wscript.echo
  wscript.echo "Syntax:"
  wscript.echo
  wscript.echo "-u User name"
  wscript.echo "-p Password"
  wscript.echo "-n Server name or IP"
  wscript.echo "-s Service name to monitor"
  wscript.echo "-v Primary cluster IP"
  wscript.echo "-f File to HTTP GET"
  wscript.echo
  wscript.echo "Examples:"
  wscript.echo
  wscript.echo "nlbmon.vbs -n 2.77.2.2 -v 2.77.22.200 -s w3svc"
  wscript.echo "nlbmon.vbs -v 2.77.22.200 -s w3svc"
  wscript.echo "nlbmon.vbs -v 2.77.22.200 -s w3svc -f whoami.txt"
  wscript.echo "nlbmon.vbs -n 2.77.2.2 -v 2.77.22.200 -f whoami.txt"
  wscript.quit
End Sub

Using the Network Load Balancing Provider

MicrosoftNLB_Node

Stop Method of the MicrosoftNLB_Node Class