Public/New-DMPerformanceReportTask.ps1

function New-DMPerformanceReportTask {
    <#
    .SYNOPSIS
        Creates an OceanStor performance report task for historical/ranged performance data.
 
    .DESCRIPTION
        Wraps the pms/report_task create interface (POST, v2 API). A report task defines what to
        collect -- a single object type/entity list, report_type 'performance' or 'capacity'
        (-ReportType) -- and over what time window. Running it (Invoke-DMPerformanceReportTask)
        generates a downloadable CSV/PDF export. Only a single content block is supported by this
        cmdlet; the underlying API allows up to 5 per task for callers building the raw body
        directly.
 
        The request body follows the documented create-report-task schema: top-level name,
        language, retention_number, format, time_segment, begin_time/end_time and the
        mandatory export schedule (frequency 'day', run_time 00:00); per-content report_type,
        object_type (documented name string), object_type_enum (numeric ID), sort_entities
        ('customer'), indicators (basic/advance ID arrays) and
        entities (id/name plus a JSON-encoded data string). Performance contents additionally
        carry compute_mode, sort_indicator, sort_type ('top') and limit; capacity contents send
        an empty indicator set and omit those performance-only fields. The documented limit field
        only accepts 5, 10 or 16, so the smallest value covering the entity count is chosen.
 
    .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 Name
        Report task name.
 
    .PARAMETER Language
        Report language code. Defaults to 'en'.
 
    .PARAMETER RetentionNumber
        Number of historical report runs the array retains for this task, 1-30. Defaults to 1.
 
    .PARAMETER Format
        Export format: CSV (default), PDF, or CSV-ORIGINAL (original per-sample granularity,
        limited to a 24-hour window).
 
    .PARAMETER TimeSegment
        Reporting window: OneHour, OneDay, OneWeek, OneMonth, OneYear, or Customer. StartTime and
        EndTime are mandatory when TimeSegment is Customer.
 
    .PARAMETER StartTime
        Window start (mandatory when TimeSegment is Customer).
 
    .PARAMETER EndTime
        Window end (mandatory when TimeSegment is Customer).
 
    .PARAMETER ObjectType
        Object type the report covers (see Get-DMPerformanceReportObjectTypeMap for the
        friendly-name -> name/enum mapping).
 
    .PARAMETER ObjectId
        One or more object IDs to include in the report. The documented entity limit values are
        5/10/16; more than 16 IDs requires -Force.
 
    .PARAMETER ReportType
        Report content type: Performance (default) or Capacity. Capacity reports send an empty
        indicator set and omit the performance-only compute/sort/limit fields.
 
    .PARAMETER Metric
        One or more friendly metric names (see Get-DMPerformanceIndicatorMap). Defaults to
        Get-DMPerformance's own default set when omitted. Ignored for -ReportType Capacity.
 
    .PARAMETER ComputeMode
        How samples are aggregated within the reporting window: Avg (default) or Max. Only sent
        for -ReportType Performance.
 
    .PARAMETER Force
        Submit even when more than 16 object IDs are supplied (beyond the largest documented
        limit value).
 
    .INPUTS
        None
 
    .OUTPUTS
        OceanstorPerformanceReportTask
 
    .EXAMPLE
        PS> New-DMPerformanceReportTask -Name 'lun-history' -ObjectType LUN -ObjectId '1' -TimeSegment OneWeek
 
    .EXAMPLE
        PS> New-DMPerformanceReportTask -Name 'lun-custom' -ObjectType LUN -ObjectId '1','2' -TimeSegment Customer -StartTime (Get-Date).AddDays(-2) -EndTime (Get-Date)
 
    .NOTES
        Filename: New-DMPerformanceReportTask.ps1
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    # String form: class type literals in attributes do not resolve inside module scope.
    [OutputType('OceanstorPerformanceReportTask')]
    param(
        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [pscustomobject]$WebSession,

        [Parameter(Mandatory = $true, Position = 0)]
        [ValidateNotNullOrEmpty()]
        # The doc says names may be 32 characters, but the live array acknowledges
        # 32-character names with success and never creates the task; 31 is the
        # real limit (observed on Dorado V600R005C27), so reject longer up front.
        [ValidateLength(1, 31)]
        [ValidatePattern('^[A-Za-z0-9_.-]+$')]
        [string]$Name,

        [string]$Language = 'en',

        [ValidateRange(1, 30)]
        [int]$RetentionNumber = 1,

        [ValidateSet('CSV', 'PDF', 'CSV-ORIGINAL')]
        [string]$Format = 'CSV',

        [Parameter(Mandatory = $true)]
        [ValidateSet('OneHour', 'OneDay', 'OneWeek', 'OneMonth', 'OneYear', 'Customer')]
        [string]$TimeSegment,

        [datetime]$StartTime,

        [datetime]$EndTime,

        [Parameter(Mandatory = $true)]
        [ValidateSet('LUN', 'FileSystem', 'Controller', 'StoragePool', 'Disk', 'Host', 'System', 'FCPort', 'EthernetPort')]
        [string]$ObjectType,

        [Parameter(Mandatory = $true)]
        [string[]]$ObjectId,

        [ValidateSet('Performance', 'Capacity')]
        [string]$ReportType = 'Performance',

        [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,

        [ValidateSet('Avg', 'Max')]
        [string]$ComputeMode = 'Avg',

        [switch]$Force
    )

    begin {
        if ($TimeSegment -eq 'Customer' -and (-not $PSBoundParameters.ContainsKey('StartTime') -or -not $PSBoundParameters.ContainsKey('EndTime'))) {
            throw 'New-DMPerformanceReportTask: StartTime and EndTime are mandatory when TimeSegment is Customer.'
        }
        if ($PSBoundParameters.ContainsKey('StartTime') -and $PSBoundParameters.ContainsKey('EndTime') -and $EndTime -le $StartTime) {
            throw 'New-DMPerformanceReportTask: EndTime must be later than StartTime.'
        }
        if (@($ObjectId).Count -gt 16) {
            if (-not $Force) {
                throw "New-DMPerformanceReportTask: the report_task interface documents at most 16 entities per content block (limit values 5/10/16); $(@($ObjectId).Count) object IDs were supplied. Use -Force to submit anyway."
            }
            Write-Warning "New-DMPerformanceReportTask: $(@($ObjectId).Count) object IDs exceed the documented maximum of 16 entities; submitting with limit 16."
        }
    }

    process {
        try {
            $session = if ($WebSession) { $WebSession } else { $script:CurrentOceanstorSession }

            $timeSegmentMap = Get-DMPerformanceReportTimeSegmentMap
            $objectTypeInfo = (Get-DMPerformanceReportObjectTypeMap)[$ObjectType]

            $entityIds = @($ObjectId)
            $limit = if ($entityIds.Count -le 5) { 5 } elseif ($entityIds.Count -le 10) { 10 } else { 16 }

            $content = @{
                report_type      = $ReportType.ToLowerInvariant()
                object_type      = $objectTypeInfo.Name
                object_type_enum = $objectTypeInfo.Enum
                # Explicit entity lists require the user-defined selection mode.
                sort_entities    = 'customer'
                indicators       = @{
                    basic   = @()
                    advance = @()
                }
                entities    = @($entityIds | ForEach-Object {
                        @{
                            id   = "$_"
                            name = "$_"
                            data = ([ordered]@{ ID = "$_"; NAME = "$_" } | ConvertTo-Json -Compress)
                        }
                    })
            }

            if ($ReportType -eq 'Performance') {
                $indicatorMap = Get-DMPerformanceIndicatorMap
                $metricNames = @(
                    if ($PSBoundParameters.ContainsKey('Metric') -and $Metric) { $Metric }
                    elseif ($ObjectType -eq 'FileSystem') { $script:DMDefaultNasPerformanceMetrics }
                    else { $script:DMDefaultPerformanceMetrics }
                )
                $indicatorIds = @($metricNames | ForEach-Object { $indicatorMap[$_].Id })

                $content.compute_mode = $ComputeMode.ToLowerInvariant()
                $content.indicators.basic = $indicatorIds
                $content.sort_indicator = $indicatorIds[0]
                $content.sort_type = 'top'
                $content.limit = $limit
            }
            elseif ($PSBoundParameters.ContainsKey('Metric') -and $Metric) {
                Write-Warning 'New-DMPerformanceReportTask: -Metric is ignored for -ReportType Capacity; capacity reports take no performance indicators.'
            }

            $body = @{
                name             = $Name
                language         = $Language
                retention_number = $RetentionNumber
                format           = $Format
                time_segment     = $timeSegmentMap[$TimeSegment]
                content          = @($content)
                # The documented schema marks the export schedule as mandatory even for
                # tasks that are only ever run on demand; the array rejects the create
                # request with 1073952264 when frequency/run_time are omitted.
                frequency        = 'day'
                run_time         = @{ hour = 0; min = 0 }
            }

            if ($TimeSegment -eq 'Customer') {
                # The array expresses begin_time/end_time in epoch milliseconds (observed
                # live: query-back returns 13-digit values), not the seconds the doc implies.
                $body.begin_time = [System.DateTimeOffset]::new($StartTime.ToUniversalTime()).ToUnixTimeMilliseconds()
                $body.end_time = [System.DateTimeOffset]::new($EndTime.ToUniversalTime()).ToUnixTimeMilliseconds()
            }

            if (-not $PSCmdlet.ShouldProcess($Name, 'Create performance report task')) {
                return
            }

            $response = Invoke-DeviceManager -WebSession $session -Method 'POST' -Resource 'pms/report_task' -BodyData $body -ApiV2
            $response = $response | Assert-DMApiSuccess
            if ($null -ne $response.data) {
                return [OceanstorPerformanceReportTask]::new($response.data, $session)
            }

            # The array acknowledges creation with data:null (observed live on Dorado
            # V600R005C27), so query the task back by its unique name to return an
            # object that carries the generated task ID.
            $queryResponse = Invoke-DeviceManager -WebSession $session -Method 'GET' -Resource 'pms/report_task' -ApiV2 | Assert-DMApiSuccess
            $createdTask = @($queryResponse.data) | Where-Object { $_.name -eq $Name }
            if (@($createdTask).Count -ne 1) {
                throw "New-DMPerformanceReportTask: the array reported success but the task '$Name' could not be read back by name."
            }
            return [OceanstorPerformanceReportTask]::new(@($createdTask)[0], $session)
        }
        catch {
            $PSCmdlet.WriteError($_)
        }
    }
}