WSAPI/CpgManagement.psm1

####################################################################################
## © 2020,2021 Hewlett Packard Enterprise Development LP
##
## Permission is hereby granted, free of charge, to any person obtaining a
## copy of this software and associated documentation files (the "Software"),
## to deal in the Software without restriction, including without limitation
## the rights to use, copy, modify, merge, publish, distribute, sublicense,
## and/or sell copies of the Software, and to permit persons to whom the
## Software is furnished to do so, subject to the following conditions:
##
## The above copyright notice and this permission notice shall be included
## in all copies or substantial portions of the Software.
##
## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
## THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
## OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
## ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
## OTHER DEALINGS IN THE SOFTWARE.
##
## File Name: CpgManagement.psm1
## Description: CPG Management cmdlets
##
## Created: February 2020
## Last Modified: February 2020
## History: v3.0 - Created
#####################################################################################

$Info = "INFO:"
$Debug = "DEBUG:"
$global:VSLibraries = Split-Path $MyInvocation.MyCommand.Path
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

############################################################################################################################################
## FUNCTION New-Cpg_WSAPI
############################################################################################################################################
Function New-Cpg_WSAPI 
{
  <#
  
  .SYNOPSIS
    The New-Cpg_WSAPI command creates a Common Provisioning Group (CPG).
  
  .DESCRIPTION
    The New-Cpg_WSAPI command creates a Common Provisioning Group (CPG).
        
  .EXAMPLE
    New-Cpg_WSAPI -CPGName XYZ
        
  .EXAMPLE
    New-Cpg_WSAPI -CPGName "MyCPG" -Domain Chef_Test
        
  .EXAMPLE
    New-Cpg_WSAPI -CPGName "MyCPG" -Domain Chef_Test -Template Test_Temp
        
  .EXAMPLE
    New-Cpg_WSAPI -CPGName "MyCPG" -Domain Chef_Test -Template Test_Temp -GrowthIncrementMiB 100
        
  .EXAMPLE
    New-Cpg_WSAPI -CPGName "MyCPG" -Domain Chef_Test -RAIDType R0

  .PARAMETER CPGName
    Specifies the name of the CPG.

  .PARAMETER Domain
    Specifies the name of the domain in which the object will reside.

  .PARAMETER Template
    Specifies the name of the template from which the CPG is created.
    
  .PARAMETER GrowthIncrementMiB
    Specifies the growth increment, in MiB, the amount of logical disk storage created on each auto-grow operation.
    
  .PARAMETER GrowthLimitMiB
    Specifies that the autogrow operation is limited to the specified storage amount, in MiB, that sets the growth limit.
    
  .PARAMETER UsedLDWarningAlertMiB
    Specifies that the threshold of used logical disk space, in MiB, when exceeded results in a warning alert.
      
  .PARAMETER RAIDType
    RAID type for the logical disk
    R0 RAID level 0
    R1 RAID level 1
    R5 RAID level 5
    R6 RAID level 6
      
  .PARAMETER SetSize
    Specifies the set size in the number of chunklets.
      
  .PARAMETER HA
    Specifies that the layout must support the failure of one port pair, one cage, or one magazine.
    PORT Support failure of a port.
    CAGE Support failure of a drive cage.
    MAG Support failure of a drive magazine.
    
  .PARAMETER Chunklets
    FIRST Lowest numbered available chunklets, where transfer rate is the fastest.
    LAST Highest numbered available chunklets, where transfer rate is the slowest.
    
  .PARAMETER NodeList
    Specifies one or more nodes. Nodes are identified by one or more integers. Multiple nodes are separated with a single comma (1,2,3).
    A range of nodes is separated with a hyphen (0–7). The primary path of the disks must be on the specified node number.
    
  .PARAMETER SlotList
    Specifies one or more PCI slots. Slots are identified by one or more integers. Multiple slots are separated with a single comma (1,2,3).
    A range of slots is separated with a hyphen (0–7). The primary path of the disks must be on the specified PCI slot number(s).
    
  .PARAMETER PortList
    Specifies one or more ports. Ports are identified by one or more integers. Multiple ports are separated with a single comma (1,2,3).
    A range of ports is separated with a hyphen (0–4). The primary path of the disks must be on the specified port number(s).
    
  .PARAMETER CageList
    Specifies one or more drive cages. Drive cages are identified by one or more integers. Multiple drive cages are separated with a single comma (1,2,3).
    A range of drive cages is separated with a hyphen (0– 3). The specified drive cage(s) must contain disks.
    
  .PARAMETER MagList
    Specifies one or more drive magazines. Drive magazines are identified by one or more integers. Multiple drive magazines are separated with a single comma (1,2,3).
    A range of drive magazines is separated with a hyphen (0–7). The specified magazine(s) must contain disks.
    
  .PARAMETER DiskPosList
    Specifies one or more disk positions within a drive magazine. Disk positions are identified by one or more integers. Multiple disk positions are separated with a single comma (1,2,3).
    A range of disk positions is separated with a hyphen (0–3). The specified portion(s) must contain disks.
    
  .PARAMETER DiskList
    Specifies one or more physical disks. Disks are identified by one or more integers. Multiple disks are separated with a single comma (1,2,3).
    A range of disks is separated with a hyphen (0–3). Disks must match the specified ID(s).
    
  .PARAMETER TotalChunkletsGreaterThan
    Specifies that physical disks with total chunklets greater than the number specified be selected.
    
  .PARAMETER TotalChunkletsLessThan
    Specifies that physical disks with total chunklets less than the number specified be selected.
    
  .PARAMETER FreeChunkletsGreaterThan
    Specifies that physical disks with free chunklets less than the number specified be selected.
    
  .PARAMETER FreeChunkletsLessThan
     Specifies that physical disks with free chunklets greater than the number specified be selected.
     
  .PARAMETER DiskType
    Specifies that physical disks must have the specified device type.
    FC Fibre Channel
    NL Near Line
    SSD SSD
      
  .PARAMETER Rpm
    Disks must be of the specified speed.

  .PARAMETER WsapiConnection
    WSAPI Connection object created with Connection command
    
  .Notes
    NAME : New-Cpg_WSAPI
    LASTEDIT: February 2020
    KEYWORDS: New-Cpg_WSAPI
   
  .Link
     http://www.hpe.com
 
  #Requires PS -Version 3.0
  
  #>

  [CmdletBinding()]
  Param(
      [Parameter(Mandatory = $true,ValueFromPipeline=$True,ValueFromPipelinebyPropertyName=$True,HelpMessage = 'Specifies the name of the CPG.')]
      [String]
      $CPGName,
      
      [Parameter(Mandatory = $false,HelpMessage = 'Specifies the name of the domain in which the object will reside.')]
      [String]
      $Domain = $null,
      
      [Parameter(Mandatory = $false,HelpMessage = 'Specifies the name of the template from which the CPG is created')]
      [String]
      $Template = $null,
      
      [Parameter(Mandatory = $false,HelpMessage = 'Specifies the growth increment, in MiB, the amount of logical disk storage created on each auto-grow operation')]
      [Int]
      $GrowthIncrementMiB = $null,
      
      [Parameter(Mandatory = $false,HelpMessage = 'Specifies that the autogrow operation is limited to the specified storage amount, in MiB, that sets the growth limit')]
      [int]
      $GrowthLimitMiB = $null,
      
      [Parameter(Mandatory = $false,HelpMessage = 'Specifies that the threshold of used logical disk space, in MiB, when exceeded results in a warning alert')]
      [int]
      $UsedLDWarningAlertMiB = $null,
      
      [Parameter(Mandatory = $false,HelpMessage = 'RAIDType R0,R1,R5 and R6 only.')]
      [string]
      $RAIDType = $null, 
      
      [Parameter(Mandatory = $false,HelpMessage = 'Specifies the set size in the number of chunklets.')]
      [int]
      $SetSize = $null,
      
      [Parameter(Mandatory = $false,HelpMessage = 'Specifies that the layout must support the failure of one port pair, one cage, or one magazine.')]
      [string]
      $HA = $null,
      
      [Parameter(Mandatory = $false,HelpMessage = 'Specifies the chunklet location preference characteristics.')]
      [string]
      $Chunklets = $null,
      
      [Parameter(Mandatory = $false,HelpMessage = 'Specifies one or more nodes. Nodes are identified by one or more integers.')]
      [String]
      $NodeList = $null,
      
      [Parameter(Mandatory = $false,HelpMessage = 'Specifies one or more nodes. Nodes are identified by one or more integers.')]
      [String]
      $SlotList = $null,
      
      [Parameter(Mandatory = $false,HelpMessage = 'Specifies one or more ports. Ports are identified by one or more integers..')]
      [String]
      $PortList = $null,
      
      [Parameter(Mandatory = $false,HelpMessage = 'Specifies one or more drive cages. Drive cages are identified by one or more integers.')]
      [String]
      $CageList = $null,
      
      [Parameter(Mandatory = $false,HelpMessage = 'Specifies one or more drive magazines. Drive magazines are identified by one or more integers..')]
      [String]
      $MagList = $null,
      
      [Parameter(Mandatory = $false,HelpMessage = 'Specifies one or more disk positions within a drive magazine. Disk positions are identified by one or more integers.')]
      [String]
      $DiskPosList = $null,
      
      [Parameter(Mandatory = $false,HelpMessage = 'Specifies one or more physical disks. Disks are identified by one or more integers.')]
      [String]
      $DiskList = $null,
      
      [Parameter(Mandatory = $false,HelpMessage = 'Specifies that physical disks with total chunklets greater than the number specified be selected.')]
      [int]
      $TotalChunkletsGreaterThan = $null,
      
      [Parameter(Mandatory = $false,HelpMessage = 'Specifies that physical disks with total chunklets less than the number specified be selected.')]
      [int]
      $TotalChunkletsLessThan = $null,
      
      [Parameter(Mandatory = $false,HelpMessage = 'Specifies that physical disks with free chunklets less than the number specified be selected.')]
      [int]
      $FreeChunkletsGreaterThan = $null,
      
      [Parameter(Mandatory = $false,HelpMessage = 'Specifies that physical disks with free chunklets greater than the number specified be selected.')]
      [int]
      $FreeChunkletsLessThan = $null,
      
      [Parameter(Mandatory = $false,HelpMessage = 'Specifies that physical disks must have the specified device type, FC (Fibre Channel) 2 is for NL (Near Line) 3 is for SSD .')]
      [string]
      $DiskType = $null,
      
      [Parameter(Mandatory = $false,HelpMessage = 'Disks must be of the specified speed')]
      [int]
      $Rpm = $null,
      
      [Parameter(Mandatory=$false, HelpMessage = 'Connection Paramater' ,ValueFromPipeline=$true)]
      $WsapiConnection = $global:WsapiConnection 
  )

  Begin 
  {
    # Test if connection exist
    Test-WSAPIConnection -WsapiConnection $WsapiConnection
  }

  Process 
  {
    # Creation of the body hash
    Write-DebugLog "Running: Creation of the body hash" $Debug
    $body = @{}    
        
    # Name parameter
    $body["name"] = "$($CPGName)"

    # Domain parameter
    If ($Domain) 
    {
        $body["domain"] = "$($Domain)"
    }

    # Template parameter
    If ($Template) 
    {
        $body["template"] = "$($Template)"
    } 

    # Template parameter
    If ($GrowthIncrementMiB) 
    {
        $body["growthIncrementMiB"] = $GrowthIncrementMiB
    } 
    
    # Template parameter
    If ($GrowthLimitMiB) 
    {
        $body["growthLimitMiB"] = $GrowthLimitMiB
    } 
    
    # Template parameter
    If ($UsedLDWarningAlertMiB) 
    {
        $body["usedLDWarningAlertMiB"] = $UsedLDWarningAlertMiB
    } 
    
    $LDLayoutBody = @{}
    # LDLayout
    #Specifies the RAID type for the logical disk
    if ($RAIDType)
    {        
        if($RAIDType -eq "R0")
        {
            $LDLayoutBody["RAIDType"] = 1
        }
        elseif($RAIDType -eq "R1")
        {
            $LDLayoutBody["RAIDType"] = 2
        }
        elseif($RAIDType -eq "R5")
        {
            $LDLayoutBody["RAIDType"] = 3
        }
        elseif($RAIDType -eq "R6")
        {
            $LDLayoutBody["RAIDType"] = 4
        }
        else
        {
            Write-DebugLog "Stop: Exiting New-Cpg_WSAPI since RAIDType $RAIDType in incorrect "
            Return "FAILURE : RAIDType :- $RAIDType is an Incorrect Please Use RAIDType R0,R1,R5 and R6 only. "
        }        
    }
    #Specifies the set size in the number of chunklets.
    if ($SetSize)
    {    
        $LDLayoutBody["setSize"] = $SetSize                
    }
    #Specifies that the layout must support the failure of one port pair, one cage, or one magazine.
    if ($HA)
    {
        if($HA -eq "PORT")
        {
            $LDLayoutBody["HA"] = 1                    
        }
        elseif($HA -eq "CAGE")
        {
            $LDLayoutBody["HA"] = 2                    
        }
        elseif($HA -eq "MAG")
        {
            $LDLayoutBody["HA"] = 3                    
        }
        else
        { 
            Write-DebugLog "Stop: Exiting New-Cpg_WSAPI since HA $HA in incorrect "
            Return "FAILURE : HA :- $HA is an Incorrect Please Use [ PORT | CAGE | MAG ] only "
        }
    }
    #Specifies the chunklet location preference characteristics
    if ($Chunklets)
    {        
        if($Chunklets -eq "FIRST")
        {
            $LDLayoutBody["chunkletPosPref"] = 1                    
        }
        elseif($Chunklets -eq "LAST")
        {
            $LDLayoutBody["chunkletPosPref"] = 2                    
        }
        else
        { 
            Write-DebugLog "Stop: Exiting New-Cpg_WSAPI since Chunklets $Chunklets in incorrect "
            Return "FAILURE : Chunklets :- $Chunklets is an Incorrect Please Use Chunklets FIRST and LAST only. "
        }
    }
    
    $LDLayoutDiskPatternsBody=@()    
    
    if ($NodeList)
    {
        $nodList=@{}
        $nodList["nodeList"] = "$($NodeList)"    
        $LDLayoutDiskPatternsBody += $nodList             
    }
    
    if ($SlotList)
    {
        $sList=@{}
        $sList["slotList"] = "$($SlotList)"    
        $LDLayoutDiskPatternsBody += $sList         
    }
    
    if ($PortList)
    {
        $pList=@{}
        $pList["portList"] = "$($PortList)"    
        $LDLayoutDiskPatternsBody += $pList         
    }
    
    if ($CageList)
    {
        $cagList=@{}
        $cagList["cageList"] = "$($CageList)"    
        $LDLayoutDiskPatternsBody += $cagList         
    }
    
    if ($MagList)
    {
        $mList=@{}
        $mList["magList"] = "$($MagList)"    
        $LDLayoutDiskPatternsBody += $mList         
    }
    
    if ($DiskPosList)
    {
        $dpList=@{}
        $dpList["diskPosList"] = "$($DiskPosList)"    
        $LDLayoutDiskPatternsBody += $dpList         
    }

    if ($DiskList)
    {
        $dskList=@{}
        $dskList["diskList"] = "$($DiskList)"    
        $LDLayoutDiskPatternsBody += $dskList         
    }
    
    if ($TotalChunkletsGreaterThan)
    {
        $tcgList=@{}
        $tcgList["totalChunkletsGreaterThan"] = $TotalChunkletsGreaterThan    
        $LDLayoutDiskPatternsBody += $tcgList         
    }
    
    if ($TotalChunkletsLessThan)
    {
        $tclList=@{}
        $tclList["totalChunkletsLessThan"] = $TotalChunkletsLessThan    
        $LDLayoutDiskPatternsBody += $tclList         
    }
    
    if ($FreeChunkletsGreaterThan)
    {
        $fcgList=@{}
        $fcgList["freeChunkletsGreaterThan"] = $FreeChunkletsGreaterThan    
        $LDLayoutDiskPatternsBody += $fcgList         
    }
    
    if ($FreeChunkletsLessThan)
    {
        $fclList=@{}
        $fclList["freeChunkletsLessThan"] = $FreeChunkletsLessThan    
        $LDLayoutDiskPatternsBody += $fclList         
    }
    
    if ($DiskType)
    {        
        if($DiskType -eq "FC")
        {            
            $dtList=@{}
            $dtList["diskType"] = 1    
            $LDLayoutDiskPatternsBody += $dtList                        
        }
        elseif($DiskType -eq "NL")
        {            
            $dtList=@{}
            $dtList["diskType"] = 2    
            $LDLayoutDiskPatternsBody += $dtList                        
        }
        elseif($DiskType -eq "SSD")
        {            
            $dtList=@{}
            $dtList["diskType"] = 3    
            $LDLayoutDiskPatternsBody += $dtList                        
        }
        else
        { 
            Write-DebugLog "Stop: Exiting New-Cpg_WSAPI since DiskType $DiskType in incorrect "
            Return "FAILURE : DiskType :- $DiskType is an Incorrect Please Use FC (Fibre Channel), NL (Near Line) and SSD only"
        }
    }
    
    if ($Rpm)
    {
        $rpmList=@{}
        $rpmList["RPM"] = $Rpm    
        $LDLayoutDiskPatternsBody += $rpmList
    }    
        
    
    if($LDLayoutDiskPatternsBody.Count -gt 0)
    {
        $LDLayoutBody["diskPatterns"] = $LDLayoutDiskPatternsBody    
    }        
    if($LDLayoutBody.Count -gt 0)
    {
        $body["LDLayout"] = $LDLayoutBody 
    }    
    
    #init the response var
    $Result = $null    
    
    #$json = $body | ConvertTo-Json -Compress -Depth 10
    #write-host " Body = $json"
    
    #Request
    $Result = Invoke-WSAPI -uri '/cpgs' -type 'POST' -body $body -WsapiConnection $WsapiConnection
    $status = $Result.StatusCode
    if($status -eq 201)
    {
        write-host ""
        write-host "Cmdlet executed successfully" -foreground green
        write-host ""
        Write-DebugLog "SUCCESS: CPG:$CPGName created successfully" $Info
        
        #write-host " StatusCode = $status"
        # Results
        Get-Cpg_WSAPI -CPGName $CPGName
        Write-DebugLog "End: New-Cpg_WSAPI" $Debug
    }
    else
    {
        write-host ""
        write-host "FAILURE : While creating CPG:$CPGName " -foreground red
        write-host ""
        Write-DebugLog "FAILURE : While creating CPG:$CPGName " $Info
        return $Result.StatusDescription
    }    
  }
  End 
  {
  }  
}
#ENG New-Cpg_WSAPI

############################################################################################################################################
## FUNCTION Update-Cpg_WSAPI
############################################################################################################################################
Function Update-Cpg_WSAPI 
{
  <#
  
  .SYNOPSIS
    The Update-Cpg_WSAPI command Update a Common Provisioning Group (CPG).
  
  .DESCRIPTION
    The Update-Cpg_WSAPI command Update a Common Provisioning Group (CPG).
    This operation requires access to all domains, as well as Super, Service, or Edit roles, or any role granted cpg_set permission.
    
  .EXAMPLE
    Update-Cpg_WSAPI -CPGName ascpg -NewName as_cpg
    
  .EXAMPLE
    Update-Cpg_WSAPI -CPGName xxx -RAIDType R1
    
  .EXAMPLE
    Update-Cpg_WSAPI -CPGName xxx -DisableAutoGrow $true
    
  .EXAMPLE
    Update-Cpg_WSAPI -CPGName xxx -RmGrowthLimit $true
    
  .EXAMPLE
    Update-Cpg_WSAPI -CPGName xxx -RmWarningAlert $true
        
  .EXAMPLE
    Update-Cpg_WSAPI -CPGName xxx -SetSize 10
    
  .EXAMPLE
    Update-Cpg_WSAPI -CPGName xxx -HA PORT
    
  .EXAMPLE
    Update-Cpg_WSAPI -CPGName xxx -Chunklets FIRST
    
  .EXAMPLE
    Update-Cpg_WSAPI -CPGName xxx -NodeList 0
        
  .PARAMETER CPGName,
    pecifies the name of Existing CPG.

  .PARAMETER NewName,
    Specifies the name of CPG to Update.

  .PARAMETER RmGrowthLimit
    Enables (false) or disables (true) auto grow limit enforcement. Defaults to false.

  .PARAMETER DisableAutoGrow
    Enables (false) or disables (true) CPG auto grow. Defaults to false..
    
  .PARAMETER RmWarningAlert
    Enables (false) or disables (true) warning limit enforcement. Defaults to false..
      
  .PARAMETER RAIDType
    RAID type for the logical disk
    R0 RAID level 0
    R1 RAID level 1
    R5 RAID level 5
    R6 RAID level 6
      
  .PARAMETER SetSize
    Specifies the set size in the number of chunklets.
      
  .PARAMETER HA
    Specifies that the layout must support the failure of one port pair, one cage, or one magazine.
    PORT Support failure of a port.
    CAGE Support failure of a drive cage.
    MAG Support failure of a drive magazine.
    
  .PARAMETER Chunklets
    FIRST Lowest numbered available chunklets, where transfer rate is the fastest.
    LAST Highest numbered available chunklets, where transfer rate is the slowest.
    
  .PARAMETER NodeList
    Specifies one or more nodes. Nodes are identified by one or more integers. Multiple nodes are separated with a single comma (1,2,3).
    A range of nodes is separated with a hyphen (0–7). The primary path of the disks must be on the specified node number.
    
  .PARAMETER SlotList
    Specifies one or more PCI slots. Slots are identified by one or more integers. Multiple slots are separated with a single comma (1,2,3).
    A range of slots is separated with a hyphen (0–7). The primary path of the disks must be on the specified PCI slot number(s).
    
  .PARAMETER PortList
    Specifies one or more ports. Ports are identified by one or more integers. Multiple ports are separated with a single comma (1,2,3).
    A range of ports is separated with a hyphen (0–4). The primary path of the disks must be on the specified port number(s).
    
  .PARAMETER CageList
    Specifies one or more drive cages. Drive cages are identified by one or more integers. Multiple drive cages are separated with a single comma (1,2,3).
    A range of drive cages is separated with a hyphen (0– 3). The specified drive cage(s) must contain disks.
    
  .PARAMETER MagList
    Specifies one or more drive magazines. Drive magazines are identified by one or more integers. Multiple drive magazines are separated with a single comma (1,2,3).
    A range of drive magazines is separated with a hyphen (0–7). The specified magazine(s) must contain disks.
    
  .PARAMETER DiskPosList
    Specifies one or more disk positions within a drive magazine. Disk positions are identified by one or more integers. Multiple disk positions are separated with a single comma (1,2,3).
    A range of disk positions is separated with a hyphen (0–3). The specified portion(s) must contain disks.
    
  .PARAMETER DiskList
    Specifies one or more physical disks. Disks are identified by one or more integers. Multiple disks are separated with a single comma (1,2,3).
    A range of disks is separated with a hyphen (0–3). Disks must match the specified ID(s).
    
  .PARAMETER TotalChunkletsGreaterThan
    Specifies that physical disks with total chunklets greater than the number specified be selected.
    
  .PARAMETER TotalChunkletsLessThan
    Specifies that physical disks with total chunklets less than the number specified be selected.
    
  .PARAMETER FreeChunkletsGreaterThan
    Specifies that physical disks with free chunklets less than the number specified be selected.
    
  .PARAMETER FreeChunkletsLessThan
     Specifies that physical disks with free chunklets greater than the number specified be selected.
     
  .PARAMETER DiskType
    Specifies that physical disks must have the specified device type.
    FC Fibre Channel
    NL Near Line
    SSD SSD
      
  .PARAMETER Rpm
    Disks must be of the specified speed.
    
  .PARAMETER WsapiConnection
    WSAPI Connection object created with Connection command
    
  .Notes
    NAME : Update-Cpg_WSAPI
    LASTEDIT: February 2020
    KEYWORDS: Update-Cpg_WSAPI
   
  .Link
     http://www.hpe.com
 
  #Requires PS -Version 3.0
  
  #>


  [CmdletBinding()]
    Param(
    [Parameter(Mandatory = $true,ValueFromPipeline=$True,ValueFromPipelinebyPropertyName=$True,HelpMessage = 'Specifies the name of Existing CPG.')]
    [String]$CPGName,
    
    [Parameter(Mandatory = $false,HelpMessage = 'Specifies the name of CPG to Update.')]
    [String]
    $NewName,
    
    [Parameter(Mandatory = $false,HelpMessage = 'Enables (false) or disables (true) CPG auto grow. Defaults to false.')]
    [Boolean]
    $DisableAutoGrow = $false,
    
    [Parameter(Mandatory = $false,HelpMessage = 'Enables (false) or disables (true) auto grow limit enforcement. Defaults to false.')]
    [Boolean]
    $RmGrowthLimit = $false,
    
    [Parameter(Mandatory = $false,HelpMessage = 'Enables (false) or disables (true) warning limit enforcement. Defaults to false.')]
    [Boolean]
    $RmWarningAlert = $false,
    
    [Parameter(Mandatory = $false,HelpMessage = 'RAIDType enumeration 1 is for R0, 2 is for R1,3 is for R5, 4 is for R6')]
    [string]
    $RAIDType = $null, 
    
    [Parameter(Mandatory = $false,HelpMessage = 'Specifies the set size in the number of chunklets.')]
    [int]
    $SetSize = $null,
    
    [Parameter(Mandatory = $false,HelpMessage = 'Specifies that the layout must support the failure of one port pair, one cage, or one magazine.')]
    [string]
    $HA = $null,
    
    [Parameter(Mandatory = $false,HelpMessage = 'Specifies the chunklet location preference characteristics.')]
    [string]
    $Chunklets = $null,
    
    [Parameter(Mandatory = $false,HelpMessage = 'Specifies one or more nodes. Nodes are identified by one or more integers.')]
    [String]
    $NodeList = $null,
    
    [Parameter(Mandatory = $false,HelpMessage = 'Specifies one or more nodes. Nodes are identified by one or more integers.')]
    [String]
    $SlotList = $null,
    
    [Parameter(Mandatory = $false,HelpMessage = 'Specifies one or more ports. Ports are identified by one or more integers..')]
    [String]
    $PortList = $null,
    
    [Parameter(Mandatory = $false,HelpMessage = 'Specifies one or more drive cages. Drive cages are identified by one or more integers.')]
    [String]
    $CageList = $null,
    
    [Parameter(Mandatory = $false,HelpMessage = 'Specifies one or more drive magazines. Drive magazines are identified by one or more integers..')]
    [String]
    $MagList = $null,
    
    [Parameter(Mandatory = $false,HelpMessage = 'Specifies one or more disk positions within a drive magazine. Disk positions are identified by one or more integers.')]
    [String]
    $DiskPosList = $null,
    
    [Parameter(Mandatory = $false,HelpMessage = 'Specifies one or more physical disks. Disks are identified by one or more integers.')]
    [String]
    $DiskList = $null,
    
    [Parameter(Mandatory = $false,HelpMessage = 'Specifies that physical disks with total chunklets greater than the number specified be selected.')]
    [int]
    $TotalChunkletsGreaterThan = $null,
    
    [Parameter(Mandatory = $false,HelpMessage = 'Specifies that physical disks with total chunklets less than the number specified be selected.')]
    [int]
    $TotalChunkletsLessThan = $null,
    
    [Parameter(Mandatory = $false,HelpMessage = 'Specifies that physical disks with free chunklets less than the number specified be selected.')]
    [int]
    $FreeChunkletsGreaterThan = $null,
    
    [Parameter(Mandatory = $false,HelpMessage = 'Specifies that physical disks with free chunklets greater than the number specified be selected.')]
    [int]
    $FreeChunkletsLessThan = $null,
    
    [Parameter(Mandatory = $false,HelpMessage = 'Specifies that physical disks must have the specified device type .')]
    [int]
    $DiskType = $null,
    
    [Parameter(Mandatory = $false,HelpMessage = 'Disks must be of the specified speed 1 is for FC (Fibre Channel) 2 is for NL (Near Line) 3 is for SSD.')]
    [int]
    $Rpm = $null,
    
    [Parameter(Mandatory=$false, ValueFromPipeline=$true , HelpMessage = 'Connection Paramater')]
    $WsapiConnection = $global:WsapiConnection
  )

  Begin 
  {
    # Test if connection exist
    Test-WSAPIConnection -WsapiConnection $WsapiConnection
  }

  Process 
  {
    # Creation of the body hash
    Write-DebugLog "Running: Creation of the body hash" $Debug
    $body = @{}

    # New Name parameter
    If ($NewName) { $body["newName"] ="$($NewName)" } 
    
    <#
    switch($DisableAutoGrow)
    {
     {$_ -eq $true} {$body["disableAutoGrow"] =$DisableAutoGrow ;break;}
     {$_ -eq $false} {$body["disableAutoGrow"] =$DisableAutoGrow ;break;}
    }
    #>

    
    # Disable Auto Growth
    If($DisableAutoGrow) { $body["disableAutoGrow"] =$DisableAutoGrow } #else { $body["disableAutoGrow"] =$DisableAutoGrow}

    # rm Growth Limit
    If($RmGrowthLimit) { $body["rmGrowthLimit"] = $RmGrowthLimit } #else { $body["rmGrowthLimit"] = $RmGrowthLimit }

    # rm Warning Alert
    If($RmWarningAlert) { $body["rmWarningAlert"] = $RmWarningAlert } #else { $body["rmWarningAlert"] = $RmWarningAlert }
    
    $LDLayoutBody = @{}
    # LDLayout
    #Specifies the RAID type for the logical disk
    if($RAIDType)
    {    
        if($RAIDType -eq "R0")
        {
            $LDLayoutBody["RAIDType"] = 1
        }
        elseif($RAIDType -eq "R1")
        {
            $LDLayoutBody["RAIDType"] = 2
        }
        elseif($RAIDType -eq "R5")
        {
            $LDLayoutBody["RAIDType"] = 3
        }
        elseif($RAIDType -eq "R6")
        {
            $LDLayoutBody["RAIDType"] = 4
        }
        else
        {
            Write-DebugLog "Stop: Exiting Update-Cpg_WSAPI since RAIDType $RAIDType in incorrect "
            Return "FAILURE : RAIDType :- $RAIDType is an Incorrect Please Use RAIDType R0,R1,R5 and R6 only. "
        }
    }
    #Specifies the set size in the number of chunklets.
    if($SetSize)
    {    
        $LDLayoutBody["setSize"] = $SetSize                
    }
    #Specifies that the layout must support the failure of one port pair, one cage, or one magazine.
    if($HA)
    {
        if($HA -eq "PORT")
        {
            $LDLayoutBody["HA"] = 1                    
        }
        elseif($HA -eq "CAGE")
        {
            $LDLayoutBody["HA"] = 2                    
        }
        elseif($HA -eq "MAG")
        {
            $LDLayoutBody["HA"] = 3                    
        }
        else
        { 
            Write-DebugLog "Stop: Exiting Update-Cpg_WSAPI since HA $HA in incorrect "
            Return "FAILURE : HA :- $HA is an Incorrect Please Use PORT,CAGE and MAG only "
        }
    }
    #Specifies the chunklet location preference characteristics
    if ($Chunklets)
    {        
        if($Chunklets -eq "FIRST")
        {
            $LDLayoutBody["chunkletPosPref"] = 1                    
        }
        elseif($Chunklets -eq "LAST")
        {
            $LDLayoutBody["chunkletPosPref"] = 2                    
        }
        else
        { 
            Write-DebugLog "Stop: Exiting Update-Cpg_WSAPI since Chunklets $Chunklets in incorrect "
            Return "FAILURE : Chunklets :- $Chunklets is an Incorrect Please Use Chunklets FIRST and LAST only. "
        }
    }    
        
    $LDLayoutDiskPatternsBody=@()    
    
    if ($NodeList)
    {
        $nodList=@{}
        $nodList["nodeList"] = "$($NodeList)"    
        $LDLayoutDiskPatternsBody += $nodList             
    }
    
    if ($SlotList)
    {
        $sList=@{}
        $sList["slotList"] = "$($SlotList)"    
        $LDLayoutDiskPatternsBody += $sList         
    }
    
    if ($PortList)
    {
        $pList=@{}
        $pList["portList"] = "$($PortList)"    
        $LDLayoutDiskPatternsBody += $pList         
    }
    
    if ($CageList)
    {
        $cagList=@{}
        $cagList["cageList"] = "$($CageList)"    
        $LDLayoutDiskPatternsBody += $cagList         
    }
    
    if ($MagList)
    {
        $mList=@{}
        $mList["magList"] = "$($MagList)"    
        $LDLayoutDiskPatternsBody += $mList         
    }
    
    if ($DiskPosList)
    {
        $dpList=@{}
        $dpList["diskPosList"] = "$($DiskPosList)"    
        $LDLayoutDiskPatternsBody += $dpList         
    }

    if ($DiskList)
    {
        $dskList=@{}
        $dskList["diskList"] = "$($DiskList)"    
        $LDLayoutDiskPatternsBody += $dskList         
    }
    
    if ($TotalChunkletsGreaterThan)
    {
        $tcgList=@{}
        $tcgList["totalChunkletsGreaterThan"] = $TotalChunkletsGreaterThan    
        $LDLayoutDiskPatternsBody += $tcgList         
    }
    
    if ($TotalChunkletsLessThan)
    {
        $tclList=@{}
        $tclList["totalChunkletsLessThan"] = $TotalChunkletsLessThan    
        $LDLayoutDiskPatternsBody += $tclList         
    }
    
    if ($FreeChunkletsGreaterThan)
    {
        $fcgList=@{}
        $fcgList["freeChunkletsGreaterThan"] = $FreeChunkletsGreaterThan    
        $LDLayoutDiskPatternsBody += $fcgList         
    }
    
    if ($FreeChunkletsLessThan)
    {
        $fclList=@{}
        $fclList["freeChunkletsLessThan"] = $FreeChunkletsLessThan    
        $LDLayoutDiskPatternsBody += $fclList         
    }
    
    if ($DiskType)
    {        
        if($DiskType -eq "FC")
        {            
            $dtList=@{}
            $dtList["diskType"] = 1    
            $LDLayoutDiskPatternsBody += $dtList                        
        }
        elseif($DiskType -eq "NL")
        {            
            $dtList=@{}
            $dtList["diskType"] = 2    
            $LDLayoutDiskPatternsBody += $dtList                        
        }
        elseif($DiskType -eq "SSD")
        {            
            $dtList=@{}
            $dtList["diskType"] = 3    
            $LDLayoutDiskPatternsBody += $dtList                        
        }
        else
        { 
            Write-DebugLog "Stop: Exiting Update-Cpg_WSAPI since DiskType $DiskType in incorrect "
            Return "FAILURE : DiskType :- $DiskType is an Incorrect Please Use FC (Fibre Channel), NL (Near Line) and SSD only"
        }
    }
    
    if ($Rpm)
    {
        $rpmList=@{}
        $rpmList["RPM"] = $Rpm    
        $LDLayoutDiskPatternsBody += $rpmList
    }    
        
    
    if($LDLayoutDiskPatternsBody.Count -gt 0)    {$LDLayoutBody["diskPatterns"] = $LDLayoutDiskPatternsBody    }        
    if($LDLayoutBody.Count -gt 0){$body["LDLayout"] = $LDLayoutBody }
    
    #$json = $body | ConvertTo-Json -Compress -Depth 10
    #write-host " Body = $json"
    
    Write-DebugLog "Info:Body : $body" $Info    
    $Result = $null
    
    #Build uri
    $uri = '/cpgs/'+$CPGName    
    #Request
    $Result = Invoke-WSAPI -uri $uri -type 'PUT' -body $body -WsapiConnection $WsapiConnection
    
    $status = $Result.StatusCode
    if($status -eq 200)
    {    
        write-host ""
        write-host "Cmdlet executed successfully" -foreground green
        write-host ""
        Write-DebugLog "SUCCESS: CPG:$CPGName successfully Updated" $Info
        # Results
        if($NewName)
        {
            Get-Cpg_WSAPI -CPGName $NewName
        }
        else
        {
            Get-Cpg_WSAPI -CPGName $CPGName
        }
        Write-DebugLog "End: Update-Cpg_WSAPI" $Debug
    }
    else
    {
        write-host ""
        write-host "FAILURE : While Updating CPG:$CPGName " -foreground red
        write-host ""
        Write-DebugLog "FAILURE : While creating CPG:$CPGName " $Info
        
        return $Result.StatusDescription
    }
  }
  End 
  {
  }
}
#END Update-Cpg_WSAPI


############################################################################################################################################
## FUNCTION Remove-Cpg_WSAPI
############################################################################################################################################
Function Remove-Cpg_WSAPI
 {
  <#
    
  .SYNOPSIS
    Removes a Common Provision Group(CPG).
  
  .DESCRIPTION
    Removes a CommonProvisionGroup(CPG)
    This operation requires access to all domains, as well as Super, or Edit roles, or any role granted cpg_remove permission.
    
  .EXAMPLE
    Remove-Cpg_WSAPI -CPGName MyCPG
    Removes a Common Provision Group(CPG) "MyCPG".
    
  .PARAMETER CPGName
    Specify name of the CPG.
    
  .PARAMETER WsapiConnection
    WSAPI Connection object created with Connection command
    
  .Notes
    NAME : Remove-Cpg_WSAPI
    LASTEDIT: February 2020
    KEYWORDS: Remove-Cpg_WSAPI
   
  .Link
     http://www.hpe.com
 
  #Requires PS -Version 3.0
    
  #>

  [CmdletBinding(SupportsShouldProcess = $True,ConfirmImpact = 'High')]
  Param(
    [Parameter(Mandatory = $true,ValueFromPipeline=$True,ValueFromPipelinebyPropertyName=$True,HelpMessage = 'Specifies the name of CPG.')]
    [String]$CPGName,
    
    [Parameter(Mandatory=$false, ValueFromPipeline=$true , HelpMessage = 'Connection Paramater')]
    $WsapiConnection = $global:WsapiConnection
    )
    
  Begin 
  {
    # Test if connection exist
    Test-WSAPIConnection -WsapiConnection $WsapiConnection
  }

  Process 
  {    
    #Build uri
    Write-DebugLog "Running: Building uri to Remove-Cpg_WSAPI ." $Debug
    $uri = '/cpgs/'+$CPGName

    #init the response var
    $Result = $null

    #Request
    Write-DebugLog "Request: Request to Remove-Cpg_WSAPI : $CPGName (Invoke-WSAPI)." $Debug
    $Result = Invoke-WSAPI -uri $uri -type 'DELETE' -WsapiConnection $WsapiConnection
    
    $status = $Result.StatusCode
    if($status -eq 200)
    {
        write-host ""
        write-host "Cmdlet executed successfully" -foreground green
        write-host ""
        Write-DebugLog "SUCCESS: CPG:$CPGName successfully remove" $Info
        Write-DebugLog "End: Remove-Cpg_WSAPI" $Debug
        return ""        
    }
    else
    {
        write-host ""
        write-host "FAILURE : While Removing CPG:$CPGName " -foreground red
        write-host ""
        Write-DebugLog "FAILURE : While creating CPG:$CPGName " $Info
        Write-DebugLog "End: Remove-Cpg_WSAPI" $Debug
        
        return $Result.StatusDescription
    }    
    
  }
  End {}  
}
#END Remove-Cpg_WSAPI

############################################################################################################################################
## FUNCTION Get-Cpg_WSAPI
############################################################################################################################################
Function Get-Cpg_WSAPI 
{
  <#
   
  .SYNOPSIS
    Get list or single common provisioning groups (CPGs) all CPGs in the storage system.
  
  .DESCRIPTION
    Get list or single common provisioning groups (CPGs) all CPGs in the storage system.
        
  .EXAMPLE
    Get-Cpg_WSAPI
    List all/specified common provisioning groups (CPGs) in the system.
    
  .EXAMPLE
    Get-Cpg_WSAPI -CPGName "MyCPG"
    List Specified CPG name "MyCPG"
    
  .PARAMETER CPGName
    Specify name of the cpg to be listed
    
  .PARAMETER WsapiConnection
    WSAPI Connection object created with Connection command
              
  .Notes
    NAME : Get-Cpg_WSAPI
    LASTEDIT: February 2020
    KEYWORDS: Get-Cpg_WSAPI
   
  .Link
     http://www.hpe.com
 
  #Requires PS -Version 3.0
   
  #>

  [CmdletBinding()]
  Param(
      [Parameter(Mandatory = $false,ValueFromPipeline=$True,ValueFromPipelinebyPropertyName=$True,HelpMessage = 'CPG Name')]
      [String]
      $CPGName,
      
      [Parameter(Mandatory=$false, ValueFromPipeline=$true , HelpMessage = 'Connection Paramater')]
      $WsapiConnection = $global:WsapiConnection
    )

  Begin 
  {
    #Test if connection exist
    Test-WSAPIConnection -WsapiConnection $WsapiConnection
  }

  Process 
  {
    $Result = $null
    $dataPS = $null    
    
    #Build uri
    if($CPGName)
    {
        $uri = '/cpgs/'+$CPGName
        #Request
        $Result = Invoke-WSAPI -uri $uri -type 'GET' -WsapiConnection $WsapiConnection
        if($Result.StatusCode -eq 200)
        {
            $dataPS = $Result.content | ConvertFrom-Json
        }
    }
    else
    {
        #Request
        $Result = Invoke-WSAPI -uri '/cpgs' -type 'GET' -WsapiConnection $WsapiConnection
        if($Result.StatusCode -eq 200)
        {
            $dataPS = ($Result.content | ConvertFrom-Json).members
        }        
    }
          
    if($Result.StatusCode -eq 200)
    {
        write-host ""
        write-host "Executed successfully" -foreground green
        write-host ""
        Write-DebugLog "SUCCESS: CPG:$CPGName Successfully Executed" $Info

        # Add custom type to the resulting oject for formating purpose
        Write-DebugLog "Running: Add custom type to the resulting object for formatting purpose" $Debug
        
        #[array]$AlldataPS = Format-Result -dataPS $dataPS -TypeName '3PAR.Cpgs'
        #return $AlldataPS
        return $dataPS
    }
    else
    {
        write-host ""
        write-host "FAILURE : While Executing Get-Cpg_WSAPI CPG:$CPGName " -foreground red
        write-host ""
        Write-DebugLog "FAILURE : While Executing Get-Cpg_WSAPI CPG:$CPGName " $Info
        
        return $Result.StatusDescription
    }
  }    
}
#END Get-Cpg_WSAPI

Export-ModuleMember New-Cpg_WSAPI , Update-Cpg_WSAPI , Remove-Cpg_WSAPI , Get-Cpg_WSAPI
# SIG # Begin signature block
# MIIhEQYJKoZIhvcNAQcCoIIhAjCCIP4CAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCC/wCNGtvjY985i
# oL08iHnre6Aj3yFqZCpGNfpoKkTi5aCCEKswggUpMIIEEaADAgECAhB4Lu4fcD9z
# xUgD+jf1OoqlMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAkdCMRswGQYDVQQI
# ExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAOBgNVBAcTB1NhbGZvcmQxGDAWBgNVBAoT
# D1NlY3RpZ28gTGltaXRlZDEkMCIGA1UEAxMbU2VjdGlnbyBSU0EgQ29kZSBTaWdu
# aW5nIENBMB4XDTIxMDUyODAwMDAwMFoXDTIyMDUyODIzNTk1OVowgZAxCzAJBgNV
# BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlQYWxvIEFsdG8x
# KzApBgNVBAoMIkhld2xldHQgUGFja2FyZCBFbnRlcnByaXNlIENvbXBhbnkxKzAp
# BgNVBAMMIkhld2xldHQgUGFja2FyZCBFbnRlcnByaXNlIENvbXBhbnkwggEiMA0G
# CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDmclZSXJBXA55ijwwFymuq+Y4F/quF
# mm2vRdEmjFhzRvTpnGjIYtVcG11ka4JGCROmNVDZGAelnqcXn5DKO710j5SICTBC
# 5gXOLwga7usifs21W+lVT0BsZTiUnFu4hEhuFTlahJIEvPGVgO1GBcuItD2QqB4q
# 9j15GDI5nGBSzIyJKMctcIalxsTSPG1kiDbLkdfsIivhe9u9m8q6NRqDUaYYQTN+
# /qGCqVNannMapH8tNHqFb6VdzUFI04t7kFtSk00AkdD6qUvA4u8mL2bUXAYz8K5m
# nrFs+ckx5Yqdxfx68EO26Bt2qbz/oTHxE6FiVzsDl90bcUAah2l976ebAgMBAAGj
# ggGQMIIBjDAfBgNVHSMEGDAWgBQO4TqoUzox1Yq+wbutZxoDha00DjAdBgNVHQ4E
# FgQUlC56g+JaYFsl5QWK2WDVOsG+pCEwDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB
# /wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwMwEQYJYIZIAYb4QgEBBAQDAgQQMEoG
# A1UdIARDMEEwNQYMKwYBBAGyMQECAQMCMCUwIwYIKwYBBQUHAgEWF2h0dHBzOi8v
# c2VjdGlnby5jb20vQ1BTMAgGBmeBDAEEATBDBgNVHR8EPDA6MDigNqA0hjJodHRw
# Oi8vY3JsLnNlY3RpZ28uY29tL1NlY3RpZ29SU0FDb2RlU2lnbmluZ0NBLmNybDBz
# BggrBgEFBQcBAQRnMGUwPgYIKwYBBQUHMAKGMmh0dHA6Ly9jcnQuc2VjdGlnby5j
# b20vU2VjdGlnb1JTQUNvZGVTaWduaW5nQ0EuY3J0MCMGCCsGAQUFBzABhhdodHRw
# Oi8vb2NzcC5zZWN0aWdvLmNvbTANBgkqhkiG9w0BAQsFAAOCAQEAY+1n2UUlQU6Z
# VoEVaZKqZf/zrM/d7Kbx+S/t8mR2E+uNXStAnwztElqrm3fSr+5LMRzBhrYiSmea
# w9c/0c7qFO9mt8RR2q2uj0Huf+oAMh7TMuMKZU/XbT6tS1e15B8ZhtqOAhmCug6s
# DuNvoxbMpokYevpa24pYn18ELGXOUKlqNUY2qOs61GVvhG2+V8Hl/pajE7yQ4diz
# iP7QjMySms6BtZV5qmjIFEWKY+UTktUcvN4NVA2J0TV9uunDbHRt4xdY8TF/Clgz
# Z/MQHJ/X5yX6kupgDeN2t3o+TrColetBnwk/SkJEsUit0JapAiFUx44j4w61Qanb
# Zmi0tr8YGDCCBYEwggRpoAMCAQICEDlyRDr5IrdR19NsEN0xNZUwDQYJKoZIhvcN
# AQEMBQAwezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3Rl
# cjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQx
# ITAfBgNVBAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczAeFw0xOTAzMTIwMDAw
# MDBaFw0yODEyMzEyMzU5NTlaMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMKTmV3
# IEplcnNleTEUMBIGA1UEBxMLSmVyc2V5IENpdHkxHjAcBgNVBAoTFVRoZSBVU0VS
# VFJVU1QgTmV0d29yazEuMCwGA1UEAxMlVVNFUlRydXN0IFJTQSBDZXJ0aWZpY2F0
# aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAIAS
# ZRc2DsPbCLPQrFcNdu3NJ9NMrVCDYeKqIE0JLWQJ3M6Jn8w9qez2z8Hc8dOx1ns3
# KBErR9o5xrw6GbRfpr19naNjQrZ28qk7K5H44m/Q7BYgkAk+4uh0yRi0kdRiZNt/
# owbxiBhqkCI8vP4T8IcUe/bkH47U5FHGEWdGCFHLhhRUP7wz/n5snP8WnRi9UY41
# pqdmyHJn2yFmsdSbeAPAUDrozPDcvJ5M/q8FljUfV1q3/875PbcstvZU3cjnEjpN
# rkyKt1yatLcgPcp/IjSufjtoZgFE5wFORlObM2D3lL5TN5BzQ/Myw1Pv26r+dE5p
# x2uMYJPexMcM3+EyrsyTO1F4lWeL7j1W/gzQaQ8bD/MlJmszbfduR/pzQ+V+DqVm
# sSl8MoRjVYnEDcGTVDAZE6zTfTen6106bDVc20HXEtqpSQvf2ICKCZNijrVmzyWI
# zYS4sT+kOQ/ZAp7rEkyVfPNrBaleFoPMuGfi6BOdzFuC00yz7Vv/3uVzrCM7LQC/
# NVV0CUnYSVgaf5I25lGSDvMmfRxNF7zJ7EMm0L9BX0CpRET0medXh55QH1dUqD79
# dGMvsVBlCeZYQi5DGky08CVHWfoEHpPUJkZKUIGy3r54t/xnFeHJV4QeD2PW6WK6
# 1l9VLupcxigIBCU5uA4rqfJMlxwHPw1S9e3vL4IPAgMBAAGjgfIwge8wHwYDVR0j
# BBgwFoAUoBEKIz6W8Qfs4q8p74Klf9AwpLQwHQYDVR0OBBYEFFN5v1qqK0rPVIDh
# 2JvAnfKyA2bLMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MBEGA1Ud
# IAQKMAgwBgYEVR0gADBDBgNVHR8EPDA6MDigNqA0hjJodHRwOi8vY3JsLmNvbW9k
# b2NhLmNvbS9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDA0BggrBgEFBQcBAQQo
# MCYwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmNvbW9kb2NhLmNvbTANBgkqhkiG
# 9w0BAQwFAAOCAQEAGIdR3HQhPZyK4Ce3M9AuzOzw5steEd4ib5t1jp5y/uTW/qof
# nJYt7wNKfq70jW9yPEM7wD/ruN9cqqnGrvL82O6je0P2hjZ8FODN9Pc//t64tIrw
# kZb+/UNkfv3M0gGhfX34GRnJQisTv1iLuqSiZgR2iJFODIkUzqJNyTKzuugUGrxx
# 8VvwQQuYAAoiAxDlDLH5zZI3Ge078eQ6tvlFEyZ1r7uq7z97dzvSxAKRPRkA0xdc
# Ods/exgNRc2ThZYvXd9ZFk8/Ub3VRRg/7UqO6AZhdCMWtQ1QcydER38QXYkqa4Ux
# FMToqWpMgLxqeM+4f452cpkMnf7XkQgWoaNflTCCBfUwggPdoAMCAQICEB2iSDBv
# myYY0ILgln0z02owDQYJKoZIhvcNAQEMBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpOZXcgSmVyc2V5MRQwEgYDVQQHEwtKZXJzZXkgQ2l0eTEeMBwGA1UEChMV
# VGhlIFVTRVJUUlVTVCBOZXR3b3JrMS4wLAYDVQQDEyVVU0VSVHJ1c3QgUlNBIENl
# cnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTE4MTEwMjAwMDAwMFoXDTMwMTIzMTIz
# NTk1OVowfDELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3Rl
# cjEQMA4GA1UEBxMHU2FsZm9yZDEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMSQw
# IgYDVQQDExtTZWN0aWdvIFJTQSBDb2RlIFNpZ25pbmcgQ0EwggEiMA0GCSqGSIb3
# DQEBAQUAA4IBDwAwggEKAoIBAQCGIo0yhXoYn0nwli9jCB4t3HyfFM/jJrYlZilA
# hlRGdDFixRDtsocnppnLlTDAVvWkdcapDlBipVGREGrgS2Ku/fD4GKyn/+4uMyD6
# DBmJqGx7rQDDYaHcaWVtH24nlteXUYam9CflfGqLlR5bYNV+1xaSnAAvaPeX7Wpy
# vjg7Y96Pv25MQV0SIAhZ6DnNj9LWzwa0VwW2TqE+V2sfmLzEYtYbC43HZhtKn52B
# xHJAteJf7wtF/6POF6YtVbC3sLxUap28jVZTxvC6eVBJLPcDuf4vZTXyIuosB69G
# 2flGHNyMfHEo8/6nxhTdVZFuihEN3wYklX0Pp6F8OtqGNWHTAgMBAAGjggFkMIIB
# YDAfBgNVHSMEGDAWgBRTeb9aqitKz1SA4dibwJ3ysgNmyzAdBgNVHQ4EFgQUDuE6
# qFM6MdWKvsG7rWcaA4WtNA4wDgYDVR0PAQH/BAQDAgGGMBIGA1UdEwEB/wQIMAYB
# Af8CAQAwHQYDVR0lBBYwFAYIKwYBBQUHAwMGCCsGAQUFBwMIMBEGA1UdIAQKMAgw
# BgYEVR0gADBQBgNVHR8ESTBHMEWgQ6BBhj9odHRwOi8vY3JsLnVzZXJ0cnVzdC5j
# b20vVVNFUlRydXN0UlNBQ2VydGlmaWNhdGlvbkF1dGhvcml0eS5jcmwwdgYIKwYB
# BQUHAQEEajBoMD8GCCsGAQUFBzAChjNodHRwOi8vY3J0LnVzZXJ0cnVzdC5jb20v
# VVNFUlRydXN0UlNBQWRkVHJ1c3RDQS5jcnQwJQYIKwYBBQUHMAGGGWh0dHA6Ly9v
# Y3NwLnVzZXJ0cnVzdC5jb20wDQYJKoZIhvcNAQEMBQADggIBAE1jUO1HNEphpNve
# aiqMm/EAAB4dYns61zLC9rPgY7P7YQCImhttEAcET7646ol4IusPRuzzRl5ARokS
# 9At3WpwqQTr81vTr5/cVlTPDoYMot94v5JT3hTODLUpASL+awk9KsY8k9LOBN9O3
# ZLCmI2pZaFJCX/8E6+F0ZXkI9amT3mtxQJmWunjxucjiwwgWsatjWsgVgG10Xkp1
# fqW4w2y1z99KeYdcx0BNYzX2MNPPtQoOCwR/oEuuu6Ol0IQAkz5TXTSlADVpbL6f
# ICUQDRn7UJBhvjmPeo5N9p8OHv4HURJmgyYZSJXOSsnBf/M6BZv5b9+If8AjntIe
# Q3pFMcGcTanwWbJZGehqjSkEAnd8S0vNcL46slVaeD68u28DECV3FTSK+TbMQ5Lk
# uk/xYpMoJVcp+1EZx6ElQGqEV8aynbG8HArafGd+fS7pKEwYfsR7MUFxmksp7As9
# V1DSyt39ngVR5UR43QHesXWYDVQk/fBO4+L4g71yuss9Ou7wXheSaG3IYfmm8SoK
# C6W59J7umDIFhZ7r+YMp08Ysfb06dy6LN0KgaoLtO0qqlBCk4Q34F8W2WnkzGJLj
# tXX4oemOCiUe5B7xn1qHI/+fpFGe+zmAEc3btcSnqIBv5VPU4OOiwtJbGvoyJi1q
# V3AcPKRYLqPzW0sH3DJZ84enGm1YMYIPvDCCD7gCAQEwgZAwfDELMAkGA1UEBhMC
# R0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9y
# ZDEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMSQwIgYDVQQDExtTZWN0aWdvIFJT
# QSBDb2RlIFNpZ25pbmcgQ0ECEHgu7h9wP3PFSAP6N/U6iqUwDQYJYIZIAWUDBAIB
# BQCgfDAQBgorBgEEAYI3AgEMMQIwADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIB
# BDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQg
# Z5Y5B49YkiHlGrxTVggpVkibviN0LP731La35ml/17swDQYJKoZIhvcNAQEBBQAE
# ggEAjp9gyDJsbv7IWN6m1YrRrQIgqBAF9Yeudj0l0Cdy6Hj1axMrO6xyTbyCB4BN
# 8P3EHJ3uf2L7ERB37nDwuchDSMYQxu2/l0OKbNbKSsp8X3VRPSzOjZJILtna/cLP
# NISETtkrHapa4JtaEA8+5wIvktbV3B+wlIf53DqnzB4h5/1n59QWbqW9ZsrOAfdM
# gElL9NODAdWR8RGO6RD+9i/qoTO0mLBMTRDri758M+vF7BtAjiF6b6TZ5oes3jxX
# g/3o/icUS1HwE+r4t7zhwJQW7faSEYvLzFem1T6iZT90pkfyB2w0yMq2J/gfjWSF
# Nkx2RpBjBhmAwsU4GciLCqdYp6GCDX4wgg16BgorBgEEAYI3AwMBMYINajCCDWYG
# CSqGSIb3DQEHAqCCDVcwgg1TAgEDMQ8wDQYJYIZIAWUDBAIBBQAweAYLKoZIhvcN
# AQkQAQSgaQRnMGUCAQEGCWCGSAGG/WwHATAxMA0GCWCGSAFlAwQCAQUABCBlB439
# DwjxScy817p9KczRrCwz4xwwCCdfYboA1SRozQIRAIhxUnRdlBAtw9V255IZUZkY
# DzIwMjEwNjE5MDUxNDE5WqCCCjcwggT+MIID5qADAgECAhANQkrgvjqI/2BAIc4U
# APDdMA0GCSqGSIb3DQEBCwUAMHIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdp
# Q2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAvBgNVBAMTKERp
# Z2lDZXJ0IFNIQTIgQXNzdXJlZCBJRCBUaW1lc3RhbXBpbmcgQ0EwHhcNMjEwMTAx
# MDAwMDAwWhcNMzEwMTA2MDAwMDAwWjBIMQswCQYDVQQGEwJVUzEXMBUGA1UEChMO
# RGlnaUNlcnQsIEluYy4xIDAeBgNVBAMTF0RpZ2lDZXJ0IFRpbWVzdGFtcCAyMDIx
# MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwuZhhGfFivUNCKRFymNr
# Udc6EUK9CnV1TZS0DFC1JhD+HchvkWsMlucaXEjvROW/m2HNFZFiWrj/ZwucY/02
# aoH6KfjdK3CF3gIY83htvH35x20JPb5qdofpir34hF0edsnkxnZ2OlPR0dNaNo/G
# o+EvGzq3YdZz7E5tM4p8XUUtS7FQ5kE6N1aG3JMjjfdQJehk5t3Tjy9XtYcg6w6O
# LNUj2vRNeEbjA4MxKUpcDDGKSoyIxfcwWvkUrxVfbENJCf0mI1P2jWPoGqtbsR0w
# wptpgrTb/FZUvB+hh6u+elsKIC9LCcmVp42y+tZji06lchzun3oBc/gZ1v4NSYS9
# AQIDAQABo4IBuDCCAbQwDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwFgYD
# VR0lAQH/BAwwCgYIKwYBBQUHAwgwQQYDVR0gBDowODA2BglghkgBhv1sBwEwKTAn
# BggrBgEFBQcCARYbaHR0cDovL3d3dy5kaWdpY2VydC5jb20vQ1BTMB8GA1UdIwQY
# MBaAFPS24SAd/imu0uRhpbKiJbLIFzVuMB0GA1UdDgQWBBQ2RIaOpLqwZr68KC0d
# RDbd42p6vDBxBgNVHR8EajBoMDKgMKAuhixodHRwOi8vY3JsMy5kaWdpY2VydC5j
# b20vc2hhMi1hc3N1cmVkLXRzLmNybDAyoDCgLoYsaHR0cDovL2NybDQuZGlnaWNl
# cnQuY29tL3NoYTItYXNzdXJlZC10cy5jcmwwgYUGCCsGAQUFBwEBBHkwdzAkBggr
# BgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tME8GCCsGAQUFBzAChkNo
# dHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRTSEEyQXNzdXJlZElE
# VGltZXN0YW1waW5nQ0EuY3J0MA0GCSqGSIb3DQEBCwUAA4IBAQBIHNy16ZojvOca
# 5yAOjmdG/UJyUXQKI0ejq5LSJcRwWb4UoOUngaVNFBUZB3nw0QTDhtk7vf5EAmZN
# 7WmkD/a4cM9i6PVRSnh5Nnont/PnUp+Tp+1DnnvntN1BIon7h6JGA0789P63ZHdj
# XyNSaYOC+hpT7ZDMjaEXcw3082U5cEvznNZ6e9oMvD0y0BvL9WH8dQgAdryBDvjA
# 4VzPxBFy5xtkSdgimnUVQvUtMjiB2vRgorq0Uvtc4GEkJU+y38kpqHNDUdq9Y9Yf
# W5v3LhtPEx33Sg1xfpe39D+E68Hjo0mh+s6nv1bPull2YYlffqe0jmd4+TaY4cso
# 2luHpoovMIIFMTCCBBmgAwIBAgIQCqEl1tYyG35B5AXaNpfCFTANBgkqhkiG9w0B
# AQsFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYD
# VQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVk
# IElEIFJvb3QgQ0EwHhcNMTYwMTA3MTIwMDAwWhcNMzEwMTA3MTIwMDAwWjByMQsw
# CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu
# ZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFzc3VyZWQgSUQg
# VGltZXN0YW1waW5nIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
# vdAy7kvNj3/dqbqCmcU5VChXtiNKxA4HRTNREH3Q+X1NaH7ntqD0jbOI5Je/YyGQ
# mL8TvFfTw+F+CNZqFAA49y4eO+7MpvYyWf5fZT/gm+vjRkcGGlV+Cyd+wKL1oODe
# Ij8O/36V+/OjuiI+GKwR5PCZA207hXwJ0+5dyJoLVOOoCXFr4M8iEA91z3FyTgqt
# 30A6XLdR4aF5FMZNJCMwXbzsPGBqrC8HzP3w6kfZiFBe/WZuVmEnKYmEUeaC50ZQ
# /ZQqLKfkdT66mA+Ef58xFNat1fJky3seBdCEGXIX8RcG7z3N1k3vBkL9olMqT4Ud
# xB08r8/arBD13ays6Vb/kwIDAQABo4IBzjCCAcowHQYDVR0OBBYEFPS24SAd/imu
# 0uRhpbKiJbLIFzVuMB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6enIZ3zbcgPMBIG
# A1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsG
# AQUFBwMIMHkGCCsGAQUFBwEBBG0wazAkBggrBgEFBQcwAYYYaHR0cDovL29jc3Au
# ZGlnaWNlcnQuY29tMEMGCCsGAQUFBzAChjdodHRwOi8vY2FjZXJ0cy5kaWdpY2Vy
# dC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3J0MIGBBgNVHR8EejB4MDqg
# OKA2hjRodHRwOi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURS
# b290Q0EuY3JsMDqgOKA2hjRodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNl
# cnRBc3N1cmVkSURSb290Q0EuY3JsMFAGA1UdIARJMEcwOAYKYIZIAYb9bAACBDAq
# MCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5jb20vQ1BTMAsGCWCG
# SAGG/WwHATANBgkqhkiG9w0BAQsFAAOCAQEAcZUS6VGHVmnN793afKpjerN4zwY3
# QITvS4S/ys8DAv3Fp8MOIEIsr3fzKx8MIVoqtwU0HWqumfgnoma/Capg33akOpMP
# +LLR2HwZYuhegiUexLoceywh4tZbLBQ1QwRostt1AuByx5jWPGTlH0gQGF+JOGFN
# YkYkh2OMkVIsrymJ5Xgf1gsUpYDXEkdws3XVk4WTfraSZ/tTYYmo9WuWwPRYaQ18
# yAGxuSh1t5ljhSKMYcp5lH5Z/IwP42+1ASa2bKXuh1Eh5Fhgm7oMLSttosR+u8Ql
# K0cCCHxJrhO24XxCQijGGFbPQTS2Zl22dHv1VjMiLyI2skuiSpXY9aaOUjGCAoYw
# ggKCAgEBMIGGMHIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMx
# GTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAvBgNVBAMTKERpZ2lDZXJ0IFNI
# QTIgQXNzdXJlZCBJRCBUaW1lc3RhbXBpbmcgQ0ECEA1CSuC+Ooj/YEAhzhQA8N0w
# DQYJYIZIAWUDBAIBBQCggdEwGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMBwG
# CSqGSIb3DQEJBTEPFw0yMTA2MTkwNTE0MTlaMCsGCyqGSIb3DQEJEAIMMRwwGjAY
# MBYEFOHXgqjhkb7va8oWkbWqtJSmJJvzMC8GCSqGSIb3DQEJBDEiBCC7HjBdUNmQ
# nM9ZBgrs0erScivpOIckm/pFnkf+7+McmjA3BgsqhkiG9w0BCRACLzEoMCYwJDAi
# BCCzEJAGvArZgweRVyngRANBXIPjKSthTyaWTI01cez1qTANBgkqhkiG9w0BAQEF
# AASCAQAcPPGRdmmj/S4+Pw9DxFbMi13Zg3mNFtlSPzQTY/UI97lC4UmU091o6a+1
# dC8wurXSWefyD2SiaMVVfpyB/DEGPHWB6MATTiL987kCEgNwytoDpM4doxkSqspl
# hN4iAPMW430Sy9YVow4vNntSIuFDNNKiJhgnkc88Y7YQcA2VNFhx3h/AS5jzc5aV
# 9G9CJqiuV4IiVunz/2yzIPZ1xcloJVabn8rv5f/E780yCYsj4z794pHST9eS6hO4
# kV2qAynSuCH3tF62ggf0K6FKOE0RtO7eyDrZ3iqe658jrLJbVT5TiQBpO3qRqwQJ
# YJW/YhxPeaRVqHcoFupRUqaOzAi+
# SIG # End signature block