PureStorage.CBS.AVS.psm1

 . $PSScriptRoot/PureStorage.CommonUtil.ps1
 . $PSScriptRoot/PureStorage.RunCommandLauncher.ps1
 . $PSScriptRoot/PureStorage.CBS.AVS.VMFS.ps1
 . $PSScriptRoot/PureStorage.CBS.AVS.Configuration.ps1

function Build-PCBSCluster {
    <#
    .SYNOPSIS
     Build or update settings for a cluster of ESXi servers
    .DESCRIPTION
     Build or update settings for a cluster of ESXi servers. Creates a hostgroup in Pure Cloud Block Store if it does not exists and
     updates iSCSI settings. Can be used when creating a new cluster or when adding hosts to a cluster.
    .PARAMETER ClusterName
     Cluster name
    .PARAMETER PureCloudBlockStoreConnection
     Pure Cloud Block Store Connection. The connection can be created using Connect-Pf2Array cmdlet
    .PARAMETER PureCloudBlockStoreCredential
     Pure Cloud Block Store Credentials
    .PARAMETER vCenterServer
     vCenter Server. If not provided, default vCenter server will be used
    .PARAMETER AVSCloudName
     AVS cloud name
    .PARAMETER AzureSubscriptionId
     Azure subscription Id. If not provided, default subscription will be used
    .PARAMETER AVSResourceGroup
     AVS ResourceGroup

    .EXAMPLE
    Build-PCBSCluster -ClusterName "mycluster" -PureCloudBlockStoreConnection $CBSConnection `
      -AVSCloudName $AVSCloudName -AzureSubscriptionId $AzureSubscriptionId -AVSResourceGroup $AVSResourceGroup
    #>

    [CmdletBinding()]
    Param (
        [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
        [String]$ClusterName,

        [Parameter(Mandatory=$false)]
        $PureCloudBlockStoreConnection,

        [Parameter(Mandatory=$false)]
        $vCenterServer,

        [Parameter(Mandatory=$false)]
        [String]$AVSCloudName,

        [Parameter(Mandatory=$false)]
        [String]$AzureSubscriptionId,

        [Parameter(Mandatory=$false)]
        [String]$AVSResourceGroup
    )

    Write-Progress -Activity "Building cluster" -Status "0% Complete:" -PercentComplete 0
    Test-vCenterConnection -vCenterServer $vCenterServer

    $cluster = Get-Cluster -Name $ClusterName
    if (-not $cluster) {
        throw "Could not find cluster '$ClusterName'..."
    }

    $fa = Connect-PureCloudBlockStore -PureCloudBlockStoreConnection $PureCloudBlockStoreConnection

    Write-Progress -Activity "Configuring iSCSI" -Status "25% Complete:" -PercentComplete 25
    $updated_hosts = New-PCBSHostGroupfromVcCluster -FlashArray $fa -Cluster $cluster `
       -AVSCloudName $AVSCloudName -AzureSubscriptionId $AzureSubscriptionId -AVSResourceGroup $AVSResourceGroup
    Write-Progress -Activity "Removing unused hosts" -Status "50% Complete:" -PercentComplete 50
    Remove-PCBSUnusedHosts -FlashArray $fa -Cluster $cluster | Out-Null

    Write-Progress -Activity "Refreshing iSCSI targets" -Status "75% Complete:" -PercentComplete 75
    $ethList = (Get-Pfa2NetworkInterface -Array $FlashArray | Where-Object {$_.services -eq "iscsi"} | Where-Object {$_.enabled -eq $true} | Where-Object {$null -ne $_.Eth.address}).Eth
    $ISCSIAddressList = @()
    foreach ($eth in $ethList) {
      $ISCSIAddressList += $eth.address
    }

    $params = @{
        ClusterName = $ClusterName
        # Need to join the list as string as RunCommand does not support array type
        ISCSIAddress = $ISCSIAddressList -join ","
    }

    Invoke-RunScript -CmdletName  "Remove-VMHostStaticiSCSITargets" -Parameters $params `
          -AVSCloudName $AVSCloudName -AzureSubscriptionId $AzureSubscriptionId -AVSResourceGroup $AVSResourceGroup
    Write-Progress -Activity "Operation is done" -Status "100% Complete:" -PercentComplete 100

}

function New-PCBSVmfsDatastore {
    <#
    .SYNOPSIS
      Creates a new VMFS datastore and mounts to a VMware cluster
    .DESCRIPTION
      Creates a new VMFS datastore and mounts to a VMware cluster
    .PARAMETER ClusterName
      Cluster name
    .PARAMETER DatastoreName
      Datastore name
    .PARAMETER Size
      Datastore capacity size in bytes
    .PARAMETER PureCloudBlockStoreConnection
     Pure Cloud Block Store Connection. The connection can be created using Connect-Pf2Array cmdlet
    .PARAMETER vCenterServer
     vCenter Server. If not provided, default vCenter server will be used
    .PARAMETER AVSCloudName
     AVS cloud name
    .PARAMETER AzureSubscriptionId
     Azure subscription Id. If not provided, default subscription will be used
    .PARAMETER AVSResourceGroup
     AVS ResourceGroup

    .EXAMPLE
      New-PCBSVmfsDatastore -ClusterName myClusterName -PureCloudBlockStoreConnection $CBSConnection -DatastoreName MyVMFSStore -Size 4GB `
        -AVSCloudName $AVSCloudName -AzureSubscriptionId $AzureSubscriptionId -AVSResourceGroup $AVSResourceGroup

      Create a datastore "MyVMFS"
    #>

    [CmdletBinding()]
    Param (
        [Parameter(Mandatory=$true)]
        [String]$ClusterName,

        [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
        [String]$DatastoreName,

        [Parameter(Mandatory=$true)]
        [UInt64]$Size,

        [Parameter(Mandatory=$false)]
        $PureCloudBlockStoreConnection,

        [Parameter(Mandatory=$false)]
        $vCenterServer,

        [Parameter(Mandatory=$false)]
        [String]$AVSCloudName,

        [Parameter(Mandatory=$false)]
        [String]$AzureSubscriptionId,

        [Parameter(Mandatory=$false)]
        [String]$AVSResourceGroup
    )
    Write-Progress -Activity "Creating datastore" -Status "0% Complete:" -PercentComplete 0
    Test-vCenterConnection -vCenterServer $vCenterServer

    $Cluster = Get-Cluster -Name $ClusterName -ErrorAction Ignore
    if (-not $Cluster) {
        throw "Cluster $ClusterName does not exist."
    }

    $fa = Connect-PureCloudBlockStore -PureCloudBlockStoreConnection $PureCloudBlockStoreConnection
    New-PfaVmfs -Cluster $Cluster -Flasharray $fa -Name $DatastoreName -Size $Size `
        -AVSCloudName $AVSCloudName -AzureSubscriptionId $AzureSubscriptionId -AVSResourceGroup $AVSResourceGroup
    Write-Progress -Activity "Operation is done" -Status "100% Complete:" -Completed

}

function Restore-PCBSVmfsDatastore {
    <#
    .SYNOPSIS
      Mounts a copy of a VMFS datastore to a VMware cluster from a Pure Cloud Block Store snapshot.
    .DESCRIPTION
      Takes in a snapshot name, the corresponding Pure Cloud Block Store, and a cluster. The VMFS copy will be resignatured and mounted.
    .PARAMETER ClusterName
      Cluster name
    .PARAMETER VolumeSnapshotName
      Volume snapshot name. A volume will be created from the volume snapshot. A datastore will be created from the volume
    .PARAMETER VolumeName
      Volume name. A datastore will be created from the volume
    .PARAMETER ProtectionGroupSnapshotName
      Protection group snapshot name. All of volume snapshots of the protection group snapshot will be used for restoring. The snapshot will be skipped if not supported
    .PARAMETER PodName
      Pod name. All of volumes of the pod will be used for restoring. The volume will be skipped if not supported
    .PARAMETER PureCloudBlockStoreConnection
      Pure Cloud Block Store Connection. The connection can be created using Connect-Pf2Array cmdlet
    .PARAMETER DatastoreName
      Datastore name
    .PARAMETER vCenterServer
     vCenter Server. If not provided, default vCenter server will be used
    .PARAMETER AVSCloudName
     AVS cloud name
    .PARAMETER AzureSubscriptionId
     Azure subscription Id. If not provided, default subscription will be used
    .PARAMETER AVSResourceGroup
     AVS ResourceGroup
    .EXAMPLE
      Restore-PCBSVmfsDatastore -ClusterName myClusterName -VolumeSnapshotName mySnapshotName -PureCloudBlockStoreConnection $CBSConnection `
        -AVSCloudName $AVSCloudName -AzureSubscriptionId $AzureSubscriptionId -AVSResourceGroup $AVSResourceGroup

      Takes in a snapshot name, the corresponding Pure Cloud Block Store, and a cluster. The VMFS copy will be resignatured and mounted.
    .EXAMPLE
      Restore-PCBSVmfsDatastore -ClusterName myClusterName -VolumeName myVolumeName -PureCloudBlockStoreConnection $CBSConnection `
        -AVSCloudName $AVSCloudName -AzureSubscriptionId $AzureSubscriptionId -AVSResourceGroup $AVSResourceGroup

      Takes in a volume name, the corresponding Pure Cloud Block Store, and a cluster. The VMFS copy will be resignatured and mounted.
    #>

    [CmdletBinding()]
    Param (
        [Parameter(Mandatory=$true)]
        [String]$ClusterName,

        [Parameter(Mandatory = $true, ParameterSetName = 'VolumeSnapshot')]
        [String]$VolumeSnapshotName,

        [Parameter(Mandatory = $true, ParameterSetName = 'Volume')]
        [String]$VolumeName,

        [Parameter(Mandatory = $true, ParameterSetName = 'ProtectionGroupSnapshot')]
        [String]$ProtectionGroupSnapshotName,

        [Parameter(Mandatory = $true, ParameterSetName = 'Pod')]
        [String]$PodName,

        [Parameter(Mandatory=$false)]
        $PureCloudBlockStoreConnection,

        [Parameter(Mandatory=$false)]
        $vCenterServer,

        [Parameter(Mandatory=$false)]
        [String]$AVSCloudName,

        [Parameter(Mandatory=$false)]
        [String]$AzureSubscriptionId,

        [Parameter(Mandatory=$false)]
        [String]$AVSResourceGroup
    )
    Write-Progress -Activity "Restoring datastore" -Status "0% Complete:" -PercentComplete 0
    Test-vCenterConnection -vCenterServer $vCenterServer
    $fa = Connect-PureCloudBlockStore -PureCloudBlockStoreConnection $PureCloudBlockStoreConnection

    $Cluster = Get-Cluster -Name $ClusterName -ErrorAction Ignore
    if (-not $Cluster) {
        throw "Cluster '$ClusterName' does not exist."
    }

    switch ($PSCmdlet.ParameterSetName) {
        'Volume' {
            Write-Host "Creating datastore from volume..."
            $NewDatastoreName = Restore-PfaVmfsFromVolume -FlashArray $fa -ClusterName $ClusterName -VolumeName $VolumeName `
                                -AVSCloudName $AVSCloudName -AzureSubscriptionId $AzureSubscriptionId -AVSResourceGroup $AVSResourceGroup
            break
        }
        'VolumeSnapshot' {
            Write-Host "Creating datastore from volume snapshot..."
            $NewDatastoreName = Restore-PfaVmfsFromVolumeSnapshot -FlashArray $fa -ClusterName $ClusterName -VolumeSnapshotName $VolumeSnapshotName `
                                -AVSCloudName $AVSCloudName -AzureSubscriptionId $AzureSubscriptionId -AVSResourceGroup $AVSResourceGroup
            break
        }
        'ProtectionGroupSnapshot' {
            Write-Host "Creating datastore from protection group snapshot..."
            $NewDatastoreName = Restore-PfaVmfsFromProtectionGroupSnapshot -FlashArray $fa -ClusterName $ClusterName -ProtectionGroupSnapshotName $ProtectionGroupSnapshotName `
                                -AVSCloudName $AVSCloudName -AzureSubscriptionId $AzureSubscriptionId -AVSResourceGroup $AVSResourceGroup
            break
        }
        'Pod' {
            Write-Host "Creating datastore from pod..."
            $NewDatastoreName = Restore-PfaVmfsFromPod -FlashArray $fa -ClusterName $ClusterName -PodName $PodName `
                                -AVSCloudName $AVSCloudName -AzureSubscriptionId $AzureSubscriptionId -AVSResourceGroup $AVSResourceGroup
            break
        }
    }
    Write-Progress -Activity "Operation is done" -Status "100% Complete:" -Completed

    return $NewDatastoreName
}


function Remove-PCBSVmfsDatastore {
  <#
    .SYNOPSIS
     Detaches and unmount a VMFS datastore from a cluster. Remove the datastore from the host group
    .DESCRIPTION
     Detaches and unmount a VMFS datastore from a cluster. Remove the datastore from the host group. The connection configured for the volume and cluster host/host group will be removed from Pure Cloud Block Store. The volume will be destroyed if there is no other connection left.
    .PARAMETER ClusterName
     Cluster name
    .PARAMETER DatastoreName
     Datastore name
    .PARAMETER PureCloudBlockStoreConnection
     Pure Cloud Block Store Connection. The connection can be created using Connect-Pf2Array cmdlet
    .PARAMETER vCenterServer
     vCenter Server. If not provided, default vCenter server will be used
    .PARAMETER AVSCloudName
     AVS cloud name
    .PARAMETER AzureSubscriptionId
     Azure subscription Id. If not provided, default subscription will be used
    .PARAMETER AVSResourceGroup
     AVS ResourceGroup
    .EXAMPLE
     Remove-PCBSVmfsDatastore -ClusterName "mycluster" -DatastoreName "myDatastore" PureCloudBlockStoreConnection $CBSConnection `
        -AVSCloudName $AVSCloudName -AzureSubscriptionId $AzureSubscriptionId -AVSResourceGroup $AVSResourceGroup

     Takes in a datastore name, datastore would be removed.
    #>

    [CmdletBinding()]
    Param (
        [Parameter(Mandatory=$true)]
        [String]$ClusterName,

        [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
        [String]$DatastoreName,

        [Parameter(Mandatory=$false)]
        $PureCloudBlockStoreConnection,

        [Parameter(Mandatory=$false)]
        $vCenterServer,

        [Parameter(Mandatory=$false)]
        [String]$AVSCloudName,

        [Parameter(Mandatory=$false)]
        [String]$AzureSubscriptionId,

        [Parameter(Mandatory=$false)]
        [String]$AVSResourceGroup
    )
    Write-Progress -Activity "Removing datastore" -Status "0% Complete:" -PercentComplete 0

    Test-vCenterConnection -vCenterServer $vCenterServer

    $FlashArray = Connect-PureCloudBlockStore -PureCloudBlockStoreConnection $PureCloudBlockStoreConnection
    Remove-PfaVmfsDatastore -ClusterName $ClusterName -DatastoreName $DataStoreName -FlashArray $FlashArray `
        -AVSCloudName $AVSCloudName -AzureSubscriptionId $AzureSubscriptionId -AVSResourceGroup $AVSResourceGroup
    Write-Progress -Activity "Operation is done" -Status "100% Complete:" -Completed

}


function Set-PCBSVmfsCapacity {
  <#
    .SYNOPSIS
     Increase the size of a Pure Cloud Block Store-based VMFS datastore.
    .DESCRIPTION
     Takes in a datastore, the corresponding Pure Cloud Block Store, and a new size. Both the volume and the VMFS will be grown.
    .PARAMETER ClusterName
     Cluster name
    .PARAMETER DatastoreName
     Datastore name
    .PARAMETER Size
     New datastore capacity size in bytes
    .PARAMETER PureCloudBlockStoreConnection
     Pure Cloud Block Store Connection. The connection can be created using Connect-Pf2Array cmdlet
    .PARAMETER vCenterServer
     vCenter Server. If not provided, default vCenter server will be used
    .PARAMETER AVSCloudName
     AVS cloud name
    .PARAMETER AzureSubscriptionId
     Azure subscription Id. If not provided, default subscription will be used
    .PARAMETER AVSResourceGroup
     AVS ResourceGroup

    .EXAMPLE
     Set-PCBSVmfsCapacity -ClusterName "mycluster" -DatastoreName myDatastore -Size 3GB -PureCloudBlockStoreConnection $CBSConnection `
        -AVSCloudName $AVSCloudName -AzureSubscriptionId $AzureSubscriptionId -AVSResourceGroup $AVSResourceGroup

     Expand size of the datastore myDatastore to 3GB.
    #>

    [CmdletBinding()]
    Param (
          [Parameter(Mandatory=$true)]
          [String]$ClusterName,

          [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
          [string]$DatastoreName,

          [Parameter(Mandatory=$true)]
          [UInt64]$Size,

          [Parameter(Mandatory=$false)]
          $PureCloudBlockStoreConnection,

          [Parameter(Mandatory=$false)]
          $vCenterServer,

          [Parameter(Mandatory=$false)]
          [String]$AVSCloudName,

          [Parameter(Mandatory=$false)]
          [String]$AzureSubscriptionId,

          [Parameter(Mandatory=$false)]
          [String]$AVSResourceGroup
    )
    Write-Progress -Activity "Resizing datastore" -Status "0% Complete:" -PercentComplete 0
    Test-vCenterConnection -vCenterServer $vCenterServer
    $FlashArray = Connect-PureCloudBlockStore -PureCloudBlockStoreConnection $PureCloudBlockStoreConnection
    $Cluster = Get-Cluster -Name $ClusterName -ErrorAction Ignore
    if (-not $Cluster) {
        throw "Cluster $ClusterName does not exist."
    }
    Set-PfaVmfsCapacity -ClusterName $ClusterName -FlashArray $FlashArray -DatastoreName $DatastoreName -SizeInByte $Size `
        -AVSCloudName $AVSCloudName -AzureSubscriptionId $AzureSubscriptionId -AVSResourceGroup $AVSResourceGroup
    Write-Progress -Activity "Operation is done" -Status "100% Complete:" -Completed
}

Export-ModuleMember -Function Build-PCBSCluster
Export-ModuleMember -Function New-PCBSVmfsDatastore
Export-ModuleMember -Function Restore-PCBSVmfsDatastore
Export-ModuleMember -Function Remove-PCBSVmfsDatastore
Export-ModuleMember -Function Set-PCBSVmfsCapacity
# SIG # Begin signature block
# MIIjUAYJKoZIhvcNAQcCoIIjQTCCIz0CAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUfAHj9XLL1IKCOSEQ1WXw8mCS
# mmaggh12MIIFMDCCBBigAwIBAgIQBAkYG1/Vu2Z1U0O1b5VQCDANBgkqhkiG9w0B
# AQsFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYD
# VQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVk
# IElEIFJvb3QgQ0EwHhcNMTMxMDIyMTIwMDAwWhcNMjgxMDIyMTIwMDAwWjByMQsw
# CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu
# ZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFzc3VyZWQgSUQg
# Q29kZSBTaWduaW5nIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
# +NOzHH8OEa9ndwfTCzFJGc/Q+0WZsTrbRPV/5aid2zLXcep2nQUut4/6kkPApfmJ
# 1DcZ17aq8JyGpdglrA55KDp+6dFn08b7KSfH03sjlOSRI5aQd4L5oYQjZhJUM1B0
# sSgmuyRpwsJS8hRniolF1C2ho+mILCCVrhxKhwjfDPXiTWAYvqrEsq5wMWYzcT6s
# cKKrzn/pfMuSoeU7MRzP6vIK5Fe7SrXpdOYr/mzLfnQ5Ng2Q7+S1TqSp6moKq4Tz
# rGdOtcT3jNEgJSPrCGQ+UpbB8g8S9MWOD8Gi6CxR93O8vYWxYoNzQYIH5DiLanMg
# 0A9kczyen6Yzqf0Z3yWT0QIDAQABo4IBzTCCAckwEgYDVR0TAQH/BAgwBgEB/wIB
# ADAOBgNVHQ8BAf8EBAMCAYYwEwYDVR0lBAwwCgYIKwYBBQUHAwMweQYIKwYBBQUH
# AQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYI
# KwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFz
# c3VyZWRJRFJvb3RDQS5jcnQwgYEGA1UdHwR6MHgwOqA4oDaGNGh0dHA6Ly9jcmw0
# LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwOqA4oDaG
# NGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RD
# QS5jcmwwTwYDVR0gBEgwRjA4BgpghkgBhv1sAAIEMCowKAYIKwYBBQUHAgEWHGh0
# dHBzOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwCgYIYIZIAYb9bAMwHQYDVR0OBBYE
# FFrEuXsqCqOl6nEDwGD5LfZldQ5YMB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6en
# IZ3zbcgPMA0GCSqGSIb3DQEBCwUAA4IBAQA+7A1aJLPzItEVyCx8JSl2qB1dHC06
# GsTvMGHXfgtg/cM9D8Svi/3vKt8gVTew4fbRknUPUbRupY5a4l4kgU4QpO4/cY5j
# DhNLrddfRHnzNhQGivecRk5c/5CxGwcOkRX7uq+1UcKNJK4kxscnKqEpKBo6cSgC
# PC6Ro8AlEeKcFEehemhor5unXCBc2XGxDI+7qPjFEmifz0DLQESlE/DmZAwlCEIy
# sjaKJAL+L3J+HNdJRZboWR3p+nRka7LrZkPas7CM1ekN3fYBIM6ZMWM9CBoYs4Gb
# T8aTEAb8B4H6i9r5gkn3Ym6hU/oSlBiFLpKR6mhsRDKyZqHnGKSaZFHvMIIFNzCC
# BB+gAwIBAgIQC4jZOitkx57ksuMgsWXX0jANBgkqhkiG9w0BAQsFADByMQswCQYD
# VQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGln
# aWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFzc3VyZWQgSUQgQ29k
# ZSBTaWduaW5nIENBMB4XDTIwMDczMDAwMDAwMFoXDTIzMTAwNDEyMDAwMFowdDEL
# MAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDU1vdW50
# YWluIFZpZXcxGzAZBgNVBAoTElB1cmUgU3RvcmFnZSwgSW5jLjEbMBkGA1UEAxMS
# UHVyZSBTdG9yYWdlLCBJbmMuMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
# AQEA6nefE6+A0nNMY82xtkQb+akwI0oxLqEbRY65bE4re+CVQV2xP89/7FIAXooq
# jxNOvrtWicWTGOZjBdAFEXXAUEyu9CkWFOXGLV3/QkcEfY3e3Z3jypa6h1EznSEp
# 3wSQEIVbigi6jR2s7NDnDoSDAKnzcGcSZ8Nz7akXFrN8PAmg3gy8a/rwm2Ko7ClR
# ZUHj1C/OMPXUqiN0Q4FyAsaeFmvg2PX2twxo192WRdNro1dkKNfmDvym2ss6MXcq
# gFEjBcZtHDv3e/i0BjT24Jm1C27IVYZzVf28dqmVfPo7l8bLEHsVKgrRf0VzS2wI
# R08gXiMrohf9tv3AbvtPk7ZaawIDAQABo4IBxTCCAcEwHwYDVR0jBBgwFoAUWsS5
# eyoKo6XqcQPAYPkt9mV1DlgwHQYDVR0OBBYEFJOwFn7S4Lj6QdNnH6tpm3FMFWJI
# MA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzB3BgNVHR8EcDBu
# MDWgM6Axhi9odHRwOi8vY3JsMy5kaWdpY2VydC5jb20vc2hhMi1hc3N1cmVkLWNz
# LWcxLmNybDA1oDOgMYYvaHR0cDovL2NybDQuZGlnaWNlcnQuY29tL3NoYTItYXNz
# dXJlZC1jcy1nMS5jcmwwTAYDVR0gBEUwQzA3BglghkgBhv1sAwEwKjAoBggrBgEF
# BQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAIBgZngQwBBAEwgYQG
# CCsGAQUFBwEBBHgwdjAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQu
# Y29tME4GCCsGAQUFBzAChkJodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGln
# aUNlcnRTSEEyQXNzdXJlZElEQ29kZVNpZ25pbmdDQS5jcnQwDAYDVR0TAQH/BAIw
# ADANBgkqhkiG9w0BAQsFAAOCAQEAMVAa7mXxhBHf0dTzf6LKDncN8KnzB59KBvd0
# KvXZc6FoLHGi2Wg6XBSP+9mdDMMYOkohqstSk7RD+reT8xiptrIkSMcVcTog1Z3e
# JjYTK8B7QsSpuu2lo0RWA5rdvqMJ+lVzbbjteTq+uicP4T/EDwv2q+iPAgpXQD8y
# r084ExDWJtMfhvy0cxh555xx88rvFWOhJnXYiFtjaO9dp7f2TnZRJ44rmB98jc9E
# BR/8GLOi/BhyPiiU4nBv8JIHVP1E5zIt8/9PhfpenmiWBbuuP0YLnzrqRhswtJaq
# jJirYNLYojmINrbvdcpEKGK1AitsnuOjFadLI7bc696Y35lUATCCBY0wggR1oAMC
# AQICEA6bGI750C3n79tQ4ghAGFowDQYJKoZIhvcNAQEMBQAwZTELMAkGA1UEBhMC
# VVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0
# LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNzdXJlZCBJRCBSb290IENBMB4XDTIy
# MDgwMTAwMDAwMFoXDTMxMTEwOTIzNTk1OVowYjELMAkGA1UEBhMCVVMxFTATBgNV
# BAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8G
# A1UEAxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0MIICIjANBgkqhkiG9w0BAQEF
# AAOCAg8AMIICCgKCAgEAv+aQc2jeu+RdSjwwIjBpM+zCpyUuySE98orYWcLhKac9
# WKt2ms2uexuEDcQwH/MbpDgW61bGl20dq7J58soR0uRf1gU8Ug9SH8aeFaV+vp+p
# VxZZVXKvaJNwwrK6dZlqczKU0RBEEC7fgvMHhOZ0O21x4i0MG+4g1ckgHWMpLc7s
# Xk7Ik/ghYZs06wXGXuxbGrzryc/NrDRAX7F6Zu53yEioZldXn1RYjgwrt0+nMNlW
# 7sp7XeOtyU9e5TXnMcvak17cjo+A2raRmECQecN4x7axxLVqGDgDEI3Y1DekLgV9
# iPWCPhCRcKtVgkEy19sEcypukQF8IUzUvK4bA3VdeGbZOjFEmjNAvwjXWkmkwuap
# oGfdpCe8oU85tRFYF/ckXEaPZPfBaYh2mHY9WV1CdoeJl2l6SPDgohIbZpp0yt5L
# HucOY67m1O+SkjqePdwA5EUlibaaRBkrfsCUtNJhbesz2cXfSwQAzH0clcOP9yGy
# shG3u3/y1YxwLEFgqrFjGESVGnZifvaAsPvoZKYz0YkH4b235kOkGLimdwHhD5QM
# IR2yVCkliWzlDlJRR3S+Jqy2QXXeeqxfjT/JvNNBERJb5RBQ6zHFynIWIgnffEx1
# P2PsIV/EIFFrb7GrhotPwtZFX50g/KEexcCPorF+CiaZ9eRpL5gdLfXZqbId5RsC
# AwEAAaOCATowggE2MA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFOzX44LScV1k
# TN8uZz/nupiuHA9PMB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6enIZ3zbcgPMA4G
# A1UdDwEB/wQEAwIBhjB5BggrBgEFBQcBAQRtMGswJAYIKwYBBQUHMAGGGGh0dHA6
# Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3aHR0cDovL2NhY2VydHMu
# ZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNydDBFBgNVHR8E
# PjA8MDqgOKA2hjRodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1
# cmVkSURSb290Q0EuY3JsMBEGA1UdIAQKMAgwBgYEVR0gADANBgkqhkiG9w0BAQwF
# AAOCAQEAcKC/Q1xV5zhfoKN0Gz22Ftf3v1cHvZqsoYcs7IVeqRq7IviHGmlUIu2k
# iHdtvRoU9BNKei8ttzjv9P+Aufih9/Jy3iS8UgPITtAq3votVs/59PesMHqai7Je
# 1M/RQ0SbQyHrlnKhSLSZy51PpwYDE3cnRNTnf+hZqPC/Lwum6fI0POz3A8eHqNJM
# QBk1RmppVLC4oVaO7KTVPeix3P0c2PR3WlxUjG/voVA9/HYJaISfb8rbII01YBwC
# A8sgsKxYoA5AY8WYIsGyWfVVa88nq2x2zm8jLfR+cWojayL/ErhULSd+2DrZ8LaH
# lv1b0VysGMNNn3O3AamfV6peKOK5lDCCBq4wggSWoAMCAQICEAc2N7ckVHzYR6z9
# KGYqXlswDQYJKoZIhvcNAQELBQAwYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERp
# Z2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMY
# RGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0MB4XDTIyMDMyMzAwMDAwMFoXDTM3MDMy
# MjIzNTk1OVowYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMu
# MTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRp
# bWVTdGFtcGluZyBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMaG
# NQZJs8E9cklRVcclA8TykTepl1Gh1tKD0Z5Mom2gsMyD+Vr2EaFEFUJfpIjzaPp9
# 85yJC3+dH54PMx9QEwsmc5Zt+FeoAn39Q7SE2hHxc7Gz7iuAhIoiGN/r2j3EF3+r
# GSs+QtxnjupRPfDWVtTnKC3r07G1decfBmWNlCnT2exp39mQh0YAe9tEQYncfGpX
# evA3eZ9drMvohGS0UvJ2R/dhgxndX7RUCyFobjchu0CsX7LeSn3O9TkSZ+8OpWNs
# 5KbFHc02DVzV5huowWR0QKfAcsW6Th+xtVhNef7Xj3OTrCw54qVI1vCwMROpVymW
# Jy71h6aPTnYVVSZwmCZ/oBpHIEPjQ2OAe3VuJyWQmDo4EbP29p7mO1vsgd4iFNmC
# KseSv6De4z6ic/rnH1pslPJSlRErWHRAKKtzQ87fSqEcazjFKfPKqpZzQmiftkaz
# nTqj1QPgv/CiPMpC3BhIfxQ0z9JMq++bPf4OuGQq+nUoJEHtQr8FnGZJUlD0UfM2
# SU2LINIsVzV5K6jzRWC8I41Y99xh3pP+OcD5sjClTNfpmEpYPtMDiP6zj9NeS3YS
# UZPJjAw7W4oiqMEmCPkUEBIDfV8ju2TjY+Cm4T72wnSyPx4JduyrXUZ14mCjWAkB
# KAAOhFTuzuldyF4wEr1GnrXTdrnSDmuZDNIztM2xAgMBAAGjggFdMIIBWTASBgNV
# HRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBS6FtltTYUvcyl2mi91jGogj57IbzAf
# BgNVHSMEGDAWgBTs1+OC0nFdZEzfLmc/57qYrhwPTzAOBgNVHQ8BAf8EBAMCAYYw
# EwYDVR0lBAwwCgYIKwYBBQUHAwgwdwYIKwYBBQUHAQEEazBpMCQGCCsGAQUFBzAB
# hhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQQYIKwYBBQUHMAKGNWh0dHA6Ly9j
# YWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRSb290RzQuY3J0MEMG
# A1UdHwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2Vy
# dFRydXN0ZWRSb290RzQuY3JsMCAGA1UdIAQZMBcwCAYGZ4EMAQQCMAsGCWCGSAGG
# /WwHATANBgkqhkiG9w0BAQsFAAOCAgEAfVmOwJO2b5ipRCIBfmbW2CFC4bAYLhBN
# E88wU86/GPvHUF3iSyn7cIoNqilp/GnBzx0H6T5gyNgL5Vxb122H+oQgJTQxZ822
# EpZvxFBMYh0MCIKoFr2pVs8Vc40BIiXOlWk/R3f7cnQU1/+rT4osequFzUNf7WC2
# qk+RZp4snuCKrOX9jLxkJodskr2dfNBwCnzvqLx1T7pa96kQsl3p/yhUifDVinF2
# ZdrM8HKjI/rAJ4JErpknG6skHibBt94q6/aesXmZgaNWhqsKRcnfxI2g55j7+6ad
# cq/Ex8HBanHZxhOACcS2n82HhyS7T6NJuXdmkfFynOlLAlKnN36TU6w7HQhJD5TN
# OXrd/yVjmScsPT9rp/Fmw0HNT7ZAmyEhQNC3EyTN3B14OuSereU0cZLXJmvkOHOr
# pgFPvT87eK1MrfvElXvtCl8zOYdBeHo46Zzh3SP9HSjTx/no8Zhf+yvYfvJGnXUs
# HicsJttvFXseGYs2uJPU5vIXmVnKcPA3v5gA3yAWTyf7YGcWoWa63VXAOimGsJig
# K+2VQbc61RWYMbRiCQ8KvYHZE/6/pNHzV9m8BPqC3jLfBInwAM1dwvnQI38AC+R2
# AibZ8GV2QqYphwlHK+Z/GqSFD/yYlvZVVCsfgPrA8g4r5db7qS9EFUrnEw4d2zc4
# GqEr9u3WfPwwggbAMIIEqKADAgECAhAMTWlyS5T6PCpKPSkHgD1aMA0GCSqGSIb3
# DQEBCwUAMGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7
# MDkGA1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBUaW1l
# U3RhbXBpbmcgQ0EwHhcNMjIwOTIxMDAwMDAwWhcNMzMxMTIxMjM1OTU5WjBGMQsw
# CQYDVQQGEwJVUzERMA8GA1UEChMIRGlnaUNlcnQxJDAiBgNVBAMTG0RpZ2lDZXJ0
# IFRpbWVzdGFtcCAyMDIyIC0gMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
# ggIBAM/spSY6xqnya7uNwQ2a26HoFIV0MxomrNAcVR4eNm28klUMYfSdCXc9FZYI
# L2tkpP0GgxbXkZI4HDEClvtysZc6Va8z7GGK6aYo25BjXL2JU+A6LYyHQq4mpOS7
# eHi5ehbhVsbAumRTuyoW51BIu4hpDIjG8b7gL307scpTjUCDHufLckkoHkyAHoVW
# 54Xt8mG8qjoHffarbuVm3eJc9S/tjdRNlYRo44DLannR0hCRRinrPibytIzNTLlm
# yLuqUDgN5YyUXRlav/V7QG5vFqianJVHhoV5PgxeZowaCiS+nKrSnLb3T254xCg/
# oxwPUAY3ugjZNaa1Htp4WB056PhMkRCWfk3h3cKtpX74LRsf7CtGGKMZ9jn39cFP
# cS6JAxGiS7uYv/pP5Hs27wZE5FX/NurlfDHn88JSxOYWe1p+pSVz28BqmSEtY+VZ
# 9U0vkB8nt9KrFOU4ZodRCGv7U0M50GT6Vs/g9ArmFG1keLuY/ZTDcyHzL8IuINeB
# rNPxB9ThvdldS24xlCmL5kGkZZTAWOXlLimQprdhZPrZIGwYUWC6poEPCSVT8b87
# 6asHDmoHOWIZydaFfxPZjXnPYsXs4Xu5zGcTB5rBeO3GiMiwbjJ5xwtZg43G7vUs
# fHuOy2SJ8bHEuOdTXl9V0n0ZKVkDTvpd6kVzHIR+187i1Dp3AgMBAAGjggGLMIIB
# hzAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggr
# BgEFBQcDCDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEwHwYDVR0j
# BBgwFoAUuhbZbU2FL3MpdpovdYxqII+eyG8wHQYDVR0OBBYEFGKK3tBh/I8xFO2X
# C809KpQU31KcMFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0
# LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1lU3RhbXBpbmdD
# QS5jcmwwgZAGCCsGAQUFBwEBBIGDMIGAMCQGCCsGAQUFBzABhhhodHRwOi8vb2Nz
# cC5kaWdpY2VydC5jb20wWAYIKwYBBQUHMAKGTGh0dHA6Ly9jYWNlcnRzLmRpZ2lj
# ZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1lU3RhbXBp
# bmdDQS5jcnQwDQYJKoZIhvcNAQELBQADggIBAFWqKhrzRvN4Vzcw/HXjT9aFI/H8
# +ZU5myXm93KKmMN31GT8Ffs2wklRLHiIY1UJRjkA/GnUypsp+6M/wMkAmxMdsJiJ
# 3HjyzXyFzVOdr2LiYWajFCpFh0qYQitQ/Bu1nggwCfrkLdcJiXn5CeaIzn0buGqi
# m8FTYAnoo7id160fHLjsmEHw9g6A++T/350Qp+sAul9Kjxo6UrTqvwlJFTU2WZoP
# VNKyG39+XgmtdlSKdG3K0gVnK3br/5iyJpU4GYhEFOUKWaJr5yI+RCHSPxzAm+18
# SLLYkgyRTzxmlK9dAlPrnuKe5NMfhgFknADC6Vp0dQ094XmIvxwBl8kZI4DXNlpf
# lhaxYwzGRkA7zl011Fk+Q5oYrsPJy8P7mxNfarXH4PMFw1nfJ2Ir3kHJU7n/NBBn
# 9iYymHv+XEKUgZSCnawKi8ZLFUrTmJBFYDOA4CPe+AOk9kVH5c64A0JH6EE2cXet
# /aLol3ROLtoeHYxayB6a1cLwxiKoT5u92ByaUcQvmvZfpyeXupYuhVfAYOd4Vn9q
# 78KVmksRAsiCnMkaBXy6cbVOepls9Oie1FqYyJ+/jbsYXEP10Cro4mLueATbvdH7
# WwqocH7wl4R44wgDXUcsY6glOJcB0j862uXl9uab3H4szP8XTE0AotjWAQ64i+7m
# 4HJViSwnGWH2dwGMMYIFRDCCBUACAQEwgYYwcjELMAkGA1UEBhMCVVMxFTATBgNV
# BAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTExMC8G
# A1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVkIElEIENvZGUgU2lnbmluZyBDQQIQ
# C4jZOitkx57ksuMgsWXX0jAJBgUrDgMCGgUAoHAwEAYKKwYBBAGCNwIBDDECMAAw
# GQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisG
# AQQBgjcCARUwIwYJKoZIhvcNAQkEMRYEFK6R2YK4dvHY14TG4kEp7BaNg6DfMA0G
# CSqGSIb3DQEBAQUABIIBAOTjBuhi2tHn4tRcyu9moa0o63ifiWBygZHF/GRiZPWz
# 30MMsN+pNDJlMjW9XCbqj6p4mArrK8zieBdD5aLLjux8+zuVRLxi/D/2m/wOh1pR
# 1IdTmsXVXjCTpgGgG1YDbqGxcZ4gixKn9PHiugO7C6Rn4dcBteBA91HrnILwsndM
# V9ELyAQBwbSd1mwImwX5ISPmw5IU8eHGmgYQ9gn3ly/4jrAc3xuEPsVWnTtZqnnO
# yqhqw0e4viJgSoAef+jQsXOvQuad7REodbRKiCIGVUZTYbPiVHN2LfA5uAp4VzA6
# 2+AshAUe6EEQb3bjXJ39f201ff4KeW8YqZaANaE1CQmhggMgMIIDHAYJKoZIhvcN
# AQkGMYIDDTCCAwkCAQEwdzBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNl
# cnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBT
# SEEyNTYgVGltZVN0YW1waW5nIENBAhAMTWlyS5T6PCpKPSkHgD1aMA0GCWCGSAFl
# AwQCAQUAoGkwGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUx
# DxcNMjMwNTE3MDUwODI4WjAvBgkqhkiG9w0BCQQxIgQgQ8of4a3UoM6RAb/AOiqh
# V1wrsoKqSV8DGnZ31pboBn4wDQYJKoZIhvcNAQEBBQAEggIAgPyLVkrRFIofWjMV
# qPmVd7IkBR4usxoCyruutjmnlFqtWjt41hEky4h6dmCFeSLwqvCODnAUBQ5U3ZUW
# qIT2uaDIvgwGaPAtxzt+/3Bur31WXkUix8G/dk1DKxJjSS5WD1ESWSh+3mkf2GsL
# O/soYW51J+NC201vZd0x3crvmao9qUacw1h9YKCkNP0f+uTLUtcNZPVFgA62A/+f
# e4SfE4/skEWrKElyUepWAn8d440bx4VB8G1JGpnlPRml4U+YwXz9AmiKToej27AB
# guDe0ucNGVseywQpKBQ9cSGdnxkWRwDsAHHp0sHO5r4R2tJFHuAgqogsQCZasbRb
# iL/DjDp9facoqv1M7X2r4eTsw/1Tm5Wnj7AJJ59jhGR/CNlZP5I/6nH5TOwScW2b
# 0BsEtLhD0RqaGOJWV0iP0LqOhIX4T0Nr2epW1bGdH1XA/IX/JcxrF1NwMdMnox9d
# gXpFTVC0iERyphLIrUufnUKpzgA/BpnUdTuULB1+TEWbBpSQa/+o/pUlFhiqk8h4
# tDZTbWeYTEO+CtcoB2GxV6X/1/YdCke5wTnaTPw4qzIM8H6Ha7hOu390e1LEXNNJ
# wcHkzI3cJigPRuQAxJGNMa+3la0McVL5TQSK1FOEeJdm99tyKK/ey3jclg9SiVbG
# 8ezWq44cjZX2SHGvu6nCsTpm72I=
# SIG # End signature block