View Host Allocation by Cluster in PowerCLI

Viewing resource allocation at a cluster level is something we don’t do enough. In the past I would look at a specific host and see if there was an issue of over allocating memory or CPUs or when budgeting for the next year I would gather stats, but rarely did I save most of that information. One of the bigger mistakes I would make is looking at the cluster as a whole and grabbing the total RAM and CPU for the cluster and compare that against the total RAM and CPU allocated to the VMs that reside on that cluster and assume that the average was the number to base my calculations on. What that doesn’t take into account is things like DRS rules where certain VMs are pinned to a host, separated from each other, or DRS being disabled all together.

This started me down a path of creating a report to show what the current utilization was for each one of my clusters and then breaking that down to the hosts in each cluster so I could get an idea of how well my VMs were spread across a cluster.

I have a decent number of clusters I’m working with so the first thing we’ll do is get all the clusters in vCenter and sort them by name. I prefer doing a name sort so they appear in the order I’m used to looking at them in vCenter

$allClusters = Get-Cluster | Sort Name

Now we’ll open our ForEach loop for all the clusters. We make this a variable so we can see all the output once the script is completed. Then we’ll create an empty array.

$clusterOutput = ForEach ($cluster in $allClusters) {
$report = @()

We’ll get all the hosts in each cluster one cluster at a time and open another ForEach loop for each one of those hosts as well.

$allHosts = $cluster | Get-VMHost | Sort name
ForEach ($vmHost in $allHosts) {

We’re going to get all the VMs on each host now. I’m only concerned about powered on VMs, but depending on your environment you may want to omit the PowerState clause. Once we get all the VMs on a host, we want to calculate how much Memory is allocated and how many CPUs are allocated. The “Measure-Object -sum” will add those numbers together for us and we’ll call that number in the report.

$vms = $vmHost | Get-VM | Where {$_.PowerState -eq "PoweredOn"}
$vmMemSum = $vms.memoryGB | Measure-Object -sum
$vmCpuSum = $vms.NumCpu | Measure-Object -sum

Now that we have the total VM memory and CPU allocated for the host, we want to see the ratio of CPUs allocated to available on the host. We use the $ratio variable to capture this value, then use PowerShell math to divide the number of vCPUs allocated to the VMs by the number of pCPUs available on the host. We then round that number to 2 decimal places.

$ratio = [math]::round($vmCpuSum.sum/$vmhost.NumCpu,2)

With all the numbers captured we can start creating the table view by defining the column names. Host name, Host State, Host memory, VM Memory, Host CPUs, VM CPUs, and VM CPU to Host CPU value are what we’re interested in.

$row = "" | Select VMHost, State, "Host Memory", "VM Memory", "Host CPU", "VM CPU", "vCPU per pCPU"

To populate this table we use $row.<Column Name> and give it the value using =. Because of the ForEach loop we’re repeating this for every single VM Host in a cluster.

$row.VMhost = $vmhost.Name
$row.State = $vmhost.ConnectionState
$row."Host Memory" = [math]::round($vmhost.MemoryTotalGB,2)
$row."VM Memory" = [math]::round($vmMemSum.sum,2)
$row."Host CPU" = $vmhost.NumCpu
$row."VM CPU" = $vmCpuSum.sum
$row."vCPU per pCPU" = $ratio

Once that has been completed we need to add the rows to our empty array and then close the ForEach loop for the hosts.

$report += $row}

At this point we now have a completed table view of the resource allocation. Since we’ll be running this in PowerShell we’ll need to display the name of the cluster between each report otherwise you might not be able to immediately recognize what cluster is being referenced. Use “Write-Output” instead of “Write-Host” so this displays in the correct order in the script. When using Write-Output with a variable in the output it needs to be wrapped in $( ) otherwise the variable name will be displayed instead.

Write-Output "$($cluster.Name) Resource Allocation"

In order to have this display per cluster we’ll call the cluster output here and then close the ForEach loop on the clusters.

$report | Format-Table -Autosize}

We can then display the output using the $clusterOutput variable we created in step 2.

$clusterOutput

This is what the output will look like:

Instead of just displaying this in the console you could export this to CSV to save it for reference. Below is the full script.

$allClusters = Get-Cluster | Sort Name
$clusterOutput = ForEach ($cluster in $allClusters) {
$report = @()
$allHosts = $cluster | Get-VMHost | Sort Name
ForEach ($vmhost in $allHosts) {
$vms = $vmhost | Get-VM | Where {$_.PowerState -eq "PoweredOn"}
$vmMemSum = $vms.memoryGB | Measure-Object -sum
$vmCpuSum = $vms.NumCpu | Measure-Object -sum
$ratio = [math]::round($vmCpuSum.sum/$vmhost.NumCpu,2)
$row = "" | Select VMHost, State, "Host Memory", "VM Memory", "Host CPU", "VM CPU", "vCPU per pCPU"
$row.VMhost = $vmhost.Name
$row.State = $vmhost.ConnectionState
$row."Host Memory" = [math]::round($vmhost.MemoryTotalGB,2)
$row."VM Memory" = [math]::round($vmMemSum.sum,2)
$row."Host CPU" = $vmhost.NumCpu
$row."VM CPU" = $vmCpuSum.sum
$row."vCPU per pCPU" = $ratio
$report += $row}
Write-Output "$($cluster.Name) Resource Allocation"
$report | Format-Table -Autosize}
$clusterOutput