CLI/DiskEnclosureManagement.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: DiskEnclosureManagement.psm1
## Description: Disk Enclosure Management cmdlets
##
## Created: October 2019
## Last Modified: October 2019
## History: v3.0 - Created
#####################################################################################

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

############################################################################################################################################
## FUNCTION Test-CLIObject
############################################################################################################################################
Function Test-CLIObject 
{
Param(     
    [string]$ObjectType, 
    [string]$ObjectName ,
    [string]$ObjectMsg = $ObjectType, 
    $SANConnection = $global:SANConnection
    )

    $IsObjectExisted = $True
    $ObjCmd = $ObjectType -replace ' ', '' 
    $Cmds = "show$ObjCmd $ObjectName"
    
    $Result = Invoke-CLICommand -Connection $SANConnection -cmds  $Cmds
    if ($Result -like "no $ObjectMsg listed")
    {
        $IsObjectExisted = $false
    }
    return $IsObjectExisted
    
} # End FUNCTION Test-CLIObject

####################################################################################################################
## FUNCTION Set-AdmitsPD
####################################################################################################################
Function Set-AdmitsPD
{
<#
  .SYNOPSIS
    The Set-AdmitsPD command creates and admits physical disk definitions to enable the use of those disks.
    
  .DESCRIPTION
    The Set-AdmitsPD command creates and admits physical disk definitions to enable the use of those disks.
    
  .EXAMPLE
   Set-AdmitsPD
   This example admits physical disks.
   
  .EXAMPLE
   Set-AdmitsPD -Nold
   Do not use the PD (as identified by the <world_wide_name> specifier) For logical disk allocation.
   
  .EXAMPLE
   Set-AdmitsPD -NoPatch
   Suppresses the check for drive table update packages for new hardware enablement.

  .EXAMPLE
    Set-AdmitsPD -Nold -wwn xyz
    Do not use the PD (as identified by the <world_wide_name> specifier) For logical disk allocation.
        
  .PARAMETER Nold
    Do not use the PD (as identified by the <world_wide_name> specifier)
    for logical disk allocation.

  .PARAMETER Nopatch
    Suppresses the check for drive table update packages for new
    hardware enablement.

  .PARAMETER wwn
    Indicates the World-Wide Name (WWN) of the physical disk to be admitted. If WWNs are
    specified, only the specified physical disk(s) are admitted.
     
  .PARAMETER SANConnection
    Specify the SAN Connection object created with New-CLIConnection or New-PoshSshConnection
    
  .Notes
    NAME: Set-AdmitsPD
    LASTEDIT: 25/10/2019
    KEYWORDS: Set-AdmitsPD
   
  .Link
     http://www.hpe.com
 
 #Requires PS -Version 3.0

 #>

[CmdletBinding()]
    param(
        [Parameter(Position=0, Mandatory=$false)]
        [switch]
        $Nold,
        
        [Parameter(Position=1, Mandatory=$false)]
        [switch]
        $NoPatch,
                
        [Parameter(Position=2, Mandatory=$false, ValueFromPipeline=$true)]
        [System.String]
        $wwn,        
        
        [Parameter(Position=3, Mandatory=$false, ValueFromPipeline=$true)]
        $SANConnection = $global:SANConnection 
       
    )    
    
    Write-DebugLog "Start: In Set-AdmitsPD - validating input values" $Debug 
    #check if connection object contents are null/empty
    if(!$SANConnection)
    {        
        #check if connection object contents are null/empty
        $Validate1 = Test-CLIConnection $SANConnection
        if($Validate1 -eq "Failed")
        {
            #check if global connection object contents are null/empty
            $Validate2 = Test-CLIConnection $global:SANConnection
            if($Validate2 -eq "Failed")
            {
                Write-DebugLog "Connection object is null/empty or the array address (FQDN/IP Address) or user credentials in the connection object are either null or incorrect. Create a valid connection object using New-CLIConnection or New-PoshSshConnection" "ERR:"
                Write-DebugLog "Stop: Exiting Set-AdmitsPD since SAN connection object values are null/empty" $Debug
                return "Unable to execute the cmdlet Set-AdmitsPD since no active storage connection session exists. `nUse New-PoshSSHConnection or New-CLIConnection to start a new storage connection session."
            }
        }
    }
    $plinkresult = Test-PARCli
    if($plinkresult -match "FAILURE :")
    {
        write-debuglog "$plinkresult" "ERR:" 
        return $plinkresult
    }        
    $cmd= "admitpd -f "
    
    
    if ($Nold)
    {    
        $cmd+=" -nold "        
    }    
    if ($NoPatch)
    {    
        $cmd+=" -nopatch "        
    }
    if($wwn)
    {
        $cmd += " $wwn"        
    }
    $Result = Invoke-CLICommand -Connection $SANConnection -cmds  $cmd
    write-debuglog " The Set-AdmitsPD command creates and admits physical disk definitions to enable the use of those disks " "INFO:" 
    return     $Result    
} # End Set-AdmitsPD

####################################################################################################################
## FUNCTION Find-Cage
####################################################################################################################
Function Find-Cage
{
<#
  .SYNOPSIS
   The Find-Cage command allows system administrators to locate a drive cage, drive magazine, or port in the system using the devices’ blinking LEDs.
 
 .DESCRIPTION
   The Find-Cage command allows system administrators to locate a drive cage, drive magazine, or port in the system using the devices’ blinking LEDs.
    
  .EXAMPLE
    Find-Cage -Time 30 -CageName cage0
    This example causes the Fibre Channel LEDs on the drive CageName cage0 to blink for 30 seconds.
   
  .EXAMPLE
    Find-Cage -Time 30 -CageName cage0 -mag 3
    This example causes the Fibre Channel LEDs on the drive CageName cage0 to blink for 30 seconds,Indicates the drive magazine by number 3.
   
  .EXAMPLE
    Find-Cage -Time 30 -CageName cage0 -PortName demo1
    This example causes the Fibre Channel LEDs on the drive CageName cage0 to blink for 30 seconds, If a port is specified, the port LED will oscillate between green and off.
    
  .EXAMPLE
    Find-Cage -CageName cage1 -Mag 2
    This example causes the Fibre Channel LEDs on the drive CageName cage1 to blink, Indicates the drive magazine by number 2.
        
  .PARAMETER Time
    Specifies the number of seconds, from 0 through 255 seconds, to blink the LED.
    If the argument is not specified, the option defaults to 60 seconds.
  
  .PARAMETER CageName
    Specifies the drive cage name as shown in the Name column of Get-Cage command output.
    
  .PARAMETER ModuleName
    Indicates the module name to locate. Accepted values are
    pcm|iom|drive. The iom specifier is not supported for node enclosures.

  .PARAMETER ModuleNumber
    Indicates the module number to locate. The cage and module number can be found
    by issuing showcage -d <cage_name>.
    
  .PARAMETER Mag
    Indicates the drive magazine by number.
    • For DC1 drive cages, accepted values are 0 through 4.
    • For DC2 and DC4 drive cages, accepted values are 0 through 9.
    • For DC3 drive cages, accepted values are 0 through 15.
        
  .PARAMETER PortName
    Indicates the port specifiers. Accepted values are A0|B0|A1|B1|A2|B2|A3|B3.
    If a port is specified, the port LED will oscillate between green and off.
    
  .PARAMETER SANConnection
    Specify the SAN Connection object created with New-CLIConnection or New-PoshSshConnection
    
  .Notes
    NAME: Find-Cage
    LASTEDIT: 25/10/2019
    KEYWORDS: Find-Cage
   
  .Link
     http://www.hpe.com
 
 #Requires PS -Version 3.0

 #>

[CmdletBinding()]
    param(
        [Parameter(Position=0, Mandatory=$false,ValueFromPipeline=$true)]
        [System.String]
        $Time,
        
        [Parameter(Position=1, Mandatory=$true,ValueFromPipeline=$true)]
        [System.String]
        $CageName,
        
        [Parameter(Position=2, Mandatory=$false,ValueFromPipeline=$true)]
        [System.String]
        $ModuleName,
        
        [Parameter(Position=3, Mandatory=$false,ValueFromPipeline=$true)]
        [System.String]
        $ModuleNumber,
        
        [Parameter(Position=4, Mandatory=$false,ValueFromPipeline=$true)]
        [System.String]
        $Mag,
        
        [Parameter(Position=5, Mandatory=$false,ValueFromPipeline=$true)]
        [System.String]
        $PortName,
                
        [Parameter(Position=6, Mandatory=$false, ValueFromPipeline=$true)]
        $SANConnection = $global:SANConnection        
    )        
    
    Write-DebugLog "Start: In Find-Cage - validating input values" $Debug 
    #check if connection object contents are null/empty
    if(!$SANConnection)
    {            
        #check if connection object contents are null/empty
        $Validate1 = Test-CLIConnection $SANConnection
        if($Validate1 -eq "Failed")
        {
            #check if global connection object contents are null/empty
            $Validate2 = Test-CLIConnection $global:SANConnection
            if($Validate2 -eq "Failed")
            {
                Write-DebugLog "Connection object is null/empty or the array address (FQDN/IP Address) or user credentials in the connection object are either null or incorrect. Create a valid connection object using New-CLIConnection or New-PoshSshConnection" "ERR:"
                Write-DebugLog "Stop: Exiting Find-Cage since SAN connection object values are null/empty" $Debug
                return "Unable to execute the cmdlet Find-Cage since no active storage connection session exists. `nUse New-PoshSSHConnection or New-CLIConnection to start a new storage connection session."
            }
        }
    }
    $plinkresult = Test-PARCli
    if($plinkresult -match "FAILURE :")
    {
        write-debuglog "$plinkresult" "ERR:" 
        return $plinkresult
    }
    
    $cmd= "locatecage "    
    
    if ($time)
    {
        $s = 0..255
        $demo = $time
        if($s -match $demo)
        {
            $str="time"
            $cmd+=" -t $time"
        }
        else
        {
            return " Error : -time $time is Not valid use seconds, from 0 through 255 Only "
        }
    }
    if ($CageName)
    {
        $cmd2="showcage "
        $Result2 = Invoke-CLICommand -Connection $SANConnection -cmds  $cmd2
        if($Result2 -match $CageName)
        {
            $cmd+=" $CageName"
        }
        else
        {
        Write-DebugLog "Stop: Exiting Find-Cage $CageName Not available "
        return "FAILURE : -CageName $CageName is Unavailable `n Try using [Get-Cage] Command "
        }
    }
    else
    {
        Write-DebugLog "Stop: CageName is mandatory" $Debug
        return "Error : -CageName is mandatory. "
    }
    if ($ModuleName)
    {        
        $cmd+=" $ModuleName"        
    }    
    if ($ModuleNumber)
    {        
        $cmd+=" $ModuleNumber"        
    }
    if ($Mag)
    {
        $a = 0..15
        $demo = $Mag
        if($a -match $demo)
        {
        $str="mag"
        $cmd +=" $Mag"
        }
        else
        {
            return "Error : -Mag $Mag is Not valid use seconds,from 0 through 15 Only"        
        }
    }    
    if ($PortName)
    {
        $s=$str
        if ($s -match "mag" )
        {
            return "FAILURE : -Mag $Mag cannot be used along with -PortName $PortName "
        }
        else
        {    
            $a = $PortName
            $b = "A0","B0","A1","B1","A2","B2","A3","B3"
            if($b -eq $a)
            {
                $cmd +=" $PortName"
            }
            else
            {
                return "Error : -PortName $PortName is invalid use [ A0| B0 | A1 | B1 | A2 | B2 | A3 | B3 ] only "
            }
        }    
    }    
    $Result = Invoke-CLICommand -Connection $SANConnection -cmds  $cmd    
    write-debuglog " Executing Find-Cage Command , surface scans or diagnostics on physical disks with the command " "INFO:"     
    if([string]::IsNullOrEmpty($Result))
    {
        return  "Success : Find-Cage Command Executed Successfully $Result"
    }
    else
    {
        return  "FAILURE : While Executing Find-Cage `n $Result"
    }         
}
# End Find-Cage

###################################################################################################################
############################################ FUNCTION Get-Cage ################################################
###################################################################################################################

Function Get-Cage
{
<#
  .SYNOPSIS
   The Get-Cage command displays information about drive cages.
   
  .DESCRIPTION
   The Get-Cage command displays information about drive cages.
    
  .EXAMPLE
    Get-Cage
    This examples display information for a single system’s drive cages.
   
  .EXAMPLE
    Get-Cage -D -CageName cage2
    Specifies that more detailed information about the drive cage is displayed
    
  .EXAMPLE
    Get-Cage -I -CageName cage2
    Specifies that inventory information about the drive cage is displayed.
            
  .PARAMETER D
    Specifies that more detailed information about the drive cage is displayed. If this option is not
    used, then only summary information about the drive cages is displayed.
        
  .PARAMETER E
    Displays error information.
    
  .PARAMETER C
    Specifies to use cached information. This option displays information faster because the cage does
    not need to be probed, however, some information might not be up-to-date without that probe.

  .PARAMETER SFP
    Specifies information about the SFP(s) attached to a cage. Currently, additional SFP information
    can only be displayed for DC2 and DC4 cages.
            
  .PARAMETER I
    Specifies that inventory information about the drive cage is displayed. If this option is not used,
    then only summary information about the drive cages is displayed.

  .PARAMETER DDm
    Specifies the SFP DDM information. This option can only be used with the
    -sfp option and cannot be used with the -d option.

    
  .PARAMETER SVC
  Displays inventory information with HPE serial number, spare part number, and so on. it is supported only on HPE 3PAR Storage 7000 Storagesystems and HPE 3PAR 8000 series systems"
  
  .PARAMETER CageName
    Specifies a drive cage name for which information is displayed. This specifier can be repeated to display information for multiple cages
      
  .PARAMETER SANConnection
    Specify the SAN Connection object created with New-CLIConnection or New-PoshSshConnection
    
  .Notes
    NAME: Get-Cage
    LASTEDIT: 25/10/2019
    KEYWORDS: Get-Cage
   
  .Link
     http://www.hpe.com
 
 #Requires PS -Version 3.0

 #>

[CmdletBinding()]
    param(
        [Parameter(Position=0, Mandatory=$false)]
        [Switch]
        $D,
        
        [Parameter(Position=1, Mandatory=$false)]
        [Switch]
        $E,
        
        [Parameter(Position=2, Mandatory=$false)]
        [Switch]
        $C,
        
        [Parameter(Position=3, Mandatory=$false)]
        [Switch]
        $SFP,
        
        [Parameter(Position=4, Mandatory=$false)]
        [Switch]
        $DDM,
        
        [Parameter(Position=5, Mandatory=$false)]
        [Switch]
        $I,
        
        [Parameter(Position=6, Mandatory=$false)]
        [Switch]
        $SVC,
        
        [Parameter(Position=7, Mandatory=$false,ValueFromPipeline=$true)]
        [System.String]
        $CageName,
            
        [Parameter(Position=8, Mandatory=$false, ValueFromPipeline=$true)]
        $SANConnection = $global:SANConnection 
       
    )        
    Write-DebugLog "Start: In Get-Cage - validating input values" $Debug 
    #check if connection object contents are null/empty
    if(!$SANConnection)
    {            
        #check if connection object contents are null/empty
        $Validate1 = Test-CLIConnection $SANConnection
        if($Validate1 -eq "Failed")
        {
            #check if global connection object contents are null/empty
            $Validate2 = Test-CLIConnection $global:SANConnection
            if($Validate2 -eq "Failed")
            {
                Write-DebugLog "Connection object is null/empty or the array address (FQDN/IP Address) or user credentials in the connection object are either null or incorrect. Create a valid connection object using New-CLIConnection or New-PoshSshConnection" "ERR:"
                Write-DebugLog "Stop: Exiting Get-Cage since SAN connection object values are null/empty" $Debug
                return "Unable to execute the cmdlet Get-Cage since no active storage connection session exists. `nUse New-PoshSSHConnection or New-CLIConnection to start a new storage connection session."
            }
        }
    }
    $plinkresult = Test-PARCli
    if($plinkresult -match "FAILURE :")
    {
        write-debuglog "$plinkresult" "ERR:" 
        return $plinkresult
    }    
    $cmd= "showcage "
    $testCmd= "showcage "
    
    if($D)
    { 
        $cmd +=" -d " 
    }
    if($E) 
    { 
        $cmd +=" -e "
    }
    if($C) 
    { 
        $cmd +=" -c "
    }
    if($SFP) 
    { 
        $cmd +=" -sfp " 
    }
    if($DDM) 
    { 
        $cmd +=" -ddm " 
    }
    if($I) 
    { 
        $cmd +=" -i " 
    }
    if($SVC) 
    { 
        $cmd +=" -svc -i" 
    }
    if ($CageName) 
    { 
        $cmd+=" $CageName "
        $testCmd+=" $CageName "
    }
    
    $Result = Invoke-CLICommand -Connection $SANConnection -cmds  $cmd
    write-debuglog " Executing Get-Cage command that displays information about drive cages. with the command " "INFO:" 
    
    if($cmd -eq "showcage " -or ($cmd -eq $testCmd))
    {
        if($Result.Count -gt 1)
        {    
            $tempFile = [IO.Path]::GetTempFileName()
            $LastItem = $Result.Count 
            #Write-Host " Result Count =" $Result.Count
            foreach ($s in  $Result[0..$LastItem] )
            {        
                $s= [regex]::Replace($s,"^ ","")            
                $s= [regex]::Replace($s," +",",")    
                #$s= [regex]::Replace($s,"-","")
                $s= $s.Trim()     
                Add-Content -Path $tempFile -Value $s
                #Write-Host " First if statement $s"
            }
            Import-Csv $tempFile 
            del $tempFile
            Return  " Success : Executing Get-Cage"
        }
        else
        {
            Return  " FAILURE : While Executing Get-Cage `n $Result"        
        }        
    }
    
    if($Result -match "Cage" )
    {
        $result    
        Return  " Success : Executing Get-Cage"
    } 
    else
    {
        Return  " FAILURE : While Executing Get-Cage `n $Result"
    } 
 } # End Get-Cage
 
 ####################################################################################################################
## FUNCTION Show-PD
####################################################################################################################
Function Show-PD
{
<#
  .SYNOPSIS
    The Show-PD command displays configuration information about the physical disks (PDs) on a system.

  .DESCRIPTION
    The Show-PD command displays configuration information about the physical disks (PDs) on a system.
   
  .EXAMPLE
    Show-PD
    This example displays configuration information about all the physical disks (PDs) on a system.
    
  .EXAMPLE
    Show-PD -PD_ID 5
    This example displays configuration information about specific or given physical disks (PDs) on a system.
    
  .EXAMPLE
    Show-PD -C
    This example displays chunklet use information for all disks.
    
  .EXAMPLE
    Show-PD -C -PD_ID 5
    This example will display chunklet use information for all disks with the physical disk ID.

  .EXAMPLE
    Show-PD -Node 0 -PD_ID 5
    
  .EXAMPLE
    Show-PD -I -Pattern -ND 1 -PD_ID 5

  .EXAMPLE
    Show-PD -C -Pattern -Devtype FC

  .EXAMPLE
    Show-PD -option p -pattern mg -patternValue 0
    TThis example will display all the FC disks in magazine 0 of all cages.
 
  .PARAMETER Listcols
    List the columns available to be shown in the -showcols option
    described below (see 'clihelp -col showpd' for help on each column).

  .PARAMETER I
    Show disk inventory (inquiry) data.

    The following columns are shown:
    Id CagePos State Node_WWN MFR Model Serial FW_Rev Protocol MediaType AdmissionTime.

  .PARAMETER E
    Show disk environment and error information. Note that reading this
    information places a significant load on each disk.

    The following columns are shown:
    Id CagePos Type State Rd_CErr Rd_UErr Wr_CErr Wr_UErr Temp_DegC
    LifeLeft_PCT.

  .PARAMETER C
    Show chunklet usage information. Any chunklet in a failed disk will be
    shown as "Fail".

    The following columns are shown:
    Id CagePos Type State Total_Chunk Nrm_Used_OK Nrm_Used_Fail
    Nrm_Unused_Free Nrm_Unused_Uninit Nrm_Unused_Unavail Nrm_Unused_Fail
    Spr_Used_OK Spr_Used_Fail Spr_Unused_Free Spr_Unused_Uninit Spr_Unused_Fail.

  .PARAMETER S
    Show detailed state information.
    This option is deprecated and will be removed in a subsequent release.

  .PARAMETER State
    Show detailed state information. This is the same as -s.

    The following columns are shown:
    Id CagePos Type State Detailed_State SedState.

  .PARAMETER Path
    Show current and saved path information for disks.

    The following columns are shown:
    Id CagePos Type State Path_A0 Path_A1 Path_B0 Path_B1 Order.

  .PARAMETER Space
    Show disk capacity usage information (in MB).

    The following columns are shown:
    Id CagePos Type State Size_MB Volume_MB Spare_MB Free_MB Unavail_MB
    Failed_MB.

  .PARAMETER Failed
    Specifies that only failed physical disks are displayed.

  .PARAMETER Degraded
    Specifies that only degraded physical disks are displayed. If both
    -failed and -degraded are specified, the command shows failed disks and
    degraded disks.

  .PARAMETER Pattern
    Physical disks matching the specified pattern are displayed.

  .PARAMETER ND
    Specifies one or more nodes. Nodes are identified by one or more
    integers (item). Multiple nodes are separated with a single comma
    (e.g. 1,2,3). A range of nodes is separated with a hyphen (e.g. 0-
    7). The primary path of the disks must be on the specified node(s).
            
  .PARAMETER ST
    Specifies one or more PCI slots. Slots are identified by one or more
    integers (item). Multiple slots are separated with a single comma
    (e.g. 1,2,3). A range of slots is separated with a hyphen (e.g. 0-
    7). The primary path of the disks must be on the specified PCI
    slot(s).
            
  .PARAMETER PT
    Specifies one or more ports. Ports are identified by one or more
    integers (item). Multiple ports are separated with a single comma
    (e.g. 1,2,3). A range of ports is separated with a hyphen (e.g. 0-
    4). The primary path of the disks must be on the specified port(s).
            
  .PARAMETER CG
    Specifies one or more drive cages. Drive cages are identified by one
    or more integers (item). Multiple drive cages are separated with a
    single comma (e.g. 1,2,3). A range of drive cages is separated with
    a hyphen (e.g. 0-3). The specified drive cage(s) must contain disks.
    
  .PARAMETER MG
    Specifies one or more drive magazines. The "1." or "0." displayed
    in the CagePos column of showpd output indicating the side of the
    cage is omitted when using the -mg option. Drive magazines are
    identified by one or more integers (item). Multiple drive magazines
    are separated with a single comma (e.g. 1,2,3). A range of drive
    magazines is separated with a hyphen(e.g. 0-7). The specified drive
    magazine(s) must contain disks.
            
  .PARAMETER PN
    Specifies one or more disk positions within a drive magazine. Disk
    positions are identified by one or more integers (item). Multiple
    disk positions are separated with a single comma(e.g. 1,2,3). A
    range of disk positions is separated with a hyphen(e.g. 0-3). The
    specified position(s) must contain disks.
            
  .PARAMETER DK
    Specifies one or more physical disks. Disks are identified by one or
    more integers(item). Multiple disks are separated with a single
    comma (e.g. 1,2,3). A range of disks is separated with a hyphen(e.g.
    0-3). Disks must match the specified ID(s).
            
  .PARAMETER Devtype
    Specifies that physical disks must have the specified device type
    (FC for Fast Class, NL for Nearline, SSD for Solid State Drive)
    to be used. Device types can be displayed by issuing the "showpd"
    command.
            
  .PARAMETER RPM
    Drives must be of the specified relative performance metric, as
    shown in the "RPM" column of the "showpd" command.
    The number does not represent a rotational speed for the drives
    without spinning media (SSD). It is meant as a rough estimation of
    the performance difference between the drive and the other drives
    in the system. For FC and NL drives, the number corresponds to
    both a performance measure and actual rotational speed. For SSD
    drives, the number is to be treated as a relative performance
    benchmark that takes into account I/O's per second, bandwidth and
    access time.

  .PARAMETER Node
    Specifies that the display is limited to specified nodes and physical
    disks connected to those nodes. The node list is specified as a series
    of integers separated by commas (e.g. 1,2,3). The list can also consist
    of a single integer. If the node list is not specified, all disks on all
    nodes are displayed.

  .PARAMETER Slots
    Specifies that the display is limited to specified PCI slots and
    physical disks connected to those PCI slots. The slot list is specified
    as a series of integers separated by commas (e.g. 1,2,3). The list can
    also consist of a single integer. If the slot list is not specified, all
    disks on all slots are displayed.

  .PARAMETER Ports
    Specifies that the display is limited to specified ports and
    physical disks connected to those ports. The port list is specified
    as a series of integers separated by commas (e.g. 1,2,3). The list can
    also consist of a single integer. If the port list is not specified, all
    disks on all ports are displayed.

  .PARAMETER WWN
    Specifies the WWN of the physical disk. This option and argument can be
    specified if the <PD_ID> specifier is not used. This option should be
    the last option in the command line.

 
  .PARAMETER SANConnection
    Specify the SAN Connection object created with New-CLIConnection or New-PoshSshConnection
    
  .Notes
    NAME: Show-PD
    LASTEDIT: 30/10/2019
    KEYWORDS: Show-PD
   
  .Link
     http://www.hpe.com
 
 #Requires PS -Version 3.0

 #>

[CmdletBinding()]
    param(
        [Parameter(Position=0, Mandatory=$false)]
        [switch]
        $I,
        
        [Parameter(Position=1, Mandatory=$false)]
        [switch]
        $E,
        
        [Parameter(Position=2, Mandatory=$false)]
        [switch]
        $C,
        
        [Parameter(Position=3, Mandatory=$false)]
        [switch]
        $StateInfo,
        
        [Parameter(Position=4, Mandatory=$false)]
        [switch]
        $State,
        
        [Parameter(Position=5, Mandatory=$false)]
        [switch]
        $Path,
        
        [Parameter(Position=6, Mandatory=$false)]
        [switch]
        $Space,
        
        [Parameter(Position=7, Mandatory=$false)]
        [switch]
        $Failed,
        
        [Parameter(Position=8, Mandatory=$false)]
        [switch]
        $Degraded,
        
        [Parameter(Position=9, Mandatory=$false,ValueFromPipeline=$true)]
        [System.String]
        $Node ,
        
        [Parameter(Position=10, Mandatory=$false,ValueFromPipeline=$true)]
        [System.String]
        $Slots ,
        
        [Parameter(Position=11, Mandatory=$false,ValueFromPipeline=$true)]
        [System.String]
        $Ports ,
        
        [Parameter(Position=12, Mandatory=$false,ValueFromPipeline=$true)]
        [System.String]
        $WWN ,
        
        [Parameter(Position=13, Mandatory=$false)]
        [switch]
        $Pattern,
        
        [Parameter(Position=14, Mandatory=$false,ValueFromPipeline=$true)]
        [System.String]
        $ND ,
        
        [Parameter(Position=15, Mandatory=$false,ValueFromPipeline=$true)]
        [System.String]
        $ST ,
        
        [Parameter(Position=16, Mandatory=$false,ValueFromPipeline=$true)]
        [System.String]
        $PT ,
        
        [Parameter(Position=17, Mandatory=$false,ValueFromPipeline=$true)]
        [System.String]
        $CG ,
        
        [Parameter(Position=18, Mandatory=$false,ValueFromPipeline=$true)]
        [System.String]
        $MG ,
        
        [Parameter(Position=19, Mandatory=$false,ValueFromPipeline=$true)]
        [System.String]
        $PN ,
        
        [Parameter(Position=20, Mandatory=$false,ValueFromPipeline=$true)]
        [System.String]
        $DK ,
        
        [Parameter(Position=21, Mandatory=$false,ValueFromPipeline=$true)]
        [System.String]
        $Devtype ,
        
        [Parameter(Position=22, Mandatory=$false,ValueFromPipeline=$true)]
        [System.String]
        $RPM ,
        
        [Parameter(Position=23, Mandatory=$false,ValueFromPipeline=$true)]
        [System.String]
        $PD_ID ,

        [Parameter(Position=23, Mandatory=$false,ValueFromPipeline=$true)]
        [switch]
        $Listcols ,
            
        [Parameter(Position=24, Mandatory=$false, ValueFromPipeline=$true)]
        $SANConnection = $global:SANConnection        
    )        
    Write-DebugLog "Start: In Show-PD - validating input values" $Debug 
    #check if connection object contents are null/empty
    if(!$SANConnection)
    {        
        #check if connection object contents are null/empty
        $Validate1 = Test-CLIConnection $SANConnection
        if($Validate1 -eq "Failed")
        {
            #check if global connection object contents are null/empty
            $Validate2 = Test-CLIConnection $global:SANConnection
            if($Validate2 -eq "Failed")
            {
                Write-DebugLog "Connection object is null/empty or the array address (FQDN/IP Address) or user credentials in the connection object are either null or incorrect. Create a valid connection object using New-CLIConnection or New-PoshSshConnection" "ERR:"
                Write-DebugLog "Stop: Exiting Show-PD since SAN connection object values are null/empty" $Debug
                return "Unable to execute the cmdlet Show-PD since no active storage connection session exists. `nUse New-PoshSSHConnection or New-CLIConnection to start a new storage connection session."
            }
        }
    }
    $plinkresult = Test-PARCli
    if($plinkresult -match "FAILURE :")
    {
        write-debuglog "$plinkresult" "ERR:" 
        return $plinkresult
    }
    
    $cmd= "showpd "    
    
    if($Listcols)
    {
        $cmd+=" -listcols "
        $Result = Invoke-CLICommand -Connection $SANConnection -cmds  $cmd
        return $Result
    }
    if($I)
    {
        $cmd+=" -i "        
    }
    if($E)
    {
        $cmd+=" -e "        
    }
    if($C)
    {
        $cmd+=" -c "        
    }
    if($StateInfo)
    {
        $cmd+=" -s "        
    }
    if($State)
    {
        $cmd+=" -state "        
    }
    if($Path)
    {
        $cmd+=" -path "        
    }
    if($Space)
    {
        $cmd+=" -space "        
    }
    if($Failed)
    {
        $cmd+=" -failed "        
    }
    if($Degraded)
    {
        $cmd+=" -degraded "        
    }
    if($Node)
    {
        $cmd+=" -nodes $Node "        
    }
    if($Slots)
    {
        $cmd+=" -slots $Slots "        
    }
    if($Ports)
    {
        $cmd+=" -ports $Ports "        
    }
    if($WWN)
    {
        $cmd+=" -w $WWN "        
    }
    if($Pattern)
    {
        if($ND)
        {
            $cmd+=" -p -nd $ND "
        }
        if($ST)
        {
            $cmd+=" -p -st $ST "
        }
        if($PT)
        {
            $cmd+=" -p -pt $PT "
        }
        if($CG)
        {
            $cmd+=" -p -cg $CG "
        }
        if($MG)
        {
            $cmd+=" -p -mg $MG "
        }
        if($PN)
        {
            $cmd+=" -p -pn $PN "
        }
        if($DK)
        {
            $cmd+=" -p -dk $DK "
        }
        if($Devtype)
        {
            $cmd+=" -p -devtype $Devtype "
        }
        if($RPM)
        {
            $cmd+=" -p -rpm $RPM "
        }
    }        
    if ($PD_ID)
    {        
        $PD=$PD_ID        
        $pdd="showpd $PD"
        $Result1 = Invoke-CLICommand -Connection $SANConnection -cmds  $pdd    
        if($Result1 -match "No PDs listed" )
        {
            Write-DebugLog "Stop: Exiting Show-PD since -PD_ID $PD_ID is not available "
            return " FAILURE : $PD_ID is not available id pLease try using only [Show-PD] to get the list of PD_ID Available. "            
        }
        else     
        {
            $cmd+=" $PD_ID "
        }
    }
    
    $Result = Invoke-CLICommand -Connection $SANConnection -cmds  $cmd
    if($Result -match "Invalid device type")
    {
        write-host""
        return $Result
    }
    if($Result.Count -lt 2)
    {    
        write-host""
        return $Result
    }
    #write-debuglog " Executing Get-Cage command that displays information about drive cages. with the command " "INFO:"
    
    #this is for option i
    if($I -Or $State -Or $StateInfo)
    {
        $flag = "True"
        $tempFile = [IO.Path]::GetTempFileName()
        $LastItem = $Result.Count -3  
        #Write-Host " Result Count =" $Result.Count
        foreach ($s in  $Result[0..$LastItem] )
        {        
            $s= [regex]::Replace($s,"^ ","")            
            $s= [regex]::Replace($s," +",",")    
            $s= [regex]::Replace($s,"-","")
            $s= $s.Trim()
            if($I)
            {
                if($flag -eq "True")
                {
                    $sTemp1=$s
                    $sTemp = $sTemp1.Split(',')
                    $sTemp[10]="AdmissionDate,AdmissionTime,AdmissionZone"                 
                    $newTemp= [regex]::Replace($sTemp," ",",")    
                    $newTemp= $newTemp.Trim()
                    $s=$newTemp
                }    
            }            
            Add-Content -Path $tempFile -Value $s
            $flag="false"        
        }                
        Import-Csv $tempFile 
        del $tempFile
    }
    ElseIf($C)
    {            
        $tempFile = [IO.Path]::GetTempFileName()
        $LastItem = $Result.Count -3  
        $incre = "true"            
        foreach ($s in  $Result[2..$LastItem] )
        {    
            $s= [regex]::Replace($s,"^ ","")            
            $s= [regex]::Replace($s," +",",")
            $s= [regex]::Replace($s,"-","")
            $s= $s.Trim()                
            if($incre -eq "true")
            {
                $sTemp1=$s
                $sTemp = $sTemp1.Split(',')
                $sTemp[5]="OK(NormalChunklets)" 
                $sTemp[6]="Fail(NormalChunklets/Used)" 
                $sTemp[7]="Free(NormalChunklets)"
                $sTemp[8]="Uninit(NormalChunklets)"
                $sTemp[10]="Fail(NormalChunklets/UnUsed)"
                $sTemp[11]="OK(SpareChunklets)" 
                $sTemp[12]="Fail(SpareChunklets/Used)" 
                $sTemp[13]="Free(SpareChunklets)"
                $sTemp[14]="Uninit(SpareChunklets)"
                $sTemp[15]="Fail(SpareChunklets/UnUsed)"
                $newTemp= [regex]::Replace($sTemp," ",",")    
                $newTemp= $newTemp.Trim()
                $s=$newTemp
            }                
            Add-Content -Path $tempFile -Value $s
            #Write-Host "$s"
            $incre="false"                
        }            
        Import-Csv $tempFile 
        del $tempFile
    }
    ElseIf($E)
    {            
        $tempFile = [IO.Path]::GetTempFileName()
        $LastItem = $Result.Count -3  
        $incre = "true"            
        foreach ($s in  $Result[1..$LastItem] )
        {    
            $s= [regex]::Replace($s,"^ ","")            
            $s= [regex]::Replace($s," +",",")
            $s= [regex]::Replace($s,"-","")
            $s= $s.Trim()                
            if($incre -eq "true")
            {
                $sTemp1=$s
                $sTemp = $sTemp1.Split(',')
                $sTemp[4]="Corr(ReadError)" 
                $sTemp[5]="UnCorr(ReadError)" 
                $sTemp[6]="Corr(WriteError)"
                $sTemp[7]="UnCorr(WriteError)"
                $newTemp= [regex]::Replace($sTemp," ",",")    
                $newTemp= $newTemp.Trim()
                $s=$newTemp
            }                
            Add-Content -Path $tempFile -Value $s
            #Write-Host "$s"
            $incre="false"                
        }
            
        Import-Csv $tempFile 
        del $tempFile
    }
    else
    {
        if($Result -match "Id")
        {
            $tempFile = [IO.Path]::GetTempFileName()
            $LastItem = $Result.Count -3  
            #Write-Host " Result Count =" $Result.Count
            foreach ($s in  $Result[1..$LastItem] )
            {        
                $s= [regex]::Replace($s,"^ ","")            
                $s= [regex]::Replace($s," +",",")
                $s= [regex]::Replace($s,"-","")
                $s= $s.Trim()     
                Add-Content -Path $tempFile -Value $s
                #Write-Host " only else statement"
            }
            write-host ""
            if($Space)
            {
                write-host "Size | Volume | Spare | Free | Unavail & Failed values are in (MiB)."
            }
            else
            {
                write-host "Total and Free values are in (MiB)."
            }                
            Import-Csv $tempFile 
            del $tempFile
        }
    }        
    if($Result.Count -gt 1)
    {    
        return "Success : Command Show-PD execute Successfully."
    }
    else
    {
        return $Result        
    }     
} # End Show-PD

##########################################################################
############################ FUNCTION Remove-PD ##########################
##########################################################################
Function Remove-PD()
{
<#
  .SYNOPSIS
   Remove-PD - Remove a physical disk (PD) from system use.

  .DESCRIPTION
   The Remove-PD command removes PD definitions from system use.

  .EXAMPLE
    The following example removes a PD with ID 1:
    Remove-PD -PDID 1
   
  .PARAMETER PDID
    Specifies the PD(s), identified by integers, to be removed from system use.

  .Notes
    NAME: Remove-PD
    LASTEDIT 30/10/2019
    KEYWORDS: Remove-PD
  
  .Link
    http://www.hpe.com

 #Requires PS -Version 3.0
#>

[CmdletBinding()]
 param(
    [Parameter(Position=0, Mandatory=$True)]
    [System.String]
    $PDID,

    [Parameter(Position=1, Mandatory=$false, ValueFromPipeline=$true)]
    $SANConnection = $global:SANConnection
 )

 Write-DebugLog "Start: In Remove-PD - validating input values" $Debug 
 #check if connection object contents are null/empty
 if(!$SANConnection)
 {
    #check if connection object contents are null/empty
    $Validate1 = Test-CLIConnection $SANConnection
    if($Validate1 -eq "Failed")
    {
        #check if global connection object contents are null/empty
        $Validate2 = Test-CLIConnection $global:SANConnection
        if($Validate2 -eq "Failed")
        {
            Write-DebugLog "Connection object is null/empty or the array address (FQDN/IP Address) or user credentials in the connection object are either null or incorrect. Create a valid connection object using New-CLIConnection or New-PoshSshConnection" " ERR: "
            Write-DebugLog "Stop: Exiting Remove-PD since SAN connection object values are null/empty" $Debug 
            Return "Unable to execute the cmdlet Remove-PD since no active storage connection session exists. `nUse New-PoshSSHConnection or New-CLIConnection to start a new storage connection session."
        }
    }
 }

 $plinkresult = Test-PARCli -SANConnection $SANConnection
 if($plinkresult -match "FAILURE :")
 {
    write-debuglog "$plinkresult"
    Return $plinkresult
 }

    $Cmd = " dismisspd "

 if($PDID)
 {
    $Cmd += " $PDID "
 }

 $Result = Invoke-CLICommand -Connection $SANConnection -cmds  $Cmd
 Write-DebugLog "Executing function : Remove-PD command -->" INFO: 
 
 Return $Result
} ## End-of Remove-PD

####################################################################################################################
################################################# FUNCTION Set-Cage ################################################
####################################################################################################################
Function Set-Cage
{
<#
  .SYNOPSIS
   The Set-Cage command enables service personnel to set or modify parameters for a drive cage.
   
 .DESCRIPTION
  The Set-Cage command enables service personnel to set or modify parameters for a drive cage.
      
  .EXAMPLE
    Set-Cage -Position left -CageName cage1
    This example demonstrates how to assign cage1 a position description of Side Left.

  .EXAMPLE
    Set-Cage -Position left -PSModel 1 -CageName cage1
    This example demonstrates how to assign model names to the power supplies in cage1. Inthisexample, cage1 hastwopowersupplies(0 and 1).
                
  .PARAMETER Position
    Sets a description for the position of the cage in the cabinet, where <position> is a description to be assigned by service personnel (for example, left-top)
  
  .PARAMETER PSModel
    Sets the model of a cage power supply, where <model> is a model name to be assigned to the power supply by service personnel.
    get information regarding PSModel try using [ Get-Cage -option d ]
    
  
  .PARAMETER CageName
    Indicates the name of the drive cage that is the object of the setcage operation.
    
  .Notes
    NAME: Set-Cage
    LASTEDIT: 30/10/2019
    KEYWORDS: Set-Cage
   
  .Link
     http://www.hpe.com
 
 #Requires PS -Version 3.0

 #>

[CmdletBinding()]
    param(
        [Parameter(Position=0, Mandatory=$false,ValueFromPipeline=$true)]
        [System.String]
        $Position,
        
        [Parameter(Position=1, Mandatory=$false,ValueFromPipeline=$true)]
        [System.String]
        $PSModel,
        
        [Parameter(Position=2, Mandatory=$false,ValueFromPipeline=$true)]
        [System.String]
        $CageName,
            
        [Parameter(Position=3, Mandatory=$false, ValueFromPipeline=$true)]
        $SANConnection = $global:SANConnection        
    )    
    Write-DebugLog "Start: In Set-Cage - validating input values" $Debug 
    #check if connection object contents are null/empty
    if(!$SANConnection)
    {        
        #check if connection object contents are null/empty
        $Validate1 = Test-CLIConnection $SANConnection
        if($Validate1 -eq "Failed")
        {
            #check if global connection object contents are null/empty
            $Validate2 = Test-CLIConnection $global:SANConnection
            if($Validate2 -eq "Failed")
            {
                Write-DebugLog "Connection object is null/empty or the array address (FQDN/IP Address) or user credentials in the connection object are either null or incorrect. Create a valid connection object using New-CLIConnection or New-PoshSshConnection" "ERR:"
                Write-DebugLog "Stop: Exiting Set-Cage since SAN connection object values are null/empty" $Debug
                return "Unable to execute the cmdlet Set-Cage since no active storage connection session exists. `nUse New-PoshSSHConnection or New-CLIConnection to start a new storage connection session."
            }
        }
    }
    $plinkresult = Test-PARCli
    if($plinkresult -match "FAILURE :")
    {
        write-debuglog "$plinkresult" "ERR:" 
        return $plinkresult
    }        
    $cmd= "setcage "
    if ($Position )
    {
        $cmd+="position $Position "
    }        
    if ($PSModel)
    {
        $cmd2="showcage -d"
        $Result2 = Invoke-CLICommand -Connection $SANConnection -cmds  $cmd2
        if($Result2 -match $PSModel)
        {
            $cmd+=" ps $PSModel "
        }    
        else
        {
            Write-DebugLog "Stop: Exiting Set-Cage -PSModel $PSModel is Not available "
            return "Failure: -PSModel $PSModel is Not available. To Find Available Model `n Try [Get-Cage -option d ] Command"
        }
    }        
    if ($CageName)
    {
        $cmd1="showcage"
        $Result1 = Invoke-CLICommand -Connection $SANConnection -cmds  $cmd1
        if($Result1 -match $CageName)
        {
            $cmd +="$CageName "
        }
        else
        {
            Write-DebugLog "Stop: Exiting Set-Cage -CageName $CageName is Not available "
            return "Failure: -CageName $CageName is Not available `n Try using [ Get-Cage ] Command to get list of Cage Name "
        }    
    }    
    else
    {
        Write-DebugLog "Stop: Exiting Set-Cage NO parameters is passed CageName is mandatory "
        return "ERROR: -CageName is a required parameter"
    }        
    $Result = Invoke-CLICommand -Connection $SANConnection -cmds  $cmd
    write-debuglog " The Set-Cage command enables service personnel to set or modify parameters for a drive cage " "INFO:"         
    if([string]::IsNullOrEmpty($Result))
    {
        return  "Success : Executing Set-Cage Command $Result "
    }
    else
    {
        return  "FAILURE : While Executing Set-Cage $Result"
    }         
} # End Set-Cage

####################################################################################################################
#################################################### FUNCTION Set-PD ###############################################
####################################################################################################################

Function Set-PD
{
<#
  .SYNOPSIS
   The Set-PD command marks a Physical Disk (PD) as allocatable or non allocatable for Logical Disks (LDs).
   
  .DESCRIPTION
   The Set-PD command marks a Physical Disk (PD) as allocatable or non allocatable for Logical Disks (LDs).
    
  .EXAMPLE
    Set-PD -Ldalloc off -PD_ID 20
    displays PD 20 marked as non allocatable for LDs.
   
  .EXAMPLE
    Set-PD -Ldalloc on -PD_ID 25
    displays PD 25 marked as allocatable for LDs.
           
  .PARAMETER ldalloc
    Specifies that the PD, as indicated with the PD_ID specifier, is either allocatable (on) or nonallocatable for LDs (off).
      
  .PARAMETER PD_ID
    Specifies the PD identification using an integer.
     
  .PARAMETER SANConnection
    Specify the SAN Connection object created with New-CLIConnection or New-PoshSshConnection
    
  .Notes
    NAME: Set-PD
    LASTEDIT: 30/10/2019
    KEYWORDS: Set-PD
   
  .Link
     http://www.hpe.com
 
 #Requires PS -Version 3.0

 #>

[CmdletBinding()]
    param(
        [Parameter(Position=0, Mandatory=$false)]
        [System.String]
        $Ldalloc,
        
        [Parameter(Position=1, Mandatory=$true,ValueFromPipeline=$true)]
        [System.String]
        $PD_ID,
            
        [Parameter(Position=2, Mandatory=$false, ValueFromPipeline=$true)]
        $SANConnection = $global:SANConnection        
    )        
    
    Write-DebugLog "Start: In Set-PD - validating input values" $Debug 
    #check if connection object contents are null/empty
    if(!$SANConnection)
    {                
        #check if connection object contents are null/empty
        $Validate1 = Test-CLIConnection $SANConnection
        if($Validate1 -eq "Failed")
        {
            #check if global connection object contents are null/empty
            $Validate2 = Test-CLIConnection $global:SANConnection
            if($Validate2 -eq "Failed")
            {
                Write-DebugLog "Connection object is null/empty or the array address (FQDN/IP Address) or user credentials in the connection object are either null or incorrect. Create a valid connection object using New-CLIConnection or New-PoshSshConnection" "ERR:"
                Write-DebugLog "Stop: Exiting Set-PD since SAN connection object values are null/empty" $Debug
                return "Unable to execute the cmdlet Set-PD since no active storage connection session exists. `nUse New-PoshSSHConnection or New-CLIConnection to start a new storage connection session."
            }
        }
    }
    $plinkresult = Test-PARCli
    if($plinkresult -match "FAILURE :")
    {
        write-debuglog "$plinkresult" "ERR:" 
        return $plinkresult
    }        
    $cmd= "setpd "    
    if ($Ldalloc)
    {
        $a = "on","off"
        $l=$Ldalloc
        if($a -eq $l)
        {
            $cmd+=" ldalloc $Ldalloc "    
        }
        else
        { 
            Write-DebugLog "Stop: Exiting Set-PD since -Ldalloc in incorrect "
            return "FAILURE : -Ldalloc $Ldalloc cannot be used only [on|off] can be used . "
        }
    }
    else
    {
        Write-DebugLog "Stop: Ldalloc is mandatory" $Debug
        return "Error : -Ldalloc is mandatory. "        
    }        
    if ($PD_ID)
    {
        $PD=$PD_ID
        if($PD -gt 4095)
        { 
            Write-DebugLog "Stop: Exiting Set-PD since -PD_ID $PD_ID Illegal integer argument "
            return "FAILURE : -PD_ID $PD_ID Illegal integer argument . Expected range [0-4095]. "
        }
        $cmd+=" $PD_ID "
    }
    else
    {
        Write-DebugLog "Stop: PD_ID is mandatory" $Debug
        return "Error : -PD_ID is mandatory. "        
    }        
    if ($cmd -eq "setpd ")
    {
        Write-DebugLog "FAILURE : Set-PD Should be used with Parameters, No parameters passed."
        return get-help  Set-PD 
    }    
    $Result = Invoke-CLICommand -Connection $SANConnection -cmds  $cmd
    
    write-debuglog " Executing Set-PD Physical Disk (PD) as allocatable or non allocatable for Logical Disks (LDs). with the command " "INFO:" 
    if([string]::IsNullOrEmpty($Result))
    {
        return  "Success : Executing Set-PD $Result"
    }
    else
    {
        return  "FAILURE : While Executing Set-PD $Result "
    }     
} # End Set-PD

##########################################################################
######################### FUNCTION Switch-PD #########################
##########################################################################
Function Switch-PD()
{
<#
  .SYNOPSIS
   Switch-PD - Spin up or down a physical disk (PD).

  .DESCRIPTION
   The Switch-PD command spins a PD up or down. This command is used when
   replacing a PD in a drive magazine.

  .EXAMPLE
    The following example instigates the spin up of a PD identified by its
    WWN of 2000000087002078:
    Switch-PD -Spinup -WWN 2000000087002078
  
  .PARAMETER Spinup
    Specifies that the PD is to spin up. If this subcommand is not used,
    then the spindown subcommand must be used.
  
  .PARAMETER Spindown
    Specifies that the PD is to spin down. If this subcommand is not used,
    then the spinup subcommand must be used.

  .PARAMETER Ovrd
   Specifies that the operation is forced, even if the PD is in use.

   
  .PARAMETER WWN
    Specifies the World Wide Name of the PD. This specifier can be repeated
    to identify multiple PDs.
   
  .Notes
    NAME: Switch-PD
    LASTEDIT 30/10/2019
    KEYWORDS: Switch-PD
  
  .Link
    http://www.hpe.com

 #Requires PS -Version 3.0
#>

[CmdletBinding()]
 param(
    [Parameter(Position=0, Mandatory=$false)]
    [switch]
    $Spinup,

    [Parameter(Position=1, Mandatory=$false)]
    [switch]
    $Spindown,
 
    [Parameter(Position=2, Mandatory=$false)]
    [switch]
    $Ovrd,    

    [Parameter(Position=3, Mandatory=$True)]
    [System.String]
    $WWN,

    [Parameter(Position=4, Mandatory=$false, ValueFromPipeline=$true)]
    $SANConnection = $global:SANConnection
 )

 Write-DebugLog "Start: In Switch-PD - validating input values" $Debug 
 #check if connection object contents are null/empty
 if(!$SANConnection)
 {
    #check if connection object contents are null/empty
    $Validate1 = Test-CLIConnection $SANConnection
    if($Validate1 -eq "Failed")
    {
        #check if global connection object contents are null/empty
        $Validate2 = Test-CLIConnection $global:SANConnection
        if($Validate2 -eq "Failed")
        {
            Write-DebugLog "Connection object is null/empty or the array address (FQDN/IP Address) or user credentials in the connection object are either null or incorrect. Create a valid connection object using New-CLIConnection or New-PoshSshConnection" " ERR: "
            Write-DebugLog "Stop: Exiting Switch-PD since SAN connection object values are null/empty" $Debug 
            Return "Unable to execute the cmdlet Switch-PD since no active storage connection session exists. `nUse New-PoshSSHConnection or New-CLIConnection to start a new storage connection session."
        }
    }
 }

 $plinkresult = Test-PARCli -SANConnection $SANConnection
 if($plinkresult -match "FAILURE :")
 {
   write-debuglog "$plinkresult"
   Return $plinkresult
 }

 $Cmd = " controlpd "

 if($Spinup)
 {
    $Cmd += " spinup "
 }
 elseif($Spindown)
 {
    $Cmd += " spindown "
 }
 else
 {
    Return "Select at least one from [ Spinup | Spindown ]"
 }

 if($Ovrd)
 {
    $Cmd += " -ovrd "
 }

 if($WWN)
 {
    $Cmd += " $WWN "
 }

 $Result = Invoke-CLICommand -Connection $SANConnection -cmds  $Cmd
 Write-DebugLog "Executing function : Switch-PD command -->" INFO: 
 
 Return $Result
} ## End-of Switch-PD
    
####################################################################################################################
################################################ FUNCTION Test-PD ##################################################
####################################################################################################################

Function Test-PD
{
<#
  .SYNOPSIS
    The Test-PD command executes surface scans or diagnostics on physical disks.
    
  .DESCRIPTION
    The Test-PD command executes surface scans or diagnostics on physical disks.
    
  .EXAMPLE
    Test-PD -specifier scrub -ch 500 -pd_ID 1
    This example Test-PD chunklet 500 on physical disk 1 is scanned for media defects.
   
  .EXAMPLE
    Test-PD -specifier scrub -count 150 -pd_ID 1
    This example scans a number of chunklets starting from -ch 150 on physical disk 1.
   
  .EXAMPLE
    Test-PD -specifier diag -path a -pd_ID 5
    This example Specifies a physical disk path as a,physical disk 5 is scanned for media defects.
        
  .EXAMPLE
    Test-PD -specifier diag -iosize 1s -pd_ID 3
    This example Specifies I/O size 1s, physical disk 3 is scanned for media defects.
    
  .EXAMPLE
    Test-PD -specifier diag -range 5m -pd_ID 3
    This example Limits diagnostic to range 5m [mb] physical disk 3 is scanned for media defects.
        
  .PARAMETER specifier
    scrub - Scans one or more chunklets for media defects.
    diag - Performs read, write, or verifies test diagnostics.
  
  .PARAMETER ch
    To scan a specific chunklet rather than the entire disk.
  
  .PARAMETER count
    To scan a number of chunklets starting from -ch.
  
  .PARAMETER path
    Specifies a physical disk path as [a|b|both|system].
  
  .PARAMETER test
    Specifies [read|write|verify] test diagnostics. If no type is specified, the default is read .

  .PARAMETER iosize
    Specifies I/O size, valid ranges are from 1s to 1m. If no size is specified, the default is 128k .
     
  .PARAMETER range
    Limits diagnostic regions to a specified size, from 2m to 2g.
    
  .PARAMETER pd_ID
    The ID of the physical disk to be checked. Only one pd_ID can be specified for the “scrub” test.
    
  .PARAMETER threads
    Specifies number of I/O threads, valid ranges are from 1 to 4. If the number of threads is not specified, the default is 1.
    
  .PARAMETER time
    Indicates the number of seconds to run, from 1 to 36000.
    
  .PARAMETER total
    Indicates total bytes to transfer per disk. If a size is not specified, the default size is 1g.
    
  .PARAMETER retry
     Specifies the total number of retries on an I/O error.
    
  .PARAMETER SANConnection
    Specify the SAN Connection object created with New-CLIConnection or New-PoshSshConnection
    
  .Notes
    NAME: Test-PD
    LASTEDIT: 30/10/2019
    KEYWORDS: Test-PD
   
  .Link
     http://www.hpe.com
 
 #Requires PS -Version 3.0

 #>

[CmdletBinding()]
    param(
        [Parameter(Position=0, Mandatory=$false,ValueFromPipeline=$true)]
        [System.String]
        $specifier,
        
        [Parameter(Position=1, Mandatory=$false,ValueFromPipeline=$true)]
        [System.String]
        $ch,
        
        [Parameter(Position=2, Mandatory=$false,ValueFromPipeline=$true)]
        [System.String]
        $count,
        
        [Parameter(Position=3, Mandatory=$false,ValueFromPipeline=$true)]
        [System.String]
        $path,
        
        [Parameter(Position=4, Mandatory=$false,ValueFromPipeline=$true)]
        [System.String]
        $test,
        
        [Parameter(Position=5, Mandatory=$false,ValueFromPipeline=$true)]
        [System.String]
        $iosize,
        
        [Parameter(Position=6, Mandatory=$false,ValueFromPipeline=$true)]
        [System.String]
        $range,
        
        [Parameter(Position=7, Mandatory=$false,ValueFromPipeline=$true)]
        [System.String]
        $threads,
        
        [Parameter(Position=8, Mandatory=$false,ValueFromPipeline=$true)]
        [System.String]
        $time,
        
        [Parameter(Position=9, Mandatory=$false,ValueFromPipeline=$true)]
        [System.String]
        $total,
        
        [Parameter(Position=10, Mandatory=$false,ValueFromPipeline=$true)]
        [System.String]
        $retry,
        
        [Parameter(Position=11, Mandatory=$false,ValueFromPipeline=$true)]
        [System.String]
        $pd_ID,
        
        [Parameter(Position=12, Mandatory=$false, ValueFromPipeline=$true)]
        $SANConnection = $global:SANConnection 
       
    )        
    
    Write-DebugLog "Start: In Test-PD - validating input values" $Debug 
    #check if connection object contents are null/empty
    if(!$SANConnection)
    {                
        #check if connection object contents are null/empty
        $Validate1 = Test-CLIConnection $SANConnection
        if($Validate1 -eq "Failed")
        {
            #check if global connection object contents are null/empty
            $Validate2 = Test-CLIConnection $global:SANConnection
            if($Validate2 -eq "Failed")
            {
                Write-DebugLog "Connection object is null/empty or the array address (FQDN/IP Address) or user credentials in the connection object are either null or incorrect. Create a valid connection object using New-CLIConnection or New-PoshSshConnection" "ERR:"
                Write-DebugLog "Stop: Exiting Test-PD since SAN connection object values are null/empty" $Debug
                return "Unable to execute the cmdlet Test-PD since no active storage connection session exists. `nUse New-PoshSSHConnection or New-CLIConnection to start a new storage connection session."
            }
        }
    }
    $plinkresult = Test-PARCli
    if($plinkresult -match "FAILURE :")
    {
        write-debuglog "$plinkresult" "ERR:" 
        return $plinkresult
    }        
    $cmd= "checkpd "    
    if ($specifier)
    {
        $spe = $specifier
        $demo = "scrub" , "diag"
        if($demo -eq $spe)
        {
            $cmd+=" $spe "
        }
        else
        {
            return " FAILURE : $spe is not a Valid specifier please use [scrub | diag] only. "
        }
    }
    else
    {
        return " FAILURE : -specifier is mandatory for Test-PD to execute "
    }        
    if ($ch)
    {
        $a=$ch
        [int]$b=$a
        if($a -eq $b)
        {
            if($cmd -match "scrub")
            {
                $cmd +=" -ch $ch "
            }
            else
            {
                return "FAILURE : -ch $ch cannot be used with -Specification diag "
            }
        }    
        else
        {
            Return "Error : -ch $ch Only Integers are Accepted "
    
        }
    }    
    if ($count)
    {
        $a=$count
        [int]$b=$a
        if($a -eq $b)
        {    
            if($cmd -match "scrub")
            {
                $cmd +=" -count $count "
            }
            else
            {
                return "FAILURE : -count $count cannot be used with -Specification diag "
            }
        }
        else
        {
            Return "Error : -count $count Only Integers are Accepted "    
        }
    }        
    if ($path)
    {
        if($cmd -match "diag")
        {
            $a = $path
            $b = "a","b","both","system"
            if($b -match $a)
            {
                $cmd +=" -path $path "
            }
            else
            {
                return "FAILURE : -path $path is invalid use [a | b | both | system ] only "
            }
        }
        else
        {
            return " FAILURE : -path $path cannot be used with -Specification scrub "
        }
    }        
    if ($test)
    {
        if($cmd -match "diag")
        {
            $a = $test 
            $b = "read","write","verify"
            if($b -eq $a)
            {
                $cmd +=" -test $test "
            }
            else
            {
                return "FAILURE : -test $test is invalid use [ read | write | verify ] only "
            }
        }
        else
        {
            return " FAILURE : -test $test cannot be used with -Specification scrub "
        }
    }            
    if ($iosize)
    {    
        if($cmd -match "diag")
        {
            $cmd +=" -iosize $iosize "
        }
        else
        {
            return "FAILURE : -test $test cannot be used with -Specification scrub "
        }
    }             
    if ($range )
    {
        if($cmd -match "diag")
        {
            $cmd +=" -range $range "
        }
        else
        {
            return "FAILURE : -range $range cannot be used with -Specification scrub "
        }
    }    
    if ($threads )
    {
        if($cmd -match "diag")
        {
            $cmd +=" -threads $threads "
        }
        else
        {
            return "FAILURE : -threads $threads cannot be used with -Specification scrub "
        }
    }
    if ($time )
    {
        if($cmd -match "diag")
        {
            $cmd +=" -time $time "
        }
        else
        {
            return "FAILURE : -time $time cannot be used with -Specification scrub "
        }
    }
    if ($total )
    {
        if($cmd -match "diag")
        {
            $cmd +=" -total $total "
        }
        else
        {
            return "FAILURE : -total $total cannot be used with -Specification scrub "
        }
    }
    if ($retry )
    {
        if($cmd -match "diag")
        {
            $cmd +=" -retry $retry "
        }
        else
        {
            return "FAILURE : -retry $retry cannot be used with -Specification scrub "
        }
    }
    if($pd_ID)
    {    
        $cmd += " $pd_ID "
    }
    else
    {
        return " FAILURE : pd_ID is mandatory for Test-PD to execute "
    }    
    $Result = Invoke-CLICommand -Connection $SANConnection -cmds  $cmd    
    write-debuglog " Executing surface scans or diagnostics on physical disks with the command " "INFO:" 
    return $Result    
} # End Test-PD

Export-ModuleMember Set-AdmitsPD , Find-Cage , Get-Cage , Show-PD , Remove-PD , Set-Cage , Set-PD , Switch-PD , Test-PD
# SIG # Begin signature block
# MIIh0AYJKoZIhvcNAQcCoIIhwTCCIb0CAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDpM81qnx1Qq+4P
# Rh9FsAp/XolnKUU/S+cJQCwhEDHX3qCCEKswggUpMIIEEaADAgECAhB4Lu4fcD9z
# 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
# V3AcPKRYLqPzW0sH3DJZ84enGm1YMYIQezCCEHcCAQEwgZAwfDELMAkGA1UEBhMC
# R0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9y
# ZDEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMSQwIgYDVQQDExtTZWN0aWdvIFJT
# QSBDb2RlIFNpZ25pbmcgQ0ECEHgu7h9wP3PFSAP6N/U6iqUwDQYJYIZIAWUDBAIB
# BQCgfDAQBgorBgEEAYI3AgEMMQIwADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIB
# BDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQg
# vK2U5xL1spoKPhbOxByZWmFmy8s9yfKl+P/01M1HLwYwDQYJKoZIhvcNAQEBBQAE
# ggEAOzw6bCpJSVbQd7ePhEgvnUCAJR2gMqSCBb7Lh8I7QLEwkH3DUFYcLdjfli9M
# ympAZAg9CH2PXpQ8GQu8s2WMlZ57F5DpFvnxWrhmBLkVKwoO+TeZLBJJ9RZrt0fv
# nlL3wnXdZTb2ELTL+tP1Wri6QDt1a2MMmLaoJkfEcOxK+vX+RH27uATVe/eodYIT
# QpFwaOom578MNn5yo4o/dhBWhlozsbCg2z+k2WG/5ocPPvmeF2JImt3KfDxJD7JJ
# bvkBrvEn7iqKOWro8evlbXDp2DGPb1zEjRLmNtZizs0leV8musMBJAibDhqI0xHx
# ZYwLiawRkGBMwnzaTb+ucpIVnqGCDj0wgg45BgorBgEEAYI3AwMBMYIOKTCCDiUG
# CSqGSIb3DQEHAqCCDhYwgg4SAgEDMQ0wCwYJYIZIAWUDBAIBMIIBDwYLKoZIhvcN
# AQkQAQSggf8EgfwwgfkCAQEGC2CGSAGG+EUBBxcDMDEwDQYJYIZIAWUDBAIBBQAE
# IA/PD3E/VPsB+A/IS3n5LBGMY8uwQoEqkxmcMzDiSi7VAhUAyFX8kSeptgKkSU7D
# lV/VdUobaAkYDzIwMjEwNjE5MDQwNDU1WjADAgEeoIGGpIGDMIGAMQswCQYDVQQG
# EwJVUzEdMBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xHzAdBgNVBAsTFlN5
# bWFudGVjIFRydXN0IE5ldHdvcmsxMTAvBgNVBAMTKFN5bWFudGVjIFNIQTI1NiBU
# aW1lU3RhbXBpbmcgU2lnbmVyIC0gRzOgggqLMIIFODCCBCCgAwIBAgIQewWx1Elo
# UUT3yYnSnBmdEjANBgkqhkiG9w0BAQsFADCBvTELMAkGA1UEBhMCVVMxFzAVBgNV
# BAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3
# b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRo
# b3JpemVkIHVzZSBvbmx5MTgwNgYDVQQDEy9WZXJpU2lnbiBVbml2ZXJzYWwgUm9v
# dCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0xNjAxMTIwMDAwMDBaFw0zMTAx
# MTEyMzU5NTlaMHcxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jw
# b3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEoMCYGA1UE
# AxMfU3ltYW50ZWMgU0hBMjU2IFRpbWVTdGFtcGluZyBDQTCCASIwDQYJKoZIhvcN
# AQEBBQADggEPADCCAQoCggEBALtZnVlVT52Mcl0agaLrVfOwAa08cawyjwVrhpon
# ADKXak3JZBRLKbvC2Sm5Luxjs+HPPwtWkPhiG37rpgfi3n9ebUA41JEG50F8eRzL
# y60bv9iVkfPw7mz4rZY5Ln/BJ7h4OcWEpe3tr4eOzo3HberSmLU6Hx45ncP0mqj0
# hOHE0XxxxgYptD/kgw0mw3sIPk35CrczSf/KO9T1sptL4YiZGvXA6TMU1t/HgNuR
# 7v68kldyd/TNqMz+CfWTN76ViGrF3PSxS9TO6AmRX7WEeTWKeKwZMo8jwTJBG1kO
# qT6xzPnWK++32OTVHW0ROpL2k8mc40juu1MO1DaXhnjFoTcCAwEAAaOCAXcwggFz
# MA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/AgEAMGYGA1UdIARfMF0w
# WwYLYIZIAYb4RQEHFwMwTDAjBggrBgEFBQcCARYXaHR0cHM6Ly9kLnN5bWNiLmNv
# bS9jcHMwJQYIKwYBBQUHAgIwGRoXaHR0cHM6Ly9kLnN5bWNiLmNvbS9ycGEwLgYI
# KwYBBQUHAQEEIjAgMB4GCCsGAQUFBzABhhJodHRwOi8vcy5zeW1jZC5jb20wNgYD
# VR0fBC8wLTAroCmgJ4YlaHR0cDovL3Muc3ltY2IuY29tL3VuaXZlcnNhbC1yb290
# LmNybDATBgNVHSUEDDAKBggrBgEFBQcDCDAoBgNVHREEITAfpB0wGzEZMBcGA1UE
# AxMQVGltZVN0YW1wLTIwNDgtMzAdBgNVHQ4EFgQUr2PWyqNOhXLgp7xB8ymiOH+A
# dWIwHwYDVR0jBBgwFoAUtnf6aUhHn1MS1cLqBzJ2B9GXBxkwDQYJKoZIhvcNAQEL
# BQADggEBAHXqsC3VNBlcMkX+DuHUT6Z4wW/X6t3cT/OhyIGI96ePFeZAKa3mXfSi
# 2VZkhHEwKt0eYRdmIFYGmBmNXXHy+Je8Cf0ckUfJ4uiNA/vMkC/WCmxOM+zWtJPI
# TJBjSDlAIcTd1m6JmDy1mJfoqQa3CcmPU1dBkC/hHk1O3MoQeGxCbvC2xfhhXFL1
# TvZrjfdKer7zzf0D19n2A6gP41P3CnXsxnUuqmaFBJm3+AZX4cYO9uiv2uybGB+q
# ueM6AL/OipTLAduexzi7D1Kr0eOUA2AKTaD+J20UMvw/l0Dhv5mJ2+Q5FL3a5NPD
# 6itas5VYVQR9x5rsIwONhSrS/66pYYEwggVLMIIEM6ADAgECAhB71OWvuswHP6EB
# IwQiQU0SMA0GCSqGSIb3DQEBCwUAMHcxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRT
# eW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0
# d29yazEoMCYGA1UEAxMfU3ltYW50ZWMgU0hBMjU2IFRpbWVTdGFtcGluZyBDQTAe
# Fw0xNzEyMjMwMDAwMDBaFw0yOTAzMjIyMzU5NTlaMIGAMQswCQYDVQQGEwJVUzEd
# MBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xHzAdBgNVBAsTFlN5bWFudGVj
# IFRydXN0IE5ldHdvcmsxMTAvBgNVBAMTKFN5bWFudGVjIFNIQTI1NiBUaW1lU3Rh
# bXBpbmcgU2lnbmVyIC0gRzMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQCvDoqq+Ny/aXtUF3FHCb2NPIH4dBV3Z5Cc/d5OAp5LdvblNj5l1SQgbTD53R2D
# 6T8nSjNObRaK5I1AjSKqvqcLG9IHtjy1GiQo+BtyUT3ICYgmCDr5+kMjdUdwDLNf
# W48IHXJIV2VNrwI8QPf03TI4kz/lLKbzWSPLgN4TTfkQyaoKGGxVYVfR8QIsxLWr
# 8mwj0p8NDxlsrYViaf1OhcGKUjGrW9jJdFLjV2wiv1V/b8oGqz9KtyJ2ZezsNvKW
# lYEmLP27mKoBONOvJUCbCVPwKVeFWF7qhUhBIYfl3rTTJrJ7QFNYeY5SMQZNlANF
# xM48A+y3API6IsW0b+XvsIqbAgMBAAGjggHHMIIBwzAMBgNVHRMBAf8EAjAAMGYG
# A1UdIARfMF0wWwYLYIZIAYb4RQEHFwMwTDAjBggrBgEFBQcCARYXaHR0cHM6Ly9k
# LnN5bWNiLmNvbS9jcHMwJQYIKwYBBQUHAgIwGRoXaHR0cHM6Ly9kLnN5bWNiLmNv
# bS9ycGEwQAYDVR0fBDkwNzA1oDOgMYYvaHR0cDovL3RzLWNybC53cy5zeW1hbnRl
# Yy5jb20vc2hhMjU2LXRzcy1jYS5jcmwwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgw
# DgYDVR0PAQH/BAQDAgeAMHcGCCsGAQUFBwEBBGswaTAqBggrBgEFBQcwAYYeaHR0
# cDovL3RzLW9jc3Aud3Muc3ltYW50ZWMuY29tMDsGCCsGAQUFBzAChi9odHRwOi8v
# dHMtYWlhLndzLnN5bWFudGVjLmNvbS9zaGEyNTYtdHNzLWNhLmNlcjAoBgNVHREE
# ITAfpB0wGzEZMBcGA1UEAxMQVGltZVN0YW1wLTIwNDgtNjAdBgNVHQ4EFgQUpRMB
# qZ+FzBtuFh5fOzGqeTYAex0wHwYDVR0jBBgwFoAUr2PWyqNOhXLgp7xB8ymiOH+A
# dWIwDQYJKoZIhvcNAQELBQADggEBAEaer/C4ol+imUjPqCdLIc2yuaZycGMv41Up
# ezlGTud+ZQZYi7xXipINCNgQujYk+gp7+zvTYr9KlBXmgtuKVG3/KP5nz3E/5jMJ
# 2aJZEPQeSv5lzN7Ua+NSKXUASiulzMub6KlN97QXWZJBw7c/hub2wH9EPEZcF1rj
# pDvVaSbVIX3hgGd+Yqy3Ti4VmuWcI69bEepxqUH5DXk4qaENz7Sx2j6aescixXTN
# 30cJhsT8kSWyG5bphQjo3ep0YG5gpVZ6DchEWNzm+UgUnuW/3gC9d7GYFHIUJN/H
# ESwfAD/DSxTGZxzMHgajkF9cVIs+4zNbgg/Ft4YCTnGf6WZFP3YxggJaMIICVgIB
# ATCBizB3MQswCQYDVQQGEwJVUzEdMBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRp
# b24xHzAdBgNVBAsTFlN5bWFudGVjIFRydXN0IE5ldHdvcmsxKDAmBgNVBAMTH1N5
# bWFudGVjIFNIQTI1NiBUaW1lU3RhbXBpbmcgQ0ECEHvU5a+6zAc/oQEjBCJBTRIw
# CwYJYIZIAWUDBAIBoIGkMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAcBgkq
# hkiG9w0BCQUxDxcNMjEwNjE5MDQwNDU1WjAvBgkqhkiG9w0BCQQxIgQglERKIyOe
# cBA0izYZludrZzLxU2W6JzbRfXW6wZUQ61UwNwYLKoZIhvcNAQkQAi8xKDAmMCQw
# IgQgxHTOdgB9AjlODaXk3nwUxoD54oIBPP72U+9dtx/fYfgwCwYJKoZIhvcNAQEB
# BIIBAEHzvlcHUiCgFnSCUoeyim6PiRmgAJzi+JyopQOQoXo0wfTgjC0WV3ma9vHf
# uNSNz4cvsV1u15BraZfTtUA4EE19Vvz4xuzn0ZpIHjWa255Z5XQLEoDDLctb4ruk
# Zlz8TmSypIY4+onkPo+5F/dA1GouDrX8uPR4x7e8VsHZzYbEUZJ0Mhk83dTOSFyp
# gYfxJVObVEstQh1Rt4hSTEL9Iniwj4DPzByxbBfrBGi9UqRZDj1GXXRR4TJPOdJV
# sfSAx0+Dw7OQdkEAImZVWgiV+P6U0KU5OVrJ6IioAxSZN7CD6Fa3MkH5f3icFpmg
# vAm9Oo8I5UpQVbKYWosmsxZXKaA=
# SIG # End signature block