PoorMans-DRS.ps1
ch384n1
powershell
4 days ago
9.6 kB
69
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:$false
Editor is loading...
Leave a Comment