CLI/TaskManagement.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: TaskManagement.psm1
## Description: Task Management cmdlets
##
## Created: January 2020
## Last Modified: January 2020
## 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 Get-Task ##############################
############################################################################
Function Get-Task
{
<#
  .SYNOPSIS
    Displays information about tasks.
  
  .DESCRIPTION
    Displays information about tasks.
    
  .EXAMPLE
    Get-Task
    Display all tasks.
        
  .EXAMPLE
    Get-Task -All
    Display all tasks. Unless the -all option is specified, system tasks
    are not displayed.
    
  .EXAMPLE
    Get-Task -Done
     Display includes only tasks that are successfully completed

  .EXAMPLE
    Get-Task -Failed
     Display includes only tasks that are unsuccessfully completed.
    
  .EXAMPLE
    Get-Task -Active
     Display includes only tasks that are currently in progress.
    
  .EXAMPLE
    Get-Task -Hours 10
     Show only tasks started within the past <hours>
     
  .EXAMPLE
    Get-Task -Task_type xyz
     Specifies that specified patterns are treated as glob-style patterns and that all tasks whose types match the specified pattern are displayed
    
  .EXAMPLE
    Get-Task -taskID 4
     Show detailed task status for specified task 4.

  .PARAMETER All
    Displays all tasks.
  
  .PARAMETER Done
    Displays only tasks that are successfully completed.
  
  .PARAMETER Failed
    Displays only tasks that are unsuccessfully completed.
  
  .PARAMETER Active
    Displays only tasks that are currently in progress
    
  .PARAMETER Hours
    Show only tasks started within the past <hours>, where <hours> is an integer from 1 through 99999.
    
  .PARAMETER Task_type
     Specifies that specified patterns are treated as glob-style patterns and that all tasks whose types match the specified pattern are displayed. To see the different task types use the showtask column help.
    
  .PARAMETER TaskID
     Show detailed task status for specified tasks. Tasks must be explicitly specified using their task IDs <task_ID>. Multiple task IDs can be specified. This option cannot be used in conjunction with other options.
    
  .PARAMETER SANConnection
    Specify the SAN Connection object created with New-CLIConnection or New-PoshSshConnection
    
  .Notes
    NAME: Get-Task
    LASTEDIT: January 2020
    KEYWORDS: Get-Task
   
  .Link
     http://www.hpe.com
 
 #Requires PS -Version 3.0

 #>

[CmdletBinding()]
    param(
        [Parameter(Position=0, Mandatory=$false,ValueFromPipeline=$true)]
        [System.String]
        $TaskID,   

        [Parameter(Position=1, Mandatory=$false,ValueFromPipeline=$true)]
        [System.String]
        $Task_type,
        
        [Parameter(Position=2, Mandatory=$false)]
        [Switch]
        $All,    
        
        [Parameter(Position=3, Mandatory=$false)]
        [Switch]
        $Done,
        
        [Parameter(Position=4, Mandatory=$false)]
        [Switch]
        $Failed,
        
        [Parameter(Position=5, Mandatory=$false)]
        [Switch]
        $Active,

        [Parameter(Position=6, Mandatory=$false,ValueFromPipeline=$true)]
        [System.String]
        $Hours,
        
        [Parameter(Position=7, Mandatory=$false, ValueFromPipeline=$true)]
        $SANConnection = $global:SANConnection         
    )        
    
    Write-DebugLog "Start: In Get-Task - 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-Task since SAN connection object values are null/empty" $Debug
                return "Unable to execute the cmdlet Get-Task 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" "ERR:" 
        return $plinkresult
    }
    
    $taskcmd = "showtask "
    
    if($TaskID)    
    {
        $taskcmd +=" -d $TaskID "
    }
    if($Task_type)    
    {
        $taskcmd +=" -type $Task_type "
    }    
    if($All)    
    {
        $taskcmd +=" -all "
    }
    if($Done)    
    {
        $taskcmd +=" -done "
    }
    if($Failed)    
    {
        $taskcmd +=" -failed "
    }
    if($Active)    
    {
        $taskcmd +=" -active "
    }
    if($Hours)    
    {
        $taskcmd +=" -t $Hours "
    }
    
    $result = Invoke-CLICommand -Connection $SANConnection -cmds  $taskcmd
    #write-host $result
    write-debuglog " Running get task status with the command --> $taskcmd" "INFO:"
    if($TaskID)    
    {
        return $result
    }    
    if($Result -match "Id" )
    {
        $tempFile = [IO.Path]::GetTempFileName()
        $LastItem = $Result.Count  
        $incre = "true"
        foreach ($s in  $Result[0..$LastItem] )
        {        
            $s= [regex]::Replace($s,"^ ","")            
            $s= [regex]::Replace($s," +",",")    
            $s= [regex]::Replace($s,"-","")            
            $s= $s.Trim() -replace 'StartTime,FinishTime','Date(ST),Time(ST),Zome(ST),Date(FT),Time(FT),Zome(FT)' 
            if($incre -eq "true")
            {
                $s=$s.Substring(1)                    
            }
            Add-Content -Path $tempFile -Value $s
            $incre="false"        
        }
        Import-Csv $tempFile 
        del $tempFile
    }    
    if($Result -match "Id")
    {
        return
        #return " Success : Executing Get-Task"
    }
    else
    {            
        return  $Result
    }    
} #END Get-Task

####################################################################################################################
## FUNCTION Remove-Task
####################################################################################################################
Function Remove-Task {
    <#
  .SYNOPSIS
    Remove one or more tasks or task details.
                                                                                                           .
  .DESCRIPTION
    The Remove-Task command removes information about one or more completed tasks
    and their details.
 
  .PARAMETER A
    Remove all tasks including details.

  .PARAMETER D
    Remove task details only.

  .PARAMETER F
    Specifies that the command is to be forced. You are not prompted for
    confirmation before the task is removed.
   
  .PARAMETER T <hours>
     Removes tasks that have not been active within the past <hours>, where
     <hours> is an integer from 1 through 99999.

  .PARAMETER TaskID
    Allows you to specify tasks to be removed using their task IDs.

  .EXAMPLES
    Remove a task based on the task ID

    Remove-Task 2

    Remove the following tasks?
    2
    select q=quit y=yes n=no: y

  .EXAMPLES
    Remove all tasks, including details

    Remove-Task -A

    Remove all tasks?
    select q=quit y=yes n=no: y
  
  .NOTES
    With this command, the specified task ID and any information associated with
    it are removed from the system. However, task IDs are not recycled, so the
    next task started on the system uses the next whole integer that has not
    already been used. Task IDs roll over at 29999. The system stores
    information for the most recent 2000 tasks.

    NAME: Remove-Task
    LASTEDIT: 25/04/2021
    KEYWORDS: Remove-Task
   
  .Link
     http://www.hpe.com
 
 #Requires PS -Version 3.0

 #>

    [CmdletBinding()]
    param(
        [Parameter(Position = 0, Mandatory = $false, HelpMessage="specify tasks to be removed using their task ID")]
        [System.String]
        $TaskID,
        
        [Parameter(Position = 1, Mandatory = $false, HelpMessage="Remove all tasks including details")]
        [Switch]
        $A,

        [Parameter(Position = 2, Mandatory = $false, HelpMessage="Remove task details only")]
        [Switch]
        $D,

        [Parameter(Position = 3, Mandatory = $false, HelpMessage="Specifies that the command is to be forced")]
        [Switch]
        $F,       

        [Parameter(Position = 4, Mandatory = $false, HelpMessage="Remove tasks that have not been active within the past <hours>, where
     <hours> is an integer from 1 through 99999"
)]
        [System.String]
        $T,        
        
        [Parameter(Position = 5, Mandatory = $false, ValueFromPipeline = $true)]
        $SANConnection = $global:SANConnection        
    )    
    
    Write-DebugLog "Start: In Remove-Task - 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-Task since SAN connection object values are null/empty" $Debug
                return "Unable to execute the cmdlet Remove-Task 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 = "removetask "    
    
    if ($F) {
        $cmd += " -f "        
    }
    else {
        Return "Force removal is only supported with the Remove-Task cmdlet."
    }
    if ($TaskID) {
        $cmd += "$TaskID"        
    }
    if ($A) {
        $cmd += " -a"        
    }
    elseif ($D) {
        $cmd += " -d"        
    }
    elseif ($T) {
        $cmd += " -t $T"        
    }    
    
    $Result = Invoke-CLICommand -Connection $SANConnection -cmds  $cmd

    write-debuglog " The Remove-Task command removes hosts from a remote copy group" "INFO:" 
    return     $Result    

} # End Remove-Task

####################################################################################################################
## FUNCTION Stop-Task
####################################################################################################################
Function Stop-Task {
  <#
.SYNOPSIS
  Cancel one or more tasks
                                                                                                         .
.DESCRIPTION
   The Stop-Task command cancels one or more tasks.

.PARAMETER F
  Forces the command. The command completes the process without prompting
  for confirmation.
 
.PARAMETER ALL
   Cancels all active tasks. If not specified, a task ID(s) must be
   specified.

.PARAMETER TaskID
   Cancels only tasks identified by their task IDs.
   TaskID must be an unsigned integer within 1-29999 range.

.EXAMPLES
  Cancel a task using the task ID

  Stop-Task 1

.NOTES
  The Stop-Task command can return before a cancellation is completed. Thus,
  resources reserved for a task might not be immediately available. This can
  prevent actions like restarting the canceled task. Use the waittask command
  to ensure orderly completion of the cancellation before taking other
  actions. See waittask for more details.

  A Service user is only allowed to cancel tasks started by that specific user.

  NAME: Stop-Task
  LASTEDIT: 25/04/2021
  KEYWORDS: Stop-Task
 
.Link
   http://www.hpe.com

#Requires PS -Version 3.0

#>

  [CmdletBinding()]
  param(

    [Parameter(Position = 0, Mandatory = $false)]
    [System.String]
    $TaskID,        


    [Parameter(Position = 1, Mandatory = $false)]
    [Switch]
    $F,       

    [Parameter(Position = 2, Mandatory = $false)]
    [System.String]
    $ALL,        
  
    [Parameter(Position = 3, Mandatory = $false, ValueFromPipeline = $true)]
    $SANConnection = $global:SANConnection        
  )    

  Write-DebugLog "Start: In Stop-Task - 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 Stop-Task since SAN connection object values are null/empty" $Debug
        return "Unable to execute the cmdlet Stop-Task 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 = "canceltask "    

  if ($F) {
    $cmd += " -f "        
  }
  else {
    Return "Force cancellation is only supported with the Stop-Task cmdlet."
  }
  if ($TaskID) {
    $cmd += "$TaskID"        
  }
  if ($ALL) {
    $cmd += " -all"        
  }        

  $Result = Invoke-CLICommand -Connection $SANConnection -cmds  $cmd

  write-debuglog " The Stop-Task command removes hosts from a remote copy group" "INFO:" 
  return     $Result    

} # End Stop-Task

####################################################################################################################
## FUNCTION Wait-Task
####################################################################################################################
Function Wait-Task {
    <#
  .SYNOPSIS
    Wait for tasks to complete.
                                                                                                           .
  .DESCRIPTION
     The Wait-Task cmdlet asks the CLI to wait for a task to complete before
     proceeding. The cmdlet automatically notifies you when the specified task
     is finished.

  .PARAMETER V
    Displays the detailed status of the task specified by <TaskID> as it
    executes. When the task completes, this command exits.
   
  .PARAMETER TaskID
      Indicates one or more tasks to wait for using their task IDs. When no
      task IDs are specified, the command waits for all non-system tasks
      to complete. To wait for system tasks, <TaskID> must be specified.

  .PARAMETER Q
     Quiet; do not report the end state of the tasks, only wait for them to
     exit.

  .EXAMPLES
    The following example shows how to wait for a task using the task ID. When
    successful, the command returns only after the task completes.

    Wait-Task 1
    Task 1 done
  
  .NOTES
    This cmdlet returns an error if any of the tasks it is waiting for fail.

    NAME: Wait-Task
    LASTEDIT: 25/04/2021
    KEYWORDS: Wait-Task
   
  .Link
     http://www.hpe.com
 
 #Requires PS -Version 3.0

 #>

    [CmdletBinding()]
    param(
        [Parameter(Position = 0, Mandatory = $false)]
        [Switch]
        $V, 
        
        [Parameter(Position = 1, Mandatory = $false)]
        [System.String]
        $TaskID,
        
        [Parameter(Position = 2, Mandatory = $false)]
        [Switch]
        $Q,        
        
        [Parameter(Position = 3, Mandatory = $false, ValueFromPipeline = $true)]
        $SANConnection = $global:SANConnection        
    )    
    
    Write-DebugLog "Start: In Wait-Task - 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 Wait-Task since SAN connection object values are null/empty" $Debug
                return "Unable to execute the cmdlet Wait-Task 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 = "waittask "    
    
    if ($V) {
        $cmd += " -v "        
    }
    if ($TaskID) {
        $cmd += "$TaskID"        
    }
    if ($Q) {
        $cmd += " -q"        
    }        
    
    $Result = Invoke-CLICommand -Connection $SANConnection -cmds  $cmd

    write-debuglog " Executed the Wait-Task cmdlet" "INFO:" 

    return     $Result    

} # End Wait-Task

Export-ModuleMember Get-Task , Remove-Task , Stop-Task , Wait-Task
# SIG # Begin signature block
# MIIh0AYJKoZIhvcNAQcCoIIhwTCCIb0CAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAe5+Lx3ofHaLFd
# qk3gVFfn1lIT1Qw9CTORfvWp8GYX56CCEKswggUpMIIEEaADAgECAhB4Lu4fcD9z
# 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
# sICxgVZMP7Gd6G2yxjsF1LooodIi36glK3l6/S8FnUAwDQYJKoZIhvcNAQEBBQAE
# ggEAHjKAtjOWTa1X6lFJHw0VjtJnEotp5vM8a5kBiIH0EqmNKnsXyhdqo7NnIrhL
# Zh3UK437Ww85NJLsCMb6eS81zjiS3Xf8Xy6vQqT6grT0bQNLQrlgiP0RhBNYIKOW
# V/2QvYUdmrM7RvbAz2nftKwryg5DJWiLzK815ryRkyqMeU/ZXzi2XxmzxlFFGE0z
# kYY8DYram6Jxq2+5CMQyFExM6ai21N/27WPsbLzPNuVML3o3VQHaIasPOrT1dOS8
# QxlH5XH/r2n/MACVOWg/rta42rDYfxVSlbX82OHX8eUbP16pKr8pnQN1Mse4RDnc
# 0bodM4lpuJvIe+AikrVqWgOCLaGCDj0wgg45BgorBgEEAYI3AwMBMYIOKTCCDiUG
# CSqGSIb3DQEHAqCCDhYwgg4SAgEDMQ0wCwYJYIZIAWUDBAIBMIIBDwYLKoZIhvcN
# AQkQAQSggf8EgfwwgfkCAQEGC2CGSAGG+EUBBxcDMDEwDQYJYIZIAWUDBAIBBQAE
# IDfbx1LHEmF1q5ybxOTO9SUtpfwGvs30ULStqjCNQHhxAhUAs0BSgyDlXx0paB55
# 4Yh9ig/rVTAYDzIwMjEwNjE5MDQzMDQwWjADAgEeoIGGpIGDMIGAMQswCQYDVQQG
# 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
# hkiG9w0BCQUxDxcNMjEwNjE5MDQzMDQwWjAvBgkqhkiG9w0BCQQxIgQg6Ydfxw5O
# Dm3lo7bXMTjfWMgHHfpbHf5gsErZ28Fh5y0wNwYLKoZIhvcNAQkQAi8xKDAmMCQw
# IgQgxHTOdgB9AjlODaXk3nwUxoD54oIBPP72U+9dtx/fYfgwCwYJKoZIhvcNAQEB
# BIIBAIbHGR5hHikgqeYoxfmCeWO3DG+KG77dy1EE1a1zFtw9wrwoxDxsVUzewm+a
# 754MzgDYzS+4aE3/9GGel8lvKCnnMkO6fwWkrG7X46nwpyLEEf3MHqW/+L8sO3ff
# OhUvINWEym00nb2zVb98GVCoZB6kOL2QVPFKnty/lY2TjIz7hp7mKHGzGyhEzcv6
# 84AMnuGZHfcaD5DxprfCiT7TFLY7pf13hVlOJlqmsK457B88blB44e9r9SXVFnwg
# BiHPGREZB3Uso0dcoBBdrb0T5OG50vdbw04Tvt+7Ig9K7A1D6Z0hEEqPLNXIeGPM
# TF2vPdK3ZCwZLminjIzASMfDPAc=
# SIG # End signature block