Private/ConvertTo-VMPerformanceRow.ps1
|
function ConvertTo-VMPerformanceRow { <# .SYNOPSIS Aggregates raw per-interval datapoints for one VM into monthly VMPerformance-compatible rows (one per VM x month x CounterName). .DESCRIPTION Buckets datapoints by calendar month (UTC) and computes Min / Median / Percentile95 / Percentile99 / Max / Average / SampleCount per bucket, emitting the three counters consumed by VMPerformance(): % Processor Time <- Percentage CPU (avg per interval) Available MBytes <- Available Memory Bytes / 1MB (exact) % Committed Bytes In Use <- 100 - (Available / TotalRAM * 100) (PROXY) The committed-memory counter is a PROXY derived from host-available memory and the SKU's total RAM; it is not the guest's true committed bytes. It is only emitted when TotalRamMB is known. .PARAMETER VMName The VM name to stamp on every row. .PARAMETER CpuPoint Datapoints for 'Percentage CPU' (objects with TimeStamp + Average). .PARAMETER MemPoint Datapoints for 'Available Memory Bytes' (objects with TimeStamp + Average). .PARAMETER TotalRamMB VM total RAM in MB (from Resolve-VMSkuRam). When $null, the committed-memory proxy counter is skipped. .OUTPUTS [pscustomobject[]] rows: vmName, Date, CounterName, Min, Median, Percentile95, Percentile99, Max, Average, SampleCount. #> [CmdletBinding()] [OutputType([pscustomobject[]])] param( [Parameter(Mandatory)] [string] $VMName, [Parameter(Mandatory)] [AllowEmptyCollection()] [object[]] $CpuPoint, [Parameter(Mandatory)] [AllowEmptyCollection()] [object[]] $MemPoint, [double] $TotalRamMB ) $rows = [System.Collections.Generic.List[pscustomobject]]::new() # Local helper: emit one row per month for a {monthKey -> double[]} map. $emit = { param($counterName, $byMonth) foreach ($monthKey in ($byMonth.Keys | Sort-Object)) { $vals = [double[]]$byMonth[$monthKey] if ($vals.Count -eq 0) { continue } $rows.Add([pscustomobject]@{ vmName = $VMName Date = $monthKey CounterName = $counterName Min = [math]::Round(($vals | Measure-Object -Minimum).Minimum, 2) Median = [math]::Round((Measure-Percentile -Value $vals -Percentile 50), 2) Percentile95 = [math]::Round((Measure-Percentile -Value $vals -Percentile 95), 2) Percentile99 = [math]::Round((Measure-Percentile -Value $vals -Percentile 99), 2) Max = [math]::Round(($vals | Measure-Object -Maximum).Maximum, 2) Average = [math]::Round(($vals | Measure-Object -Average).Average, 2) SampleCount = $vals.Count }) } } $monthOf = { param($ts) ([datetime]$ts).ToUniversalTime().ToString('yyyy-MM-01') } # --- CPU: % Processor Time --- $cpuByMonth = @{} foreach ($p in $CpuPoint) { if ($null -eq $p.Average) { continue } $mk = & $monthOf $p.TimeStamp if (-not $cpuByMonth.ContainsKey($mk)) { $cpuByMonth[$mk] = [System.Collections.Generic.List[double]]::new() } $cpuByMonth[$mk].Add([double]$p.Average) } & $emit '% Processor Time' $cpuByMonth # --- Memory: Available MBytes (exact) + % Committed Bytes In Use (proxy) --- $availByMonth = @{} $commByMonth = @{} $totalBytes = if ($PSBoundParameters.ContainsKey('TotalRamMB') -and $TotalRamMB -gt 0) { $TotalRamMB * 1MB } else { $null } foreach ($p in $MemPoint) { if ($null -eq $p.Average) { continue } $mk = & $monthOf $p.TimeStamp $availMB = [double]$p.Average / 1MB if (-not $availByMonth.ContainsKey($mk)) { $availByMonth[$mk] = [System.Collections.Generic.List[double]]::new() } $availByMonth[$mk].Add($availMB) if ($null -ne $totalBytes) { $committedPct = 100.0 - ([double]$p.Average / $totalBytes * 100.0) if ($committedPct -lt 0) { $committedPct = 0 } if ($committedPct -gt 100) { $committedPct = 100 } if (-not $commByMonth.ContainsKey($mk)) { $commByMonth[$mk] = [System.Collections.Generic.List[double]]::new() } $commByMonth[$mk].Add($committedPct) } } & $emit 'Available MBytes' $availByMonth if ($null -ne $totalBytes) { & $emit '% Committed Bytes In Use' $commByMonth } return $rows.ToArray() } |