[PowerCLI] 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. 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
Leave a Reply