The Gist Of The Solution
The proposed solution starts with making slight modifications to the sample load balancing script, cpuutilization.vbs, provided by VMware. Normally this script determines the load balancing priority of a given RDS host based on cpu utilization. Sticking with the basic structure of this script we can further refine the process for determining load balancing priority to include vROPs information about the underlying infrastructure. For this demo in particular, the Health, Workload and Capacity_Remaining badges, or super metrics, of the underlying ESXi hosts will be utilized.
To keep requirements simple on the RDSH VMs themselves, information from vROPs will be relayed to the modified cpuutillization.vbs script via the existence of text files in a remote share. These text files will be put into place when a PowerCLI script running from an administrative system determines that at vSphere hosts associated with a particular RDSH VM has potential health or capacity issues. The cpuutilizlation.vbs will in turn lower the load balancing priority of RDSH VM based on the detection of the text file.
The modified cpuutillization.vbs script can be found here at the bottom of this post. The PowerCLI script that detects problems with underlying ESXi hosts and creates text files accordingly can be found at the bottom of this post as well. To use these in your own environment replace the server names, user names and passwords with those from your own environment and give it a go. If you want a more in depth explanation on how the process works, just continue reading.
Creating Your Own Load Balancing Script
Official guidance for load balancing using customized scripts can be found here: Configuring Load Balancing For RDS Hosts Sample scripts can be located within the view agent directory on the RDS hosts here: c:\Program Files\VMware\VMware View\Agent\scripts. The basic idea behind these sample scripts, or any custom script you make, is that they produce an exit code from 0 - 3 after running directly on the RDS hosts. These scripts run on a regular basis - every 5 minutes by default - and their exit codes map out to higher or lower load preferences for Horizon Connections severs to use when deciding where to place new sessions. An exit code of 3 means an RDS host should be given higher preference priority for taking on new sesssions. An exit code of 0 means an RDS host should not be given any new sessions at all. Here's a breakdown of the different exit codes and associated load preferences:

With the sample scripts cpuutilization.vbs and memoryutilization.vbs, higher or lower priority is provided to a RDS host based on cpu or memory usage. For example, with the cpuutilization.vbs script, hosts with CPU utilization of 90% or higher are given a load preference of block, with an exit code of 0, while host with low CPU utilization under 25% are given high preference with an exit code of 3.

For this demo, the cpuutilization.vbs has been modified to adjust load preference according to both CPU utilization and any health or capacity warnings obtained from vROPs. Essentially, when there's issues reported by vROPs we're taking the normal exit codes of this script and decrementing by 1. So if the script would normally spit out an exit code of 3 based on CPU utilization, but currently has issues according to vROPs, the exit code is going to be decremented to 2. Or if a system already had low preference with an exit code of 1 because of high CPU usage, it will drop down to a load preference of block with an exit code of 0 if issues are detected through vROPs.
The PowerCLI Script
The PowerCLI script works through a combination of vSphere PowerCLI, Horizon PowerCLI and the PowerCLI module for vROPs. Initially, Horizon PowerCLI is used determine the RDS hosts that are members of the specified RDSH farm. (This is a process I detailed in a previous blog posts that can be viewed here) For each RDSH VM in the farm vSphere PowerCLI is used to obtain the name of the vSphere host the VM is running on. Finally, the PowerCLI module for vROPs is leveraged to obtain super metrics about the ESXi host.
To initially connect to the vROPs manager, we do this:
Connect-OMServer YOUR_VROPS_SERVER -user admin -Password YOUR_PASSWORD
Then, with the name of the ESXi host in hand we can use the Get-OMResource cmdlet to obtain relevent info about the ESXi box. Here's an example of how to do that.
The object returned by Get-OMResource is chock-full of vROPs information regarding the ESXi host. Here's an example of retrieving badge information about the vSphere host using the returned object stored in $EsxiHealth.

For our demo load balancing script we're going to zero in on the Health, Capacity_Remaining and Workload badge. If Health isn't green or Capacity_Remaining or Workload are red a text file is created in the remote share indicating the RDSH VM is sitting on a ESXi box that's suspect.

The RDSH VM in question, InstaApp1, is barely using any CPU and would normally report an exit code of 3 and high load preference. However, the modified script detects the existence of this text file and accordingly returns an exit code of 2 and load preference of medium. In Horizon Administrator, we can see that as a result the load balance status goes from this:

To this:

The modified cpuutilization.vbs script:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
' | |
' (c) VMware 2015 | |
' | |
' This load script is for use on RDSH machines to generate a Load Preference value which will be reported back | |
' to Connection Brokers. Brokers will then use the reported value to determine which of the RDSH machines will be | |
' used for creating new Application sessions. | |
' | |
' This script will return a | |
' RC of 0 ( mapped to a preference of BLOCK) if cpu utilisation is > 90% | |
' RC of 1 ( mapped to a preference of LOW) if cpu utilisation is > 75% | |
' RC of 2 ( mapped to a preference of MED) if cpu utilisation is > 25% | |
' RC of 3 ( mapped to a preference of HIGH) if cpu utilisation is <= 25% | |
' | |
' This script will return return codes >= 100 for execution errors | |
' | |
'Option Explicit | |
sharePath = “\\YOUR_SERVER\YOUR_SHARE_NAME\” | |
Const performanceStatsRegKey = "HKLM\SOFTWARE\VMware, Inc.\VMware VDM\Performance Stats" | |
' Read the % of cpu that's been utilised from the registry | |
' | |
Dim strCpuPercentage : strCpuPercentage = ReadRegString(performanceStatsRegKey & "\CpuPercentage") | |
' Allow for registry value being missing between startup and the end of the first sampling interval | |
If isNull(strCpuPercentage) Then | |
WScript.Echo "error reading CpuPercentage from registry" | |
WScript.Quit(2) | |
End If | |
Dim intCpuPercentage : intCpuPercentage = CInt(strCpuPercentage) | |
If intCpuPercentage < 0 Or intCpuPercentage > 100 Then | |
WScript.Echo "Invalid CpuPercentage" | |
WScript.Quit(100) | |
End If | |
' Console output for testing | |
' | |
' Wscript.Echo "intCpuPercentage=" & intCpuPercentage | |
' Return an appropriate error code based on the % | |
' | |
If intCpuPercentage > 90 Then | |
WScript.Quit(0) | |
ElseIf intCpuPercentage > 75 Then | |
If CurrentVROPSAlert(sharePath) Then | |
WScript.Quit(0) | |
Else | |
WScript.Quit(1) | |
End If | |
ElseIf intCpuPercentage > 25 Then | |
If CurrentVROPSAlert(sharePath) Then | |
WScript.Quit(1) | |
Else | |
WScript.Quit(2) | |
End If | |
Else | |
If CurrentVROPSAlert(sharePath) Then | |
WScript.Quit(2) | |
Else | |
WScript.Quit(3) | |
End If | |
End If | |
Function ReadRegString(strRegKey) | |
On Error Resume Next | |
Dim wshell : Set wshell = wscript.CreateObject("WScript.Shell") | |
ReadRegString = wshell.RegRead(strRegKey) | |
If Err.Number <> 0 Then | |
ReadRegString = Null | |
End If | |
End Function | |
Function CurrentVROPSAlert(share) | |
Set wshNetwork = WScript.CreateObject( "WScript.Network" ) | |
strComputerName = wshNetwork.ComputerName | |
DaPath = share & wshNetwork.ComputerName & "_VROPS_ALERT.txt" | |
Set fso = CreateObject("Scripting.FileSystemObject") | |
If (fso.FileExists(DaPath)) Then | |
CurrentVROPSAlert = True | |
Else | |
CurrentVROPSAlert = False | |
End If | |
End Function |
The PowerShell script:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Insure requried modules are imported | |
Import-Module VMware.VimAutomation.HorizonView | |
Import-Module VMware.VimAutomation.Core | |
Import-Module VMware.VimAutomation.Vrops | |
# Variables for the name of Instant Clone Farm and connection server. | |
$farmName = “FARM_NAME” | |
$connectionServer = “CONNECTION_SERVER” | |
$sharePath = “\\SERVER_NAME\SHARE\” | |
#Connect to vROPs and vCenter server | |
$noNosie = Connect-OMServer VROPS_SERVER -user USER_NAME -Password YOUR_PASSWORD | |
$noNoise = Connect-VIServer -server VCENTER_SERVER -User YOUR_USER_NAME -Password YOUR_PASSWORD | |
#Connect to View API and assign extension data to variable | |
$noNoise = Connect-HVServer -server $connectionServer -user USER_NAME -password YOUR_PASSWORD -Domain YOUR_DOMAIN | |
$Services1=$Global:DefaultHVServers.ExtensionData | |
# Get host names from Instant Farm using FarmHealth service and FarmHealth_Get method. | |
#This method requires a Farm ID, which we obtain with Get-HVSummary | |
$MySummary = Get-HVFarmSummary $farmName | |
$FarmHealth = New-Object VMware.Hv.FarmHealthService | |
$TestFarmHealth = $FarmHealth.FarmHealth_Get($Services1, $MySummary.id) | |
# Walks through list of RDS hosts, using their name property to look up their AD objects. | |
ForEach ( $RDSHHost in ($TestFarmHealth.RdsServerHealth)) | |
{ | |
#Obtain vSphere host for RDSH | |
$DaVM = Get-VM $RDSHHost.Name | |
#Obtain vROPs info on ESXi host using PowerCLI Module for vROPs | |
$DaHealth = Get-OMResource $DaVM.VMHost.Name | |
#Obtain health status for 3 of the most relevant badges | |
$WorkLoad = $DaHealth.ExtensionData.Badges | Where { $_.Type -eq "WORKLOAD" } | |
$Health = $DaHealth.ExtensionData.Badges | Where { $_.Type -eq "HEALTH" } | |
$CainpacityRemaing = $DaHealth.ExtensionData.Badges | Where { $_.Type -eq "CAPACITY_REMAINING" } | |
$hostname = hostname | |
$filePath = $sharePath + $RDSHHost.Name + "_VROPS_ALERT.txt" | |
If ( Test-Path $filePath ) { | |
del $filePath | |
} | |
If ($WorkLoad.Color -eq "RED") { | |
echo "Workload is red" >> $filePath | |
} | |
If ($Health.Color -ne "GREEN") { | |
echo "Health is not green" >> $filePath | |
} | |
If ($CainpacityRemaing.Color -eq "RED") { | |
echo "Capapcity remaining is red" >> $filePath | |
} | |
} |
Nice guide, happy to find it
ReplyDelete