PoorMans-DRS.ps1
ch384n1
powershell
9 months ago
9.6 kB
109
No Index
<#
.SYNOPSIS
A script to balance VMs across hosts in a vSphere cluster based on memory usage.
.DESCRIPTION
This script connects to a vCenter server, retrieves memory consumption statistics for hosts in the specified cluster, and migrates VMs to balance memory usage. It continuously checks the balance and migrates VMs until the memory usage across hosts is balanced within ±5%. The script logs the progress and status of tasks to the console.
.PARAMETER vCenterServer
The address of the vCenter server to connect to.
.PARAMETER clusterName
The name of the cluster within the vCenter server where VM balancing will occur.
.EXAMPLE
.\PoorMans-DRS.ps1 -vCenterServer "vcenter.domain.com" -clusterName "Cluster 01"
Connects to the specified vCenter server and balances VMs across the specified cluster based on memory usage.
#>
param (
[Parameter(Mandatory = $true, HelpMessage = "Specify the vCenter server address.")]
[ValidateNotNullOrEmpty()]
[string]$vCenterServer,
[Parameter(Mandatory = $true, HelpMessage = "Specify the cluster name.")]
[ValidateNotNullOrEmpty()]
[string]$clusterName
)
# Connect to vCenter
Connect-VIServer -Server $vCenterServer
# Get all hosts in the specified cluster
$cluster = Get-Cluster -Name $clusterName
$vmHosts = Get-VMHost -Location $cluster
# Create a reference list of all powered-on VMs and their memory usage
$vmList = Get-VM -Location $cluster | Where-Object { $_.PowerState -eq 'PoweredOn' } | Select-Object Name, MemoryGB, @{Name="VMHostName";Expression={$_.VMHost.Name}}
Write-Host "Retrieved reference list of all powered-on VMs and their memory usage."
# Function to get the current consumed memory for all hosts in the cluster
function Get-ClusterConsumedMemory {
param (
$VMHosts
)
# Retrieve real-time consumed memory for each host
$memoryUsage = $VMHosts | Get-Stat -Stat "mem.consumed.average" -Realtime -MaxSamples 1
$hostMemory = @{}
foreach ($stat in $memoryUsage) {
$hostName = $stat.Entity.Name
$consumedMemory = [math]::Round($stat.Value, 2)
$hostMemory[$hostName] = $consumedMemory
Write-Host "Consumed Memory for Host ${hostName}: ${consumedMemory} MB"
}
return $hostMemory
}
# Function to display memory usage as percentages
function DisplayMemoryPercentages {
param (
$memoryUsage
)
$totalMemory = ($memoryUsage.Values | Measure-Object -Sum).Sum
foreach ($currentHost in $memoryUsage.Keys) {
$percentage = ($memoryUsage[$currentHost] / $totalMemory) * 100
Write-Host "Consumed Memory for Host ${currentHost}: ${memoryUsage[$currentHost]} $([math]::Round($percentage, 2))%"
}
$consumedDifference = [math]::Abs(($memoryUsage.Values | Measure-Object -Maximum).Maximum - ($memoryUsage.Values | Measure-Object -Minimum).Minimum)
$differencePercentage = ($consumedDifference / $totalMemory) * 100
Write-Host "Updated Consumed Memory Difference: $([math]::Round($differencePercentage, 2))%"
}
# Function to simulate the impact of a migration and check if it improves balance
function ShouldMoveVM {
param (
$sourceHostMemory,
$destinationHostMemory,
$vmMemory
)
# Calculate the current difference and the difference if the VM is moved
$currentDifference = [math]::Abs($sourceHostMemory - $destinationHostMemory)
$newSourceMemory = $sourceHostMemory - $vmMemory
$newDestinationMemory = $destinationHostMemory + $vmMemory
$newDifference = [math]::Abs($newSourceMemory - $newDestinationMemory)
# Only move the VM if it reduces the memory difference
return $newDifference -lt $currentDifference
}
# Function to move VMs to balance memory usage
function Wait-ForTask {
param (
[Parameter(Mandatory=$true)]
[string]$TaskId
)
$maxRetries = 10
$retryInterval = 10
$retryCount = 0
while ($retryCount -lt $maxRetries) {
Start-Sleep -Seconds $retryInterval
try {
$task = Get-Task -Id $TaskId -ErrorAction Stop
$taskState = $task.State
Write-Host "Current task state: $taskState"
if ($taskState -eq 'Success') {
Write-Host "Task completed successfully."
return
}
elseif ($taskState -eq 'Error') {
Write-Host "Task failed."
return
}
}
catch {
if ($_.Exception.Message -match "The object 'vim.Task:task-[0-9]+' has already been deleted or has not been completely created") {
Write-Host "Task ID $TaskId is no longer valid or has not been fully created."
# Break out if task is deleted or invalid
return
}
else {
Write-Host "Error retrieving task state: $_"
}
}
# Increment retry count
$retryCount++
# Increase delay between retries
$retryInterval = [math]::Min($retryInterval * 2, 120)
}
Write-Host "Exceeded maximum retries. Task may be in an unknown state."
}
function Move-VMs {
param (
[ref]$memoryUsage,
[ref]$vmReference,
$memoryToMove,
[ref]$isBalanced
)
# Continuously select the most and least utilized hosts based on updated usage
while (-not $isBalanced.Value) {
# Find the most and least utilized hosts dynamically
$sourceHost = ($memoryUsage.Value.GetEnumerator() | Sort-Object Value -Descending | Select-Object -First 1).Key
$destinationHost = ($memoryUsage.Value.GetEnumerator() | Sort-Object Value | Select-Object -First 1).Key
# Select VMs on the source host sorted by memory consumption
$vmsOnSource = $vmReference.Value | Where-Object { $_.VMHostName -eq $sourceHost } | Sort-Object -Property MemoryGB -Descending
$movedMemory = 0
foreach ($vm in $vmsOnSource) {
$vmMemoryMB = $vm.MemoryGB * 1024
# Check if moving this VM will improve the balance
if (-not (ShouldMoveVM -sourceHostMemory $memoryUsage.Value[$sourceHost] -destinationHostMemory $memoryUsage.Value[$destinationHost] -vmMemory $vmMemoryMB)) {
Write-Host "Skipping migration of VM $($vm.Name) as it does not improve balance."
continue
}
if ($movedMemory -ge $memoryToMove) { break }
# Attempt to move the VM
try {
Write-Host "Migrating VM $($vm.Name) with $($vm.MemoryGB) GB from $($sourceHost) to $($destinationHost)"
$moveTask = Move-VM -VM (Get-VM -Name $vm.Name) -Destination (Get-VMHost -Name $destinationHost) -Confirm:$false -RunAsync
Write-Host "Task ID captured: $($moveTask.Id)"
# Call the Wait-ForTask function
Wait-ForTask -TaskId $moveTask.Id
# Remove the migrated VM from the reference list
$vmReference.Value = $vmReference.Value | Where-Object { $_.Name -ne $vm.Name }
$movedMemory += $vmMemoryMB
# Recalculate the memory difference after each migration
$memoryUsage.Value = Get-ClusterConsumedMemory -VMHosts $vmHosts
DisplayMemoryPercentages -memoryUsage $memoryUsage.Value
# Check if balance is achieved
$consumedDifference = [math]::Abs(($memoryUsage.Value.Values | Measure-Object -Maximum).Maximum - ($memoryUsage.Value.Values | Measure-Object -Minimum).Minimum)
$totalMemory = ($memoryUsage.Value.Values | Measure-Object -Sum).Sum
$differencePercentage = ($consumedDifference / $totalMemory) * 100
if ($differencePercentage -le 5) {
Write-Host "Hosts are balanced within ±5% memory usage difference. Stopping further migrations."
$isBalanced.Value = $true
break
}
}
catch {
Write-Host "An error occurred while moving VM $($vm.Name): $_"
}
# Check if balance was achieved
if ($isBalanced.Value) { break }
}
Write-Host "Total memory moved: $movedMemory MB"
}
}
# Main balancing loop
$maxIterations = 10 # Set a limit to the number of iterations to prevent endless running
$iteration = 0
$isBalanced = $false # Flag to indicate when balance is achieved
$memoryUsage = [ref](Get-ClusterConsumedMemory -VMHosts $vmHosts)
while ($iteration -lt $maxIterations -and -not $isBalanced) {
# Display the memory usage percentages
DisplayMemoryPercentages -memoryUsage $memoryUsage.Value
# Calculate the memory that needs to be moved to balance the difference
$memoryToMove = [math]::Round(($memoryUsage.Value.Values | Measure-Object -Maximum).Maximum - ($memoryUsage.Value.Values | Measure-Object -Minimum).Minimum) / 2
Write-Host "Memory needed to move to balance: $memoryToMove MB"
# Balance by migrating VMs from the host with the most consumed memory to the least
Move-VMs -memoryUsage $memoryUsage -vmReference ([ref]$vmList) -memoryToMove $memoryToMove -isBalanced ([ref]$isBalanced)
# Exit the loop if balance is achieved
if ($isBalanced) { break }
# Increment iteration count and pause to allow stats to update
$iteration++
Start-Sleep -Seconds 30 # Adjust the sleep time as needed to allow stats to update
}
# Disconnect from vCenter
Disconnect-VIServer -Server $vCenterServer -Confirm:$falseEditor is loading...
Leave a Comment