Change Admission Control Percentages
I was recently asked by a customer to explain some of the vSphere 6.5 features that would be interesting for his environment.
This post is from 2018. It stays online because people still look for it, but it describes the products as they were then.
I was recently asked by a customer to explain some of the vSphere 6.5 features that would be interesting for his environment. I knew the environment so I pointed out some things that 6.5 does better than the currently installed 6.0. One of those things I mentioned was the automated adjustment of the resource percentage in the HA admission control settings.
For those of you who don’t know: vSphere 6.5 does automatically change the resource percentage when you add or remove hosts to a cluster. For example: You have a 4-Node cluster and set the HA admission control setting to cluster resource percentage and you want to tolerate 1 host failure. vSphere sets the percentages to 25% for CPU and Memory. If you now go ahead and add one host those settings are automatically updated to 20% memory and cpu.
Anyways, the customer wanted that feature because he does that quite often (adding/removing hosts) but he could not update to 6.5 for a couple of reasons (Hardware compatibility and 3rd party software that integrates into vCenter, etc). So he asked me if there was a way to get that feature without updating. My response was: “Everything is possible with PowerCLI”.
So, long story, here is the script to backport this 6.5 feature to 6.0. Now, you’d have to call this script every time a change of hosts has occurred in your clusters, or you could let it run twice a day or something (still not as elegant as the 6.5 feature, but close enough).
Update: this script is now published at GitHub.
Oh, almost forgot: This script uses the PSLogging Module that is available from the powershellgallery.
#---------------------------------------------------------[Script Parameters]------------------------------------------------------
Param (
#Script parameters go here
[Parameter(Mandatory = $true)][string]$vCenter,
[Parameter(Mandatory = $true)][string]$cluster,
[Parameter(Mandatory = $true)][string]$failuresToTolerate
)
#---------------------------------------------------------[Initialisations]--------------------------------------------------------
#Set Error Action to Silently Continue
$ErrorActionPreference = 'SilentlyContinue'
#Import Modules & Snap-ins
Import-Module PSLogging
#----------------------------------------------------------[Declarations]----------------------------------------------------------
#Script Version
$sScriptVersion = '1.0'
#Log File Info
$sLogPath = 'C:\Temp'
$sLogName = 'HA_AdmissionControlLog.log'
$sLogFile = Join-Path -Path $sLogPath -ChildPath $sLogName
#-----------------------------------------------------------[Functions]------------------------------------------------------------
Function Connect-VMwareServer {
Param ([Parameter(Mandatory = $true)][string]$VMServer)
Begin {
Write-LogInfo -LogPath $sLogFile -Message "Connecting to vCenter Server [$VMServer]..."
}
Process {
Try {
$oCred = Get-Credential -Message 'Enter credentials to connect to vCenter Server'
Connect-VIServer -Server $VMServer -Credential $oCred -ErrorAction Stop
}
Catch {
Write-LogError -LogPath $sLogFile -Message $_.Exception -ExitGracefully
Break
}
}
End {
If ($?) {
Write-LogInfo -LogPath $sLogFile -Message 'Completed Successfully.'
Write-LogInfo -LogPath $sLogFile -Message ' '
}
}
}
Function checkRAMCompliance {
Param ([Parameter(Mandatory = $true)][string]$clusterName)
Begin {
Write-LogInfo -LogPath $sLogFile -Message "Checking if all hosts in cluster [$clusterName] have the same amount of RAM..."
$myHostsRAM = @()
}
Process {
Try {
$clusterObject = Get-Cluster -Name $clusterName -ErrorAction Stop
$hostObjects = $clusterObject | Get-VMHost -ErrorAction Stop
foreach ($hostObject in $hostObjects) {
$HostInfoMEM = "" | Select-Object MEM
$HostInfoMEM.MEM = $hostObject.MemoryTotalGB
$myHostsRAM += $HostInfoMEM
}
#Check if all values are correct: return true, otherwise return false
if (@($myHostsRAM | Select-Object -Unique).Count -eq 1) {
#CPU are the same
Write-LogInfo -LogPath $sLogFile -Message "RAM in the cluster is the same"
return $true
}
else {
Write-LogInfo -LogPath $sLogFile -Message "RAM in the cluster is NOT the same"
return $false
}
}
Catch {
Write-LogError -LogPath $sLogFile -Message $_.Exception -ExitGracefully
Break
}
}
End {
If ($?) {
Write-LogInfo -LogPath $sLogFile -Message 'Completed Successfully.'
Write-LogInfo -LogPath $sLogFile -Message ' '
}
}
}
Function checkCPUCompliance {
Param ([Parameter(Mandatory = $true)][string]$clusterName)
Begin {
Write-LogInfo -LogPath $sLogFile -Message "Checking if all hosts in cluster [$clusterName] have the same CPU..."
$myHostsCPU = @()
}
Process {
Try {
$clusterObject = Get-Cluster -Name $clusterName -ErrorAction Stop
$hostObjects = $clusterObject | Get-VMHost -ErrorAction Stop
foreach ($hostObject in $hostObjects) {
$HostInfoCPU = "" | Select-Object CPU
$HostInfoCPU.CPU = $hostObject.CpuTotalMhz
$myHostsCPU += $HostInfoCPU
}
#Check if all values are correct: return true, otherwise return false
if (@($myHostsCPU | Select-Object -Unique).Count -eq 1) {
Write-LogInfo -LogPath $sLogFile -Message "CPU in the cluster is the same"
return $true
}
else {
Write-LogInfo -LogPath $sLogFile -Message "CPU in the cluster is NOT the same"
return $false
}
}
Catch {
Write-LogError -LogPath $sLogFile -Message $_.Exception -ExitGracefully
Break
}
}
End {
If ($?) {
Write-LogInfo -LogPath $sLogFile -Message 'Completed Successfully.'
Write-LogInfo -LogPath $sLogFile -Message ' '
}
}
}
Function getBiggestCPUInCluster {
Param ([Parameter(Mandatory=$true)][string]$clusterName)
Begin {
Write-LogInfo -LogPath $sLogFile -Message "Trying to get the biggest CPU resource in cluster [$clusterName]..."
}
Process {
Try {
$clusterObject = Get-Cluster $clusterName -ErrorAction Stop
$hostObjects = $clusterObject | Get-VMHost -ErrorAction Stop
$cpuResources = @()
foreach ($hostObject in $hostObjects) {
$cpuInfo = "" | Select-Object CPU
$cpuInfo.CPU = $hostObject.CpuTotalMhz
$cpuResources += $cpuInfo
}
return ($cpuResources | Measure-Object -Maximum)
}
Catch {
Write-LogError -LogPath $sLogFile -Message $_.Exception -ExitGracefully
Break
}
}
End {
If ($?) {
Write-LogInfo -LogPath $sLogFile -Message 'Completed Successfully.'
Write-LogInfo -LogPath $sLogFile -Message ' '
}
}
}
Function getBiggestRAMInCluster {
Param ([Parameter(Mandatory=$true)][string]$clusterName)
Begin {
Write-LogInfo -LogPath $sLogFile -Message "Trying to get the biggest RAM resource in cluster [$clusterName]..."
}
Process {
Try {
$clusterObject = Get-Cluster $clusterName -ErrorAction Stop
$hostObjects = $clusterObject | Get-VMHost -ErrorAction Stop
$ramResources = @()
foreach ($hostObject in $hostObjects) {
$ramInfo = "" | Select-Object RAM
$ramInfo.RAM = $hostObject.MemoryTotalGB
$ramResources += $ramInfo
}
return ($ramResources | Measure-Object -Maximum)
}
Catch {
Write-LogError -LogPath $sLogFile -Message $_.Exception -ExitGracefully
Break
}
}
End {
If ($?) {
Write-LogInfo -LogPath $sLogFile -Message 'Completed Successfully.'
Write-LogInfo -LogPath $sLogFile -Message ' '
}
}
}
Function SetAdmissionControl {
Param ([Parameter(Mandatory = $true)][string]$clusterName)
Begin {
Write-LogInfo -LogPath $sLogFile -Message "Setting the admission policy on cluster [$clusterName].."
}
Process {
Try {
$RAMCompliance = checkRAMCompliance $clusterName
$CPUCompliance = checkCPUCompliance $clusterName
$totalAmountofHostsInCluster = (Get-Cluster -Name $clusterName -ErrorAction Stop | Get-VMHost -ErrorAction Stop).Count
if (($RAMCompliance -eq $true) -and ($CPUCompliance -eq $true)) {
#Same hardware. calculation very simple
[int]$ramPercentageToReserve = [math]::Round((100 / ($totalAmountofHostsInCluster) * ($failuresToTolerate)), 0)
[int]$cpuPercentageToReserve = [math]::Round((100 / ($totalAmountofHostsInCluster) * ($failuresToTolerate)), 0)
}
if ($CPUCompliance -eq $false) {
#calculate with different CPU resources but same RAM resources
#get biggest CPU amount, total amount and number of hosts in cluster
$biggestCPUValue = getBiggestCPUInCluster $clusterName
$totalCPUMhz = (Get-Cluster $clusterName).ExtensionData.Summary.TotalCPU
[int]$cpuPercentageToReserve = [math]::Round(((($biggestCPUValue) * 100) / ($totalCPUMhz)*($failuresToTolerate)),0)
}
if ($RAMCompliance -eq $false) {
#in this case RAM is the decisive factor
#get biggest RAM amount, total amount and number of hosts in cluster
$biggestMemoryValue = getBiggestRAMInCluster $clusterName
$totalMemoryGB = [math]::Round(((Get-Cluster $clusterName).ExtensionData.Summary.TotalMemory /1024 /1024 /1024),0)
[int]$ramPercentageToReserve = [math]::Round(((($biggestMemoryValue) * 100) / ($totalMemoryGB)*($failuresToTolerate)),0)
}
Write-LogInfo -LogPath $sLogFile -Message "CPU Value calculated: [$cpuPercentageToReserve].."
Write-LogInfo -LogPath $sLogFile -Message "RAM Value calculated: [$ramPercentageToReserve].."
$spec = New-Object VMware.Vim.ClusterConfigSpecEx
$spec.dasConfig = New-Object VMware.Vim.ClusterDasConfigInfo
$spec.dasConfig.AdmissionControlPolicy = New-Object VMware.Vim.ClusterFailoverResourcesAdmissionControlPolicy
$spec.dasConfig.AdmissionControlEnabled = $true
$spec.dasConfig.AdmissionControlPolicy.cpuFailoverResourcesPercent = $cpuPercentageToReserve
$spec.dasConfig.AdmissionControlPolicy.memoryFailoverResourcesPercent = $ramPercentageToReserve
$clusterObject = Get-Cluster $clusterName
$clusterView = Get-View $clusterObject
$clusterView.ReconfigureComputeResource_Task($spec, $true)
}
Catch {
Write-LogError -LogPath $sLogFile -Message $_.Exception -ExitGracefully
Break
}
}
End {
If ($?) {
Write-LogInfo -LogPath $sLogFile -Message 'Completed Successfully.'
Write-LogInfo -LogPath $sLogFile -Message ' '
}
}
}
#-----------------------------------------------------------[Execution]------------------------------------------------------------
Start-Log -LogPath $sLogPath -LogName $sLogName -ScriptVersion $sScriptVersion
Connect-VMwareServer -VMServer $vCenter
SetAdmissionControl -clusterName $cluster
Stop-Log -LogPath $sLogFile