Public/Add-DMQosAssociation.ps1

function Add-DMQosAssociation {
    <#
    .SYNOPSIS
        Associates a Huawei Oceanstor SmartQoS policy with a LUN group, host, vStore, or child policy.
 
    .DESCRIPTION
        Adds an association via the ioclass/create_associate resource. Exactly one target
        parameter must be supplied. Individual LUN/file system attachment is not handled by
        this cmdlet - use New-DMQosPolicy/Set-DMQosPolicy's LunName/LunId/FileSystemName/FileSystemId
        parameters instead. ASSOCIATEOBJTYPE values used: 230 (child SmartQoS policy),
        256 (LUN group), 21 (host), 16442 (vStore).
 
        A SmartQoS policy can only be bound to one object type. The cmdlet pre-validates the
        resolved policy's current binding (LUN, file system, host, or child policy) and fails
        with a clear message - without calling the array - when the requested association
        would conflict with an existing binding of a different type.
 
    .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
        Name of the SmartQoS (parent) policy. Mutually exclusive with Id (enforced by parameter set).
 
    .PARAMETER Id
        ID of the SmartQoS (parent) policy. Mutually exclusive with Name (enforced by parameter set).
 
    .PARAMETER LunGroupName
        Name of the LUN group to associate. Mutually exclusive with the other target parameters.
 
    .PARAMETER LunGroupId
        ID of the LUN group to associate. Mutually exclusive with the other target parameters.
 
    .PARAMETER HostName
        Name of the host to associate. Mutually exclusive with the other target parameters.
 
    .PARAMETER HostId
        ID of the host to associate. Mutually exclusive with the other target parameters.
 
    .PARAMETER VstoreName
        Name of the vStore to associate. Mutually exclusive with the other target parameters.
 
    .PARAMETER VstoreAssociationId
        ID of the vStore to associate. Mutually exclusive with the other target parameters.
 
    .PARAMETER ChildPolicyName
        Name of the child SmartQoS policy to associate (hierarchical policy). Mutually exclusive with the other target parameters.
 
    .PARAMETER ChildPolicyId
        ID of the child SmartQoS policy to associate (hierarchical policy). Mutually exclusive with the other target parameters.
 
    .INPUTS
        System.Management.Automation.PSCustomObject
 
    .OUTPUTS
        System.Object
 
    .EXAMPLE
        PS> Add-DMQosAssociation -Name 'qos01' -LunGroupName 'production-luns'
 
    .EXAMPLE
        PS> Add-DMQosAssociation -Id '1' -HostName 'esx01'
 
    .EXAMPLE
        PS> Add-DMQosAssociation -Name 'qos01-parent' -ChildPolicyName 'qos01-child'
 
    .NOTES
        Filename: Add-DMQosAssociation.ps1
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', '')]

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium', DefaultParameterSetName = 'ByName')]
    param(
        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [pscustomobject]$WebSession,

        [Parameter(Mandatory = $true, ParameterSetName = 'ByName', ValueFromPipelineByPropertyName = $true, Position = 0)]
        [ValidateNotNullOrEmpty()]
        [ArgumentCompleter({
                param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
                $session = if ($fakeBoundParameters.ContainsKey('WebSession')) {
                    $fakeBoundParameters.WebSession
                }
                else {
                    $script:CurrentOceanstorSession
                }
                (Get-DMQosPolicy -WebSession $session).Name | Sort-Object -Unique | Where-Object { $_ -like "$wordToComplete*" }
            })]
        [string]$Name,

        [Parameter(Mandatory = $true, ParameterSetName = 'ById', ValueFromPipelineByPropertyName = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$Id,

        [string]$LunGroupName,
        [string]$LunGroupId,
        [string]$HostName,
        [string]$HostId,
        [string]$VstoreName,
        [string]$VstoreAssociationId,
        [string]$ChildPolicyName,
        [string]$ChildPolicyId
    )

    begin {
        $targetParams = 'LunGroupName', 'LunGroupId', 'HostName', 'HostId', 'VstoreName', 'VstoreAssociationId', 'ChildPolicyName', 'ChildPolicyId'
        $specified = @($targetParams | Where-Object { $PSBoundParameters.ContainsKey($_) })
        if ($specified.Count -ne 1) {
            throw "Specify exactly one of: $($targetParams -join ', ')."
        }
    }

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

            $policy = switch ($PSCmdlet.ParameterSetName) {
                'ById' { @(Get-DMQosPolicy -WebSession $session -Id $Id)[0] }
                default { @(Get-DMQosPolicy -WebSession $session -Name $Name | Where-Object Name -EQ $Name)[0] }
            }
            if ($null -eq $policy) { throw 'Could not resolve the SmartQoS policy.' }

            if ($LunGroupName) {
                $group = @(Get-DMlunGroup -WebSession $session -Name $LunGroupName | Where-Object Name -EQ $LunGroupName)[0]
                if ($null -eq $group) { throw "Could not resolve LunGroupName '$LunGroupName'." }
                $associateObjType = 256
                $associateObjId = $group.Id
                $targetDescription = "LUN group '$LunGroupName'"
            }
            elseif ($LunGroupId) {
                $associateObjType = 256
                $associateObjId = $LunGroupId
                $targetDescription = "LUN group '$LunGroupId'"
            }
            elseif ($HostName) {
                $targetHost = @(Get-DMhost -WebSession $session -Name $HostName | Where-Object Name -EQ $HostName)[0]
                if ($null -eq $targetHost) { throw "Could not resolve HostName '$HostName'." }
                $associateObjType = 21
                $associateObjId = $targetHost.Id
                $targetDescription = "host '$HostName'"
            }
            elseif ($HostId) {
                $associateObjType = 21
                $associateObjId = $HostId
                $targetDescription = "host '$HostId'"
            }
            elseif ($VstoreName) {
                $vstore = @(Get-DMvStore -WebSession $session | Where-Object Name -EQ $VstoreName)[0]
                if ($null -eq $vstore) { throw "Could not resolve VstoreName '$VstoreName'." }
                $associateObjType = 16442
                $associateObjId = $vstore.Id
                $targetDescription = "vStore '$VstoreName'"
            }
            elseif ($VstoreAssociationId) {
                $associateObjType = 16442
                $associateObjId = $VstoreAssociationId
                $targetDescription = "vStore '$VstoreAssociationId'"
            }
            elseif ($ChildPolicyName) {
                $childPolicy = @(Get-DMQosPolicy -WebSession $session -Name $ChildPolicyName | Where-Object Name -EQ $ChildPolicyName)[0]
                if ($null -eq $childPolicy) { throw "Could not resolve ChildPolicyName '$ChildPolicyName'." }
                $associateObjType = 230
                $associateObjId = $childPolicy.Id
                $targetDescription = "child policy '$ChildPolicyName'"
            }
            else {
                $associateObjType = 230
                $associateObjId = $ChildPolicyId
                $targetDescription = "child policy '$ChildPolicyId'"
            }

            # Pre-validation: a SmartQoS (ioclass) policy binds to a single object type.
            # Associating an object whose type differs from the one already bound triggers
            # the array's ioclass object-type conflict (e.g. a policy already bound to a LUN
            # cannot also take a LUN group). Detect the current binding from the resolved
            # policy and fail with a clear message instead of firing a request the array
            # will reject. Empty entries are filtered so an absent list ('@($null)') does
            # not read as a binding.
            $boundTo = [ordered]@{
                'LUN'          = @($policy.'Lun List'    | Where-Object { $_ })
                'file system'  = @($policy.'FS List'     | Where-Object { $_ })
                'host'         = @($policy.'Host List'   | Where-Object { $_ })
                'child policy' = @($policy.'Policy List' | Where-Object { $_ })
            }
            # Adding an object of the same type as an existing binding is legitimate (a
            # second host, or another child policy on a hierarchical parent), so exclude the
            # compatible bucket from the conflict check. LUN group (256) and vStore (16442)
            # have no same-type list exposed on the policy object, so any existing binding
            # conflicts with them.
            $compatibleBinding = switch ($associateObjType) {
                21 { 'host' }
                230 { 'child policy' }
                default { $null }
            }
            $conflicts = foreach ($kind in $boundTo.Keys) {
                if ($kind -ne $compatibleBinding -and $boundTo[$kind].Count -gt 0) {
                    "$kind ($($boundTo[$kind] -join ', '))"
                }
            }
            if ($conflicts) {
                throw "SmartQoS policy '$($policy.Name)' is already associated with $($conflicts -join '; '). A SmartQoS policy can only be bound to one object type; remove the existing association before adding $targetDescription."
            }

            $body = @{
                ID                   = $policy.Id
                ASSOCIATEOBJTYPE     = $associateObjType
                ASSOCIATEOBJIDLIST   = @($associateObjId)
            }

            if ($PSCmdlet.ShouldProcess($policy.Name, "Associate $targetDescription")) {
                $response = Invoke-DeviceManager -WebSession $session -Method 'PUT' -Resource 'ioclass/create_associate' -BodyData $body
                $response = $response | Assert-DMApiSuccess
                return $response.error
            }
        }
        catch {
            $PSCmdlet.WriteError($_)
        }
    }
}