Skip to content

Instantly share code, notes, and snippets.

@jbirley
Created August 28, 2018 18:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jbirley/b419c05292ca3e06254b35b2f6de3f88 to your computer and use it in GitHub Desktop.
Save jbirley/b419c05292ca3e06254b35b2f6de3f88 to your computer and use it in GitHub Desktop.
This function gives Virtual Machine vCPU and vNuma rightsizing recommendations based on Rules of Thumb from this popular VMWare blog post: https://blogs.vmware.com/performance/2017/03/virtual-machine-vcpu-and-vnuma-rightsizing-rules-of-thumb.html#comment-117062 This function assumes that each VM is part of a vSphere cluster and will therefore ev…
#Requires -Version 3.0
function Get-vNumaInfo {
<#
.SYNOPSIS
Gets the current resource (vCPU and vRAM) information about one or more VMs and makes a recommendation based on best practice for vNuma.
.DESCRIPTION
This function gives Virtual Machine vCPU and vNuma rightsizing recommendations based on Rules of Thumb from this popular VMWare blog post:
https://blogs.vmware.com/performance/2017/03/virtual-machine-vcpu-and-vnuma-rightsizing-rules-of-thumb.html#comment-117062
This function assumes that each VM is part of a vSphere cluster and will therefore evaluate recommendations based on the smallest
node within a cluster, both from a CPU and Memory perspective. Currently, this function also assumes that all VMHosts are dual socket
servers and are already NUMA balanced at the physical layer since this is the most common deployment scenario.
.PARAMETER Cluster
One or more vSphere Cluster objects. Using this parameter is preferred over the VM parameter since it will evaluate the cluster
resources only once as it determines a recommendation for all VM's within the cluster.
.PARAMETER VM
One or more vSphere VM's. Using this parameter is less performant, especially when analyzing larger numbers of VM's.
For each VM, the cluster membership is determined before a recommendation is calculated. For larger numbers,
please see the Cluster parameter for better performance.
.PARAMETER VmClusterName
This is an optional parameter used in combination with the 'VM' parameter. It can help speed up results
in the case that all VMs are to be measured against a single cluster's resources or if you wish to "What-if" model
a set of VM's into another cluster. See example #2 below.
.EXAMPLE
Connect-ViServer -Server 'vCenterServer'
$AllClusters = Get-Cluster
Get-vNumaInfo -Cluster $AllClusters
.EXAMPLE
Connect-ViServer -Server 'vCenterServer'
$LargeVMs = Get-VM | Where {$_.NumCPU -gt 14}
Get-vNumaInfo -VM $LargeVMs -VmClusterName 'BigCluster'
.EXAMPLE
Connect-ViServer -Server 'vCenterServer'
Get-Datastore 'DataStore1' | Get-VM | get-vNumaInfo
.INPUTS
String
.OUTPUTS
PSCustomObject
.NOTES
Author: Jim Birley
@jbirley
#>
[CmdletBinding()]
[OutputType('PSCustomObject')]
param (
[Parameter(Mandatory,Position=0,ParameterSetName='ByCluster')]
#[VMware.VimAutomation.ViCore.Impl.V1.Inventory.ComputeResourceImpl[]]$Cluster,
[VMware.VimAutomation.ViCore.Impl.V1.Inventory.ClusterImpl[]]$Cluster,
[Parameter(Mandatory,ValueFromPipeline,Position=0,ParameterSetName='ByVM')]
[VMware.VimAutomation.ViCore.Impl.V1.Inventory.InventoryItemImpl[]]$VM, #typename obtained by $VM.GetType() and grabbed 'BaseType'
[Parameter(Position=1,ParameterSetName='ByVM')]
[string]$VmClusterName
)
BEGIN {
if ($VmClusterName) {
Write-Verbose "Evaluating $VmClusterName for smallest VMHost"
$pNumaCpu = (Get-Cluster $VmClusterName | Get-VMhost | Sort-Object -Property 'NumCPU' | Select-Object -First 1).NumCPU/2
$pNumaMem = [math]::Round((Get-cluster $VmClusterName | Get-VMhost | Sort-Object -Property 'MemoryTotalGB' | Select-Object -First 1).MemoryTotalGB,0)/2
}
}
PROCESS {
if ($VM) {
foreach ($MyVM in $VM) {
if (-Not $VmClusterName) {
#Determine each VM's VMHost and cluster and derive the minimum pNUMA Size. (Cores and pMem)
$HostName = ($MyVM | Select-Object VMHost).VMHost
$VmClusterName = (Get-VMHost -Name $HostName | Select-Object Parent).Parent
$pNumaCpu = (Get-Cluster $VmClusterName | Get-VMhost | Sort-Object -property 'NumCPU' | Select-Object -First 1).NumCPU/2
$pNumaMem = [math]::Round((Get-Cluster $VmClusterName | Get-VMhost | Sort-Object -Property 'MemoryTotalGB' | Select-Object -First 1).MemoryTotalGB,0)/2
}
$vNumaNodes = $MyVM.NumCPU/$MyVM.CoresPerSocket
$IsOverSized = if ($MyVM.NumCPU -gt ($pNumaCpu * 2) -Or $MyVM.MemoryGB -gt ($pNumaMem * 2)) {$true} else {$false}
$IsWide = if ($MyVM.NumCPU -gt $pNumaCpu -Or $MyVM.MemoryGB -gt $pNumaMem) {$true} else {$false}
$ReconfigureVM = $true
$NewvCPU = $MyVM.NumCPU
$NewMemGB = $MyVM.MemoryGB
if ($vNumaNodes -eq 1) {
if (-Not $IsWide) {
$ReconfigureVM = $false #already optimal
$NewCoresPerSocket = $MyVM.CoresPerSocket
$NewvNumaNodes = 1
} else { #wide VM with one vNUMA node
if ($IsOverSized) { #shrink the VM by CPU or Mem if necessary
if ($NewvCPU -gt $pNumaCpu) {
$NewvCPU = $pNumaCpu * 2
}
if ($NewMemGB -gt $pNumaMem) {
$NewMemGB = $pNumaMem * 2
}
}
$NewCoresPerSocket = $NewvCPU / 2
$NewvNumaNodes = 2
}
} else { #more than one vNuma Node
if (-Not $IsWide) { #make it one vNuma node
$NewCoresPerSocket = $NewvCPU
$NewvNumaNodes = 1
} else { #wide VM with more than one vNuma Node
if ($IsOverSized) { #shrink the VM by CPU or Mem if necessary
if ($NewvCPU -gt $pNumaCpu) {
$NewvCPU = $pNumaCpu * 2
}
if ($NewMemGB -gt $pNumaMem) {
$NewMemGB = $pNumaMem * 2
}
}
$NewCoresPerSocket = $NewvCPU / 2
$NewvNumaNodes = 2
}
}
#In the case that the VM was "wide", but already configured optimally, set ReconfigureVM to false.
if (($vNumaNodes -eq $NewvNumaNodes) -And ($MyVM.CoresPerSocket -eq $NewCoresPerSocket)) {$ReconfigureVM = $false}
[PSCustomObject]@{
'Name' = $MyVM.Name
'Cluster' = $VmClusterName
'pNumaCpu' = $pNumaCpu
'pNumaMem' = $pNumaMem
'NumCPU' = $MyVM.NumCPU
'MemoryGB' = $MyVM.MemoryGB
'vSockets' = $vNumaNodes
'CoresPerSocket' = $MyVM.CoresPerSocket
'IsWide' = $IsWide
'IsOverSized' = $IsOverSized
'ReconfigureVM' = $ReconfigureVM
'NewvCPU' = $NewvCPU
'NewMemGB' = $NewMemGB
'NewvSockets' = $NewvNumaNodes
'NewCoresPerSocket' = $NewCoresPerSocket
}
}
} else {
foreach ($MyCluster in $Cluster) {
$AllVms = Get-VM -Location $MyCluster
if ($AllVms) {
Get-vNumaInfo -VM $AllVms -VmClusterName $MyCluster.Name
}
}
}
}
END {}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment