Functions/Private/Get-MigrationRuleImpact.ps1
|
function Get-MigrationRuleImpact { <# .SYNOPSIS Evaluates whether a proposed live migration would break, fix, or be neutral with respect to the configured affinity and anti-affinity rules. .DESCRIPTION For each rule that references the VM being migrated, the function simulates the post-migration placement and compares it to the current placement: Break (currently satisfied → will be violated): Hard rule → HasHardViolation = $true (caller should exclude this destination) Soft rule → HasSoftViolation = $true (caller should apply a score penalty) Fix (currently violated → will be satisfied): → FixesViolation = $true (caller should apply a score bonus) Neutral (no change in satisfaction status) → no flags set. .OUTPUTS PSCustomObject: HasHardViolation, HasSoftViolation, FixesViolation, HardReasons[], SoftReasons[], FixReasons[] #> [CmdletBinding()] param( [Parameter(Mandatory)] [string] $VMName, [Parameter(Mandatory)] [string] $DestinationNode, [Parameter(Mandatory)] [PSCustomObject] $Snapshot, [PSCustomObject[]] $RuleSet ) $empty = [PSCustomObject]@{ HasHardViolation = $false HasSoftViolation = $false FixesViolation = $false HardReasons = @() SoftReasons = @() FixReasons = @() } if (-not $RuleSet -or $RuleSet.Count -eq 0) { return $empty } # Current placement: VMName → HostNode $vmHost = @{} foreach ($vm in $Snapshot.VMs) { $vmHost[$vm.VMName] = $vm.HostNode } $hardReasons = [System.Collections.Generic.List[string]]::new() $softReasons = [System.Collections.Generic.List[string]]::new() $fixReasons = [System.Collections.Generic.List[string]]::new() foreach ($rule in $RuleSet) { if ($rule.VMs -notcontains $VMName) { continue } $activeVMs = @($rule.VMs | Where-Object { $vmHost.ContainsKey($_) }) if ($activeVMs.Count -eq 0) { continue } $severity = if ($rule.Enforced) { 'enforced' } else { 'soft' } switch ($rule.Type) { 'VmVmAffinity' { # All members must share one host. $currentHosts = @($activeVMs | ForEach-Object { $vmHost[$_] } | Select-Object -Unique) $simHosts = @($activeVMs | ForEach-Object { if ($_ -eq $VMName) { $DestinationNode } else { $vmHost[$_] } } | Select-Object -Unique) $wasSatisfied = $currentHosts.Count -le 1 $willSatisfied = $simHosts.Count -le 1 if ($wasSatisfied -and -not $willSatisfied) { $msg = "Breaks $severity affinity rule '$($rule.Name)' — members would span: $($simHosts -join ', ')" if ($rule.Enforced) { $hardReasons.Add($msg) } else { $softReasons.Add($msg) } } elseif (-not $wasSatisfied -and $willSatisfied) { $fixReasons.Add("Satisfies affinity rule '$($rule.Name)' — all members consolidated on '$DestinationNode'") } } 'VmVmAntiAffinity' { # No two members may share a host. $currentConflicts = @($activeVMs | Group-Object { $vmHost[$_] } | Where-Object { $_.Count -gt 1 }) $simConflicts = @($activeVMs | Group-Object { if ($_ -eq $VMName) { $DestinationNode } else { $vmHost[$_] } } | Where-Object { $_.Count -gt 1 }) $wasSatisfied = $currentConflicts.Count -eq 0 $willSatisfied = $simConflicts.Count -eq 0 if ($wasSatisfied -and -not $willSatisfied) { $peer = ($simConflicts[0].Group | Where-Object { $_ -ne $VMName })[0] $msg = "Breaks $severity anti-affinity rule '$($rule.Name)' — '$VMName' would share '$DestinationNode' with '$peer'" if ($rule.Enforced) { $hardReasons.Add($msg) } else { $softReasons.Add($msg) } } elseif (-not $wasSatisfied -and $willSatisfied) { $fixReasons.Add("Satisfies anti-affinity rule '$($rule.Name)' — '$VMName' separated from all group members") } } 'VmHostAffinity' { # VM must reside on one of the listed hosts. $wasSatisfied = $rule.Hosts -contains $vmHost[$VMName] $willSatisfied = $rule.Hosts -contains $DestinationNode if ($wasSatisfied -and -not $willSatisfied) { $msg = "Breaks $severity host-affinity rule '$($rule.Name)' — '$DestinationNode' is not in [$(($rule.Hosts) -join ', ')]" if ($rule.Enforced) { $hardReasons.Add($msg) } else { $softReasons.Add($msg) } } elseif (-not $wasSatisfied -and $willSatisfied) { $fixReasons.Add("Satisfies host-affinity rule '$($rule.Name)' — '$VMName' moves onto allowed host '$DestinationNode'") } } 'VmHostAntiAffinity' { # VM must NOT reside on any of the listed hosts. $wasSatisfied = $rule.Hosts -notcontains $vmHost[$VMName] $willSatisfied = $rule.Hosts -notcontains $DestinationNode if ($wasSatisfied -and -not $willSatisfied) { $msg = "Breaks $severity host-anti-affinity rule '$($rule.Name)' — '$DestinationNode' is an excluded host" if ($rule.Enforced) { $hardReasons.Add($msg) } else { $softReasons.Add($msg) } } elseif (-not $wasSatisfied -and $willSatisfied) { $fixReasons.Add("Satisfies host-anti-affinity rule '$($rule.Name)' — '$VMName' moves off excluded host") } } } } [PSCustomObject]@{ HasHardViolation = $hardReasons.Count -gt 0 HasSoftViolation = $softReasons.Count -gt 0 FixesViolation = $fixReasons.Count -gt 0 HardReasons = $hardReasons.ToArray() SoftReasons = $softReasons.ToArray() FixReasons = $fixReasons.ToArray() } } |