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. .PARAMETER ResourceId The VM's ARM resource id. Stamped (lowercased) on every row as the unique key. .PARAMETER SkuName The VM size (e.g. 'Standard_D4as_v5'), stamped on every row for context. .OUTPUTS [pscustomobject[]] rows: resourceId, vmName, skuName, 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, [string] $ResourceId, [string] $SkuName ) $rows = [System.Collections.Generic.List[pscustomobject]]::new() # resourceId is lowercased so it joins reliably in Kusto (string equality is case-sensitive). $ridLower = if ($ResourceId) { $ResourceId.ToLowerInvariant() } else { $null } # 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]@{ resourceId = $ridLower vmName = $VMName skuName = $SkuName 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() } |