Skip to content

Instantly share code, notes, and snippets.

@jwcarroll
Created June 5, 2012 16:08
Show Gist options
  • Save jwcarroll/2875967 to your computer and use it in GitHub Desktop.
Save jwcarroll/2875967 to your computer and use it in GitHub Desktop.
A set of functions that can be used to automate a cluster using NLB
# Name: Network Load Balancer Functions
# Description: Collection of functions for managing NLB on 2003 machines
# Author: Josh Carroll
Add-Type @'
public class NLBStatusCode
{
public NLBStatusCode(int code, string name, System.ConsoleColor color){
_code = code;
_name = name;
_color = color;
}
private int _code;
public int Code {
get { return _code; }
}
private string _name;
public string Name {
get { return _name; }
}
private System.ConsoleColor _color;
public System.ConsoleColor Color {
get { return _color; }
}
public override System.String ToString() {
return System.String.Format("{0} ({1})", Name,Code);
}
}
'@
<#
.SYNOPSIS
Retrieves a single Node on an NLB Cluster by Machine/IP
.DESCRIPTION
This function queries a machine uwing WMI in order to retrieve a single Node object.
.PARAMETER nodeIp
The dedicated IP address of the Node in the NLB cluster
.PARAMETER computerName
Optional parameter to specify the machine name where the Node lives. Defaults to running computer name.
.PARAMETER credentials
Optional PSCredentials Object to use when making the WMI call to a remote machine. By default the current users credentials are used in the query.
.EXAMPLE
GetNode "10.1.1.100"
Description
-----------
Returns the node with dedicated IP of "10.1.1.100" on local machine
.EXAMPLE
GetNode "10.1.1.100" "server1"
Description
-----------
Returns the node with dedicated IP of "10.1.1.100" on remote machine using executing users credentials.
.EXAMPLE
$devpass = ConvertTo-SecureString "MyPassword" -AsPlainText -Force
C:\PS> $devlogin = new-object -typename System.Management.Automation.PSCredential -argumentlist "devuser@domain.net",$devpass
C:\PS> GetNode "10.1.1.100" "server1" $devlogin
Description
-----------
Returns the node with dedicated IP of "10.1.1.100" on remote machine using specified credentials
.EXAMPLE
$devpass = ConvertTo-SecureString "MyPassword" -AsPlainText -Force
C:\PS> $devlogin = new-object -typename System.Management.Automation.PSCredential -argumentlist "devuser@domain.net",$devpass
C:\PS> $node = GetNode "10.1.1.100" "server1" $devlogin
C:\PS> $node.DrainStop()
Description
-----------
Returns a node and then drainstops it.
.LINK
GetAllNodes
DrainStopAll
DrainStopAllAndWait
StartAll
StartAllAndWait
WaitForStatus
GetNodeStatus
#>
Function GetNode
{
Param([string]$nodeIp, [string]$computerName=$env:computername, $credentials="")
if($credentials -eq ""){
get-wmiobject -class MicrosoftNLB_Node -namespace "root/MicrosoftNLB" -computer $computerName | where-object {$_.DedicatedIPAddress -eq $nodeIp}
}
else{
get-wmiobject -class MicrosoftNLB_Node -namespace "root/MicrosoftNLB" -computer $computerName -credential $credentials -authentication 6 | where-object {$_.DedicatedIPAddress -eq $nodeIp}
}
}
<#
.SYNOPSIS
Gets all nodes that are associated with a single machine
.DESCRIPTION
Given a computer name, a WMI query will be performed which returns all NLB Nodes associated with that machine.
.PARAMETER computerName
Optional parameter to specify the machine name where the Node lives. Defaults to running computer name.
.PARAMETER credentials
Optional PSCredentials Object to use when making the WMI call to a remote machine. By default the current users credentials are used in the query.
.EXAMPLE
GetAllNodes server1
Description
-----------
Returns all available nodes associated with a remote machine using currently logged in users credentials.
.EXAMPLE
$devpass = ConvertTo-SecureString "MyPassword" -AsPlainText -Force
C:\PS>$devlogin = new-object -typename System.Management.Automation.PSCredential -argumentlist "devuser@domain.net",$devpass
C:\PS>GetAllNodes server1 $devlogin
Description
-----------
Returns all available nodes associated with a remote machine using provided credentials.
.LINK
GetNode
DrainStopAll
DrainStopAllAndWait
StartAll
StartAllAndWait
WaitForStatus
GetNodeStatus
#>
Function GetAllNodes
{
Param([string]$computerName=$env:computername, $credentials="")
if($credentials -eq ""){
get-wmiobject -class MicrosoftNLB_Node -namespace "root/MicrosoftNLB" -computer $computerName -ErrorAction SilentlyContinue
}
else{
get-wmiobject -class MicrosoftNLB_Node -namespace "root/MicrosoftNLB" -computer $computerName -credential $credentials -authentication 6 -ErrorAction SilentlyContinue
}
}
Function GetNodeList
{
Param($credentials="", [string[]]$computerNames)
$nodeList = New-Object 'System.Collections.Generic.List[object]'
foreach($computerName in $computerNames){
$nodes = GetAllNodes $computerName $credentials
if(!$nodes){
$nodes = @((CreateUndefinedNode $computerName))
}
foreach($node in $nodes){
$nodeList.Add($node)
}
}
$nodeList
}
<#
.SYNOPSIS
Performs a drainstop operation on every node on specified machine(s).
.DESCRIPTION
This operation will query each machine specified and issue a drainstop command for each node found. It does not wait for that operation to complete.
.PARAMETER credentials
Optional PSCredentials Object to use when making the WMI call to a remote machine. By default the current users credentials are used in the query.
.PARAMETER computerNames
An array of computer names that will be queried for their associated NLB Nodes.
.EXAMPLE
DrainStopAll -computerNames ("server1","server2")
Description
-----------
Issues a drainstop command for all nodes on "server1" and "server2" using currently logged in users credentials
.LINK
GetNode
GetAllNodes
DrainStopAllAndWait
StartAll
StartAllAndWait
WaitForStatus
GetNodeStatus
#>
Function DrainStopAll
{
Param($credentials="", [string[]]$computerNames)
foreach($computerName in $computerNames){
$nodes = GetAllNodes $computerName $credentials
foreach($node in $nodes){
$ip = ($node).DedicatedIPAddress
$name = ($node).ComputerName
$output = "Drainstopping: {0,-15} {1,-30}" -f $ip, $name
Write-Host $output
($node).DrainStop()
}
}
}
<#
.SYNOPSIS
Performs a drainstop operation on every node on specified machine(s), and waits for the operation to complete successfully.
.DESCRIPTION
This operation will query each machine specified and issue a drainstop command for each node found.
It will poll on a regular interval to see if the operation has completed successfully.
A timeout can be specified to exit the function if the nodes have not reached their desired state within that timeframe.
.PARAMETER refreshRate
The amount of time, in seconds, between checking to see if all nodes have reached the "Stopped" state. Default is 30 seconds.
.PARAMETER timeoutSeconds
The maximum amount of time in seconds to before polling ceases and the function exits. Default is 5 minutes.
.PARAMETER forceStopOnTimeout
Optional parameter that will issue a stop command if nodes have not stopped when the timeout is reached.
.PARAMETER credentials
Optional PSCredentials Object to use when making the WMI call to a remote machine. By default the current users credentials are used in the query.
.PARAMETER computerNames
An array of computer names that will be queried for their associated NLB Nodes.
.EXAMPLE
DrainStopAllAndWait -refreshRate 10 -timeoutSeconds 1200 -computerNames ("server1","server2")
Description
-----------
Issues a drainstop command for all nodes on "server1" and "server2" using currently logged in users credentials.
This will continue to poll every 10 seconds to see if nodes have stopped, and exit after 20 minutes.
.LINK
GetNode
GetAllNodes
DrainStopAll
StartAll
StartAllAndWait
WaitForStatus
GetNodeStatus
#>
Function DrainStopAllAndWait
{
Param([int]$refreshRate=30, [int]$timeoutSeconds=300, [bool]$forceStopOnTimeout=$false, $credentials="", [string[]]$computerNames)
DrainStopAll $credentials $computerNames
Write-Host
WaitForStatus "Stopped" $refreshRate $timeoutSeconds $credentials $computerNames
if($forceStopOnTimeout){
$nodeList = GetNodeList $credentials $computerNames
$nodesNotInTargetStatus = FilterNodesByStatus $nodeList ("Stopped")
if($nodesNotInTargetStatus){
Write-Host "Forcing Stop..."
Write-Host
WriteNodeStatus -writeHeader $true
foreach($node in $nodesNotInTargetStatus){
($node).Stop()
WriteNodeStatus $node
}
}
}
}
<#
.SYNOPSIS
Allows a caller to wait for all nodes on a set of computers to reach a specific status.
For a list of available statuses use:
GetNLBStatusCode -list $true
.DESCRIPTION
This operation will query each machine specified and wait for a specific status.
It will poll on a regular interval to see if the operation has completed successfully.
A timeout can be specified to exit the function if the nodes have not reached their desired state within that timeframe.
.PARAMETER statusList
An array of Statuses to monitor the nodes for.
.PARAMETER refreshRate
The amount of time, in seconds, between checking to see if all nodes have reached the specified state. Default is 30 seconds.
.PARAMETER timeoutSeconds
The maximum amount of time in seconds to before polling ceases and the function exits. Default is 5 minutes.
.PARAMETER credentials
Optional PSCredentials Object to use when making the WMI call to a remote machine. By default the current users credentials are used in the query.
.PARAMETER computerNames
An array of computer names that will be queried for their associated NLB Nodes.
.EXAMPLE
WaitForStatus -statusList ("Started","Converged") -refreshRate 10 -timeoutSeconds 1200 -computerNames ("server1","server2")
Description
-----------
Issues a drainstop command for all nodes on "server1" and "server2" using currently logged in users credentials.
This will continue to poll every 10 seconds to see if nodes have stopped, and exit after 20 minutes.
.LINK
GetNode
GetAllNodes
DrainStopAll
DrainStopAllAndWait
StartAll
StartAllAndWait
GetNodeStatus
#>
Function WaitForStatus
{
Param([string[]]$statusList, [int]$refreshRate=30, [int]$timeoutSeconds=300, $credentials="", [string[]]$computerNames)
try{
if($refreshRate -lt 5){
Write-Host "Refresh rate cannot be less than 5 seconds... resetting"
$refreshRate = 5
}
$nodesNotInTargetStatus
$isTimeExpired
$sw = New-Object 'System.Diagnostics.Stopwatch'
$timeout = [System.TimeSpan]::FromSeconds($timeoutSeconds)
$formattedStatusList = [System.String]::Join(",",$statusList)
do{
$nodeList = GetNodeList $credentials $computerNames
$updateMsg = "Target Status: {0} | Refresh Rate: {1} seconds | Elapsed Time: {2:mm\\:ss}" -f $formattedStatusList,$refreshRate,$sw.Elapsed
$nodesNotInTargetStatus = FilterNodesByStatus $nodeList $statusList
$isTimeExpired = $sw.Elapsed -ge $timeout
Write-Host
Write-Host $updateMsg
Write-Host
WriteNodeStatus -writeHeader $true
foreach($node in $nodeList){
WriteNodeStatus $node
}
Write-Host
if($sw.IsRunning -and !$isTimeExpired){
Start-Sleep -s $refreshRate
}
else{
$sw.Start()
}
}while($nodesNotInTargetStatus -and !$isTimeExpired)
$sw.Stop()
if($isTimeExpired){
"Timeout of {0} seconds has been reached." -f $timeoutSeconds
}
else{
Write-Host
"All nodes have reached target status of: {0}" -f $formattedStatusList
}
Write-Host
}
catch [System.Exception]{
Write-Error $_.Exception.ToString()
}
}
Function FilterNodesByStatus
{
Param($nodeList, [string[]]$statusList)
$cmdList = New-Object 'System.Collections.Generic.List[string]'
foreach($status in $statusList){
$statusCode = (GetNLBStatusCode $status).Code
$cmdItem = "`$_.StatusCode -ne $statusCode"
$cmdList.Add($cmdItem)
}
$cmdBody = [System.String]::Join(" -and ",$cmdList)
$cmd = "`$nodeList | where {{{0}}}" -f $cmdBody
$ExecutionContext.InvokeCommand.InvokeScript($cmd)
}
<#
.SYNOPSIS
Performs a start operation on every node on specified machine(s).
.DESCRIPTION
This operation will query each machine specified and issue a start command for each node found. It does not wait for that operation to complete.
.PARAMETER credentials
Optional PSCredentials Object to use when making the WMI call to a remote machine. By default the current users credentials are used in the query.
.PARAMETER computerNames
An array of computer names that will be queried for their associated NLB Nodes.
.EXAMPLE
DrainStopAll -computerNames ("server1","server2")
Description
-----------
Issues a start command for all nodes on "server1" and "server2" using currently logged in users credentials
.LINK
GetNode
GetAllNodes
DrainStopAll
DrainStopAllAndWait
StartAllAndWait
WaitForStatus
GetNodeStatus
#>
Function StartAll
{
Param($credentials="", [string[]]$computerNames)
foreach($computerName in $computerNames){
$nodes = GetAllNodes $computerName $credentials
foreach($node in $nodes){
$ip = ($node).DedicatedIPAddress
$name = ($node).ComputerName
$output = "Starting: {0,-15} {1,-30}" -f $ip, $name
Write-Host $output
($node).Start()
}
}
}
<#
.SYNOPSIS
Performs a start operation on every node on specified machine(s), and waits for the operation to complete successfully.
.DESCRIPTION
This operation will query each machine specified and issue a drainstop command for each node found.
It will poll on a regular interval to see if the operation has completed successfully.
A timeout can be specified to exit the function if the nodes have not reached their desired state within that timeframe.
.PARAMETER refreshRate
The amount of time, in seconds, between checking to see if all nodes have reached the "Started" or "Converged" state. Default is 30 seconds.
.PARAMETER timeoutSeconds
The maximum amount of time in seconds to before polling ceases and the function exits. Default is 5 minutes.
.PARAMETER credentials
Optional PSCredentials Object to use when making the WMI call to a remote machine. By default the current users credentials are used in the query.
.PARAMETER computerNames
An array of computer names that will be queried for their associated NLB Nodes.
.EXAMPLE
StartAllAndWait -refreshRate 10 -timeoutSeconds 1200 -computerNames ("server1","server2")
Description
-----------
Issues a start command for all nodes on "server1" and "server2" using currently logged in users credentials.
This will continue to poll every 10 seconds to see if nodes have started, and exit after 20 minutes.
.LINK
GetNode
GetAllNodes
DrainStopAll
DrainStopAllAndWait
StartAll
StartAllAndWait
WaitForStatus
GetNodeStatus
#>
Function StartAllAndWait
{
Param([int]$refreshRate=30, [int]$timeoutSeconds=300, $credentials="", [string[]]$computerNames)
StartAll $credentials $computerNames
Write-Host
WaitForStatus ("Started","Converged") $refreshRate $timeoutSeconds $credentials $computerNames
}
<#
.SYNOPSIS
Returns the current status for all nodes on the computers specified, and displays it in a friendly format.
.DESCRIPTION
Returns the current status for all nodes on the computers specified, and displays it in a friendly format.
.PARAMETER credentials
Optional PSCredentials Object to use when making the WMI call to a remote machine. By default the current users credentials are used in the query.
.PARAMETER computerNames
An array of computer names that will be queried for their associated NLB Nodes.
.EXAMPLE
GetNodeStatus -computerNames server1,server2
IP Server Status
--------------- ------------------------------ -------------------------
10.1.1.100 server1.domain.net Converging
10.1.1.102 server2.domain.net Converged
Description
-----------
Returns each node and a snapshot of their current status displayed in a friendly format.
.LINK
GetAllNodes
DrainStopAll
DrainStopAllAndWait
StartAll
StartAllAndWait
WaitForStatus
GetNodeStatus
#>
Function GetNodeStatus
{
Param($credentials="", [string[]]$computerNames)
Write-Host
WriteNodeStatus -writeHeader $true
foreach($computerName in $computerNames){
$nodes = GetAllNodes $computerName $credentials
if(!$nodes){
$nodes = @((CreateUndefinedNode $computerName))
}
foreach($node in $nodes){
WriteNodeStatus $node
}
}
Write-Host
}
Function CreateStatusCodeMappings
{
$strCmprIgnoreCase = [System.StringComparer]::OrdinalIgnoreCase
$nlbStatusCodeMapping = New-Object -TypeName "System.Collections.Generic.Dictionary[string,System.Object]" -ArgumentList $strCmprIgnoreCase
$nlbStatusCodeMapping.Add("Undefined",(New-Object NLBStatusCode @(0,"Undefined",[ConsoleColor]::DarkGray)))
$nlbStatusCodeMapping.Add("Stopped",(New-Object NLBStatusCode @(1005,"Stopped",[ConsoleColor]::Red)))
$nlbStatusCodeMapping.Add("Converging",(New-Object NLBStatusCode @(1006,"Converging",[ConsoleColor]::Yellow)))
$nlbStatusCodeMapping.Add("Converged",(New-Object NLBStatusCode @(1007,"Converged",[ConsoleColor]::DarkGreen)))
$nlbStatusCodeMapping.Add("Started",(New-Object NLBStatusCode @(1008,"Started",[ConsoleColor]::Green)))
$nlbStatusCodeMapping.Add("Draining",(New-Object NLBStatusCode @(1009,"Draining",[ConsoleColor]::Yellow)))
$nlbStatusCodeMapping.Add("Suspended",(New-Object NLBStatusCode @(1013,"Suspended",[ConsoleColor]::Gray)))
$nlbStatusCodeMapping
}
Function CreateUndefinedNode()
{
Param([string]$serverName)
New-Object PSObject -Property @{
StatusCode = 0;
DedicatedIPAddress = "0.0.0.0";
ComputerName = $serverName
}
}
Function WriteNodeStatus()
{
Param($node, [bool]$writeHeader=$false)
if($writeHeader){
"{0,-15} {1,-30} {2,-25}" -f "IP","Server","Status"
"{0,-15} {1,-30} {2,-25}" -f (RepeatString "-" 15),(RepeatString "-" 30),(RepeatString "-" 25)
}
if($node){
$status = TranslateNodeStatusCode ($node).StatusCode
$ip = ($node).DedicatedIPAddress
$name = ($node).ComputerName
Write-Host ("{0,-15} {1,-30} " -f $ip, $name) -NoNewLine
Write-Host ("{0,-25}" -f $status.Name) -ForegroundColor $status.Color
}
}
Function RepeatString()
{
Param([string]$str="",[int]$count=1)
new-object -TypeName System.String -ArgumentList $str,$count
}
Function TranslateNodeStatusCode
{
Param([int]$statusCode)
$codesMap = $nlbStatusCodeMapping
switch($statusCode){
0 { $codesMap["Undefined"] }
1005 { $codesMap["Stopped"] }
1006 { $codesMap["Converging"] }
1007 { $codesMap["Converged"] }
1008 { $codesMap["Started"] }
1009 { $codesMap["Draining"] }
1013 { $codesMap["Suspended"] }
}
}
Function GetNLBStatusCode
{
Param([string]$statusCode, [boolean]$list=$false)
$codesMap = $nlbStatusCodeMapping
if($list){
""
"{0,-15} {1,-4}" -f "Status","Code"
"{0,-15} {1,-4}" -f (RepeatString "-" 15),(RepeatString "-" 4)
foreach($key in $codesMap.Keys){
"{0,-15} {1,-4}" -f $key,$codesMap[$key]
}
""
}
else{
if($codesMap.ContainsKey($statusCode)){
$codesMap[$statusCode]
}
else{
$codesMap["Undefined"]
}
}
}
$nlbStatusCodeMapping = CreateStatusCodeMappings
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment