Public/Get-DMPerformanceHistory.ps1
|
function Get-DMPerformanceHistory { <# .SYNOPSIS Retrieves historical/ranged performance samples for OceanStor objects. .DESCRIPTION The performance_data endpoint used by Get-DMPerformance only ever returns the current sample, so historical/ranged queries have to be driven through the separate pms/report_task workflow instead: create a Customer-time-segment report task, run it, wait for the export, download the zip, and parse the CSV inside. This cmdlet is a convenience wrapper around that pipeline (New-DMPerformanceReportTask, Invoke-DMPerformanceReportTask, Save-DMPerformanceReportFile, Import-DMPerformanceReportCsv) for callers who just want OceanStor.PerformanceSample objects for a time range, without managing the underlying report task lifecycle themselves. The report task and its export log are removed automatically afterwards unless -KeepReportTask is specified. The report zip's CSV column layout has not been confirmed against a live array. Rather than guess at header text, this cmdlet maps the last N columns of each row positionally against the -Metric list, in the exact order that was submitted to the report task at creation time (the module authored that order, so it is the one thing that is certain). Any leading columns are inspected by name for an object-ID- or time-like header; when none is found, ObjectId/Timestamp are left blank/MinValue on the resulting sample. See PerformanceGAP.md Phase 4 notes. .PARAMETER WebSession Optional parameter to define the session to be use on the REST call. If not defined, the module's cached $script:CurrentOceanstorSession session will be used .PARAMETER ObjectType The type of object to retrieve performance history for. .PARAMETER ObjectId One or more object IDs to retrieve performance history for. Pipeline-able. .PARAMETER Metric One or more friendly metric names (see Get-DMPerformanceIndicatorMap). Defaults to Get-DMPerformance's own default set when omitted. .PARAMETER StartTime Window start. .PARAMETER EndTime Window end. .PARAMETER ComputeMode How samples are aggregated within the reporting window: Avg (default) or Max. .PARAMETER TimeoutSec Maximum seconds to wait for the report task's export to finish generating. Defaults to 120. .PARAMETER KeepReportTask Skip removing the underlying report task and export log afterwards. Useful for troubleshooting the raw report task/export/CSV directly. .INPUTS System.String .OUTPUTS System.Collections.ArrayList .EXAMPLE PS> Get-DMPerformanceHistory -ObjectType LUN -ObjectId '1' -StartTime (Get-Date).AddDays(-1) -EndTime (Get-Date) .EXAMPLE PS> Get-DMPerformanceHistory -ObjectType Controller -ObjectId '0A','0B' -Metric TotalIOPS,AvgLatencyMs -StartTime (Get-Date).AddHours(-6) -EndTime (Get-Date) -KeepReportTask .NOTES Filename: Get-DMPerformanceHistory.ps1 #> [CmdletBinding()] [OutputType([System.Collections.ArrayList])] param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $false)] [pscustomobject]$WebSession, [Parameter(Mandatory = $true, Position = 0)] [ValidateSet('LUN', 'FileSystem', 'Controller', 'StoragePool', 'Disk', 'Host', 'System', 'FCPort', 'EthernetPort')] [string]$ObjectType, [Parameter(Mandatory = $true, Position = 1, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [string[]]$ObjectId, [ArgumentCompleter({ param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) (Get-DMPerformanceIndicatorMap).Keys | Where-Object { $_ -like "$wordToComplete*" } })] [ValidateScript({ $validNames = (Get-DMPerformanceIndicatorMap).Keys foreach ($metricName in $_) { if ($metricName -notin $validNames) { throw "Unknown performance metric '$metricName'. Valid metrics: $($validNames -join ', ')" } } return $true })] [string[]]$Metric, [Parameter(Mandatory = $true)] [datetime]$StartTime, [Parameter(Mandatory = $true)] [datetime]$EndTime, [ValidateSet('Avg', 'Max')] [string]$ComputeMode = 'Avg', [ValidateRange(1, 86400)] [int]$TimeoutSec = 120, [switch]$KeepReportTask ) begin { $session = if ($WebSession) { $WebSession } else { $script:CurrentOceanstorSession } $metricNames = @( if ($PSBoundParameters.ContainsKey('Metric') -and $Metric) { $Metric } elseif ($ObjectType -eq 'FileSystem') { $script:DMDefaultNasPerformanceMetrics } else { $script:DMDefaultPerformanceMetrics } ) $collectedIds = [System.Collections.Generic.List[string]]::new() } process { foreach ($id in $ObjectId) { $collectedIds.Add($id) } } end { $samples = [System.Collections.ArrayList]::new() if ($collectedIds.Count -eq 0) { return $samples } # 14-char prefix + 17 guid chars = 31, the real pms/report_task name cap # (the documented 32 is off by one on live arrays: a 32-character name is # acknowledged with success but the task is never created). $taskName = "DMPerfHistory_$([guid]::NewGuid().ToString('N').Substring(0, 17))" $tempZip = Join-Path ([System.IO.Path]::GetTempPath()) "$taskName.zip" $task = $null $log = $null try { $task = New-DMPerformanceReportTask -WebSession $session -Name $taskName -TimeSegment 'Customer' ` -StartTime $StartTime -EndTime $EndTime -Format 'CSV' -RetentionNumber 1 ` -ObjectType $ObjectType -ObjectId @($collectedIds) -Metric $metricNames -ComputeMode $ComputeMode -Confirm:$false if ($null -eq $task) { throw 'Get-DMPerformanceHistory: failed to create the underlying report task.' } $log = Invoke-DMPerformanceReportTask -WebSession $session -Id $task.Id -TimeoutSec $TimeoutSec -Confirm:$false if ($null -eq $log) { throw 'Get-DMPerformanceHistory: report task did not produce an export log.' } Save-DMPerformanceReportFile -WebSession $session -LogId $log.LogId -TaskId $task.Id -Path $tempZip -Force | Out-Null $rows = Import-DMPerformanceReportCsv -ZipPath $tempZip $metricCount = $metricNames.Count # Live arrays export long-format CSVs -- one row per object/metric/timestamp # with headers 'Object Type,Object Instance,Statistical Metric,Value,Time, # Object Type ID,Object Instance ID,Statistical Metric ID' (confirmed on # Dorado V600R005C27). Pivot those rows into one sample per object/timestamp. $firstRow = $rows | Select-Object -First 1 $firstRowProps = if ($firstRow) { @($firstRow.PSObject.Properties.Name) } else { @() } if (($firstRowProps -contains 'Statistical Metric ID') -and ($firstRowProps -contains 'Value')) { $indicatorMap = Get-DMPerformanceIndicatorMap $indicatorIdToMetric = @{} foreach ($metricName in $metricNames) { $indicatorIdToMetric["$($indicatorMap[$metricName].Id)"] = $metricName } foreach ($group in ($rows | Group-Object -Property 'Object Instance ID', 'Time')) { $groupRows = @($group.Group) $firstOfGroup = $groupRows[0] # Times arrive as '2026-07-06 20:24:10 DST'; drop the trailing zone token. $timestamp = [datetime]::MinValue $rawTime = "$($firstOfGroup.Time)" -replace '\s+[A-Z]{2,5}$', '' $parsed = [datetime]::MinValue if ([datetime]::TryParse($rawTime, [ref]$parsed)) { $timestamp = $parsed } $metrics = [ordered]@{} foreach ($metricName in $metricNames) { $metrics[$metricName] = $null } foreach ($entry in $groupRows) { $entryMetricName = $indicatorIdToMetric["$($entry.'Statistical Metric ID')"] if ($entryMetricName) { $rawValue = $entry.Value $metrics[$entryMetricName] = if ($null -ne $rawValue -and "$rawValue" -ne '') { [double]$rawValue } else { $null } } } $sample = New-DMPerformanceSample -ObjectType $ObjectType -ObjectId $firstOfGroup.'Object Instance ID' -Timestamp $timestamp ` -Metrics $metrics -Session $session [void]$samples.Add($sample) } return $samples } # Wide-format fallback (one column per metric), kept for firmware whose # exports differ from the long format observed live. foreach ($row in $rows) { $propNames = @($row.PSObject.Properties.Name | Where-Object { $_ -ne 'SourceFile' }) if ($propNames.Count -lt $metricCount) { Write-Warning "Get-DMPerformanceHistory: skipping a CSV row with fewer columns ($($propNames.Count)) than requested metrics ($metricCount)." continue } $startIdx = $propNames.Count - $metricCount $metricPropNames = $propNames[$startIdx..($propNames.Count - 1)] $leadingPropNames = if ($startIdx -gt 0) { $propNames[0..($startIdx - 1)] } else { @() } $objectIdProp = $leadingPropNames | Where-Object { $_ -match '(?i)object.?id|^id$' } | Select-Object -First 1 $timeProp = $leadingPropNames | Where-Object { $_ -match '(?i)time|date' } | Select-Object -First 1 $rowObjectId = if ($objectIdProp) { $row.$objectIdProp } else { $null } $timestamp = [datetime]::MinValue if ($timeProp) { $rawTime = $row.$timeProp if ($rawTime -match '^\d+$') { $timestamp = [DateTimeOffset]::FromUnixTimeSeconds([long]$rawTime).UtcDateTime } else { $parsed = [datetime]::MinValue if ([datetime]::TryParse($rawTime, [ref]$parsed)) { $timestamp = $parsed } } } $metrics = [ordered]@{} for ($i = 0; $i -lt $metricCount; $i++) { $rawValue = $row.($metricPropNames[$i]) $metrics[$metricNames[$i]] = if ($null -ne $rawValue -and "$rawValue" -ne '') { [double]$rawValue } else { $null } } $sample = New-DMPerformanceSample -ObjectType $ObjectType -ObjectId $rowObjectId -Timestamp $timestamp ` -Metrics $metrics -Session $session [void]$samples.Add($sample) } return $samples } finally { if (-not $KeepReportTask) { if ($log) { try { Invoke-DeviceManager -WebSession $session -Method 'DELETE' -Resource "pms/report_task/task_log?log_id=$($log.LogId)" -ApiV2 | Out-Null } catch { Write-Warning "Get-DMPerformanceHistory: failed to remove export log $($log.LogId): $_" } } if ($task) { try { Remove-DMPerformanceReportTask -WebSession $session -Id $task.Id -Confirm:$false | Out-Null } catch { Write-Warning "Get-DMPerformanceHistory: failed to remove report task $($task.Id): $_" } } } if (Test-Path -LiteralPath $tempZip) { Remove-Item -LiteralPath $tempZip -Force -ErrorAction SilentlyContinue } } } } |