Private/SKU/Get-SkuSimilarityScore.ps1
|
function Get-SkuSimilarityScore { <# .SYNOPSIS Scores how similar a candidate SKU is to a target SKU profile. .DESCRIPTION Weighted scoring across 8 dimensions: vCPU (20), memory (20), family (15), family version newness (15), architecture (10), premium IO (5), disk IOPS (8), data disk count (7). Max 100. Family version newness uses continuous formulas so future generations (v8, v9, v10+) score correctly without code changes. #> param( [Parameter(Mandatory)][hashtable]$Target, [Parameter(Mandatory)][hashtable]$Candidate, [hashtable]$FamilyInfo ) $score = 0 # vCPU closeness (20 points) if ($Target.vCPU -gt 0 -and $Candidate.vCPU -gt 0) { $maxCpu = [math]::Max($Target.vCPU, $Candidate.vCPU) $cpuScore = 1 - ([math]::Abs($Target.vCPU - $Candidate.vCPU) / $maxCpu) $score += [math]::Round($cpuScore * 20) } # Memory closeness (20 points) if ($Target.MemoryGB -gt 0 -and $Candidate.MemoryGB -gt 0) { $maxMem = [math]::Max($Target.MemoryGB, $Candidate.MemoryGB) $memScore = 1 - ([math]::Abs($Target.MemoryGB - $Candidate.MemoryGB) / $maxMem) $score += [math]::Round($memScore * 20) } # Family match (15 points) — exact = 15, same category = 11, same first letter = 7 if ($Target.Family -eq $Candidate.Family) { $score += 15 } else { $targetInfo = if ($FamilyInfo) { $FamilyInfo[$Target.Family] } else { $null } $candidateInfo = if ($FamilyInfo) { $FamilyInfo[$Candidate.Family] } else { $null } $targetCat = if ($targetInfo) { $targetInfo.Category } else { 'Unknown' } $candidateCat = if ($candidateInfo) { $candidateInfo.Category } else { 'Unknown' } if ($targetCat -ne 'Unknown' -and $targetCat -eq $candidateCat) { $score += 11 } elseif ($Target.Family.Length -gt 0 -and $Candidate.Family.Length -gt 0 -and $Target.Family[0] -eq $Candidate.Family[0]) { $score += 7 } } # Family version newness (15 points) — continuous formulas, no version cap $targetVer = if ($Target.FamilyVersion) { [int]$Target.FamilyVersion } else { 1 } $candidateVer = if ($Candidate.FamilyVersion) { [int]$Candidate.FamilyVersion } else { 1 } if ($Target.Family -eq $Candidate.Family) { if ($candidateVer -gt $targetVer) { # Upgrade: base 8 + (candidateVer - 1), capped at 15 $score += [math]::Min(8 + $candidateVer - 1, 15) } elseif ($candidateVer -eq $targetVer) { $score += 6 } else { $score += 1 } } else { # Cross-family: 2 points per version above v1, capped at 13 $score += [math]::Min(2 * ($candidateVer - 1), 13) } # Architecture match (10 points) if ($Target.Architecture -eq $Candidate.Architecture) { $score += 10 } # Premium IO match (5 points) — if target needs premium, candidate must have it if ($Target.PremiumIO -eq $true -and $Candidate.PremiumIO -eq $true) { $score += 5 } elseif ($Target.PremiumIO -ne $true) { $score += 5 } # Disk IOPS closeness (8 points) — uncached disk IO throughput if ($Target.UncachedDiskIOPS -gt 0 -and $Candidate.UncachedDiskIOPS -gt 0) { $maxIOPS = [math]::Max($Target.UncachedDiskIOPS, $Candidate.UncachedDiskIOPS) $iopsScore = 1 - ([math]::Abs($Target.UncachedDiskIOPS - $Candidate.UncachedDiskIOPS) / $maxIOPS) $score += [math]::Round($iopsScore * 8) } elseif ($Target.UncachedDiskIOPS -le 0) { $score += 8 } # Data disk count closeness (7 points) if ($Target.MaxDataDiskCount -gt 0 -and $Candidate.MaxDataDiskCount -gt 0) { $maxDisks = [math]::Max($Target.MaxDataDiskCount, $Candidate.MaxDataDiskCount) $diskScore = 1 - ([math]::Abs($Target.MaxDataDiskCount - $Candidate.MaxDataDiskCount) / $maxDisks) $score += [math]::Round($diskScore * 7) } elseif ($Target.MaxDataDiskCount -le 0) { $score += 7 } return [math]::Min($score, 100) } |