PureStorage.CommonUtil.ps1

<#
Common Utility functions.
#>


$DEFAULT_PER_HOST_TIMEOUT_IN_SECONDS = 30
$MIN_TIMEOUT_IN_MINUTES = 10
$MAX_TIMEOUT_IN_MINUTES = 60
$SUCCEEDED_PROVISIONING_STATUS = "Succeeded"

function Get-IScsiAdapter {
    <#
    .SYNOPSIS
      Gets the iSCSI Software Adapter from an ESXi host
    .DESCRIPTION
      Retrieves the iSCSI Software Adapter from the specified ESXi host
      Returns the adapter object if found, throws exception if not found
    #>


    Param(
        [Parameter(Mandatory = $true, ValueFromPipeline = $True)]
        [VMware.VimAutomation.ViCore.Types.V1.Inventory.VMHost]$Esxi
    )

    $iscsiadapter = $esxi | Get-VMHostHBA -Type iscsi | Where-Object { $_.Model -eq "iSCSI Software Adapter" }

    if ($null -eq $iscsiadapter) {
        throw "No Software iSCSI adapter found on host $($Esxi.NetworkInfo.HostName)."
    }

    return $iscsiadapter
}


function Set-HostHBAISCSITargets {
    <#
    .SYNOPSIS
      Ensures iSCSI targets from the FlashArray are configured on the ESXi host
    .DESCRIPTION
      Checks if the ESXi host has the correct iSCSI targets configured for the FlashArray.
      If targets are missing, reconfigures the ESXi host iSCSI settings. Returns $true if updated and $false otherwise
    #>


    Param(
        [Parameter(Mandatory = $true, ValueFromPipeline = $True)]
        [VMware.VimAutomation.ViCore.Types.V1.Inventory.VMHost]$Esxi,

        [Parameter(Mandatory = $true)]
        [ValidateNotNull()]
        $FlashArray,

        [Parameter(Mandatory = $true)]
        [VMware.VimAutomation.ViCore.Types.V1.Inventory.Cluster]$Cluster,

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

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

        [Parameter(Mandatory = $false)]
        [int]$TimeoutInMinutes = 10,

        [Parameter(Mandatory = $true)]
        $Logger
    )

    # Get iSCSI adapter on ESXi host
    $iscsiadapter = Get-IScsiAdapter -Esxi $esxi
    $faiSCSItargets = Get-Pfa2NetworkInterface -Array $FlashArray | Where-Object { $_.services -eq "iscsi" -and $_.enabled -and $_.Eth.address }
    $ipEndpoints = @($faiSCSItargets | ForEach-Object { "$($_.Eth.address):3260" })

    # Check if iSCSI targets are configured
    $iscsiTargets = $iscsiadapter | Get-IScsiHbaTarget -Type Send | Where-Object { $ipEndpoints -contains "$($_.Address):$($_.Port)" }
    if ($iscsiTargets.Count -eq $faiSCSItargets.Count) {
        $Logger.LogDebug("iSCSI targets are already configured for host $($esxi.Name)")
        return $false
    }

    $Logger.LogInfo("iSCSI targets are not fully configured for host $($esxi.Name), reconfiguring...")
    Set-VMHostiSCSI -Cluster $Cluster -esxi $esxi -flasharray $FlashArray -TimeoutInMinutes $TimeoutInMinutes `
        -AVSCloudName $AVSCloudName -AVSResourceGroup $AVSResourceGroup -ErrorAction Stop -Logger $Logger | Out-Null
    return $true
}


function Get-PfaHostFromVmHost {
    <#
    .SYNOPSIS
      Gets a FlashArray host object from a ESXi vmhost object
    .DESCRIPTION
      Takes in a vmhost and returns a matching FA host if found
    #>


    Param(
        [Parameter(Mandatory=$true,ValueFromPipeline=$True)]
        [VMware.VimAutomation.ViCore.Types.V1.Inventory.VMHost]$Esxi,

        [Parameter(Mandatory=$true,ValueFromPipeline=$True)]
        $Flasharray
    )
    $iscsiadapter = $esxi | Get-VMHostHBA -Type iscsi | Where-Object {$_.Model -eq "iSCSI Software Adapter"}
    $fahosts = Get-Pfa2Host -Array $FlashArray | Where-Object {$_.IsLocal -eq $True}
    $ArrayName = Get-ArrayName -FlashArray $FlashArray
    if ($null -ne $iscsiadapter)
    {
        $iqn = $iscsiadapter.ExtensionData.IScsiName
        foreach ($fahost in $fahosts)
        {
            if ($fahost.iqns.count -ge 1)
            {
                foreach ($fahostiqn in $fahost.iqns)
                {
                    if ($iqn.ToLower() -eq $fahostiqn.ToLower())
                    {
                        $faHostMatch = $fahost
                        break
                    }
                }
            }
        }
    }
    if ($null -ne $faHostMatch)
    {
      return $faHostMatch
    }
    else
    {
        throw "No matching host for $($esxi.Name) could be found on the Pure Cloud Block Store $ArrayName"
    }
}

function Get-PfaHostGroupfromVcCluster {
    <#
    .SYNOPSIS
      Retrieves a FA host group from an ESXi cluster
    .DESCRIPTION
      Takes in a vCenter Cluster and retrieves corresonding host group
    #>


    Param(
        [Parameter(Mandatory=$true,ValueFromPipeline=$True)]
        [VMware.VimAutomation.ViCore.Types.V1.Inventory.Cluster]$Cluster,

        [Parameter(Mandatory=$True)]
        $Flasharray,

        [Parameter(Mandatory=$true)]
        $Logger
    )

    $esxiHosts = $cluster |Get-VMHost
    $faHostGroups = @()
    $faHostGroupNames = @()
    $ArrayName = Get-ArrayName -FlashArray $FlashArray
    foreach ($esxiHost in $esxiHosts)
    {
        try {
            $faHost = $esxiHost | Get-PfaHostFromVmHost -flasharray $flasharray
            if ($null -ne $faHost.HostGroup.Name)
            {
                if ($faHostGroupNames.contains($faHost.HostGroup.Name))
                {
                    continue
                }
                else {
                    $faHostGroupNames += $faHost.HostGroup.Name
                    $faHostGroup = Get-Pfa2HostGroup -Array $Flasharray -Name $($faHost.HostGroup.Name)  -ErrorAction stop | Where-Object {$_.IsLocal -eq $True}
                    $faHostGroups += $faHostGroup
                }
            }
        }
        catch{
            continue
        }
    }
    if ($null -eq $faHostGroup)
    {
        throw "No host group found for cluster $($Cluster.Name) on $ArrayName."
    }
    if ($faHostGroups.count -gt 1)
    {
        $Logger.LogWarning("Cluster $($Cluster.Name) spans more than one host group. The recommendation is to have only one host group per cluster")
    }
    return $faHostGroups
}


function Get-ArrayName {
    Param(
        [Parameter(Mandatory=$true)]
        $FlashArray
    )
    $ArrayName = (Get-Pfa2Array -Array $Flasharray).Name
    return $ArrayName
}

function Get-AzureAuthHeader {
    param (
        $AzContext
    )
    $AzProfile = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile
    $ProfileClient = New-Object -TypeName Microsoft.Azure.Commands.ResourceManager.Common.RMProfileClient -ArgumentList ($AzProfile)
    $Token = $profileClient.AcquireAccessToken($azContext.Subscription.TenantId)
    if (-not $Token) {
        throw "Failed to acquire Azure access token for tenant $($azContext.Subscription.TenantId)"
    }
    $AuthHeader = @{
        'Content-Type'='application/json'
        'Authorization'='Bearer ' + $token.AccessToken
    }

    return $AuthHeader
}

function Get-AVSvCenterEndpoint {
    param (
        [Hashtable] $AuthHeader,
        [String] $SubscriptionId,
        [String] $AvsResourceGroupName,
        [String] $AvsPrivateCloudName
    )
    $RestUri = "https://management.azure.com/subscriptions/$($subscriptionId)/resourceGroups/$($avsResourceGroupName)/providers/Microsoft.AVS/privateClouds/$($avsPrivateCloudName)?api-version=2022-05-01"
    $Response = Invoke-RestMethod -Uri $restUri -Method Get -Headers $authHeader
    if (-not $Response) {
        throw "Failed to get AVS vCenter endpoint. Please make sure you have the right permission to access the AVS instance with subscriptionId $subscriptionId, resource group $avsResourceGroupName and private cloud $avsPrivateCloudName"
    }
    $VcsaEndpoint = $response.properties.endpoints.vcsa
    $VcsaIPAddress = [System.Uri]::new($vcsaEndpoint).Host

    return $vcsaIPAddress
}

function Get-AVSvCenterCredential {
    param (
        [Hashtable] $AuthHeader,
        [String] $SubscriptionId,
        [String] $AvsResourceGroupName,
        [String] $AvsPrivateCloudName
    )
    $RestUri = "https://management.azure.com/subscriptions/$($subscriptionId)/resourceGroups/$($avsResourceGroupName)/providers/Microsoft.AVS/privateClouds/$($avsPrivateCloudName)/listAdminCredentials?api-version=2022-05-01"
    $Response = Invoke-RestMethod -Uri $restUri -Method Post -Headers $AuthHeader
    if (-not $Response) {
        throw "Failed to get AVS vCenter credential. Please make sure you have the right permission to access the AVS instance with subscriptionId $subscriptionId, resource group $avsResourceGroupName and private cloud $avsPrivateCloudName"
    }
    return $Response
}

function Connect-AVSvCenter {
    param (
        [String] $AVSResourceGroupName,
        [String] $AVSPrivateCloudName,

        [Parameter(Mandatory=$true)]
        $Logger
    )
    $AzContext = Get-AzContext
    $AzureSubscriptionId = $AzContext.Subscription.Id
    $Logger.LogDebug("The default subscriptionId $AzureSubscriptionId of the current Azure context will be used.")
    $AuthHeader = Get-AzureAuthHeader -AzContext $AzContext
    $AVSvCenterCredential = Get-AVSvCenterCredential -AuthHeader $AuthHeader -SubscriptionId $AzureSubscriptionId -AVSResourceGroupName $AVSResourceGroupName -AVSPrivateCloudName $AVSPrivateCloudName
    $AVSvCenterEndpoint = Get-AVSvCenterEndpoint  -AuthHeader $AuthHeader -SubscriptionId $AzureSubscriptionId -AVSResourceGroupName $AVSResourceGroupName -AVSPrivateCloudName $AVSPrivateCloudName
    $Credential = New-Object System.Management.Automation.PSCredential -ArgumentList ($AVSvCenterCredential.vCenterUsername, $(ConvertTo-SecureString $AVSvCenterCredential.vCenterPassword -AsPlainText -Force))
    # Try to connect to vcenter everytime when a cmdlet is invoked by the user to make sure right avs instance is connected
    $vCenterServer = Connect-VIserver -server $AVSvCenterEndpoint -Credential $Credential -ErrorAction Stop
    return $vCenterServer
}

function Connect-PureCloudBlockStore {
    Param (
        [Parameter(Mandatory=$false)]
        $PureCloudBlockStoreConnection
    )

    if (-not $PureCloudBlockStoreConnection) {
        $fa = $global:PURE_AUTH_2_X
        if (-not $fa) {
            throw "Please login to Everpure Cloud Dedicated using: Connect-Pfa2Array"
        }
    }
    else {
        $fa = $PureCloudBlockStoreConnection
    }

    $ClientName = "PureStorage.CBS.AVS"
    $module = Get-Module -Name $ClientName
    if ($module) {
      $Version = $module.Version.ToString()
    } else {
      $Version = "0.0.0.0"
    }

    # if was able to connect, also update the user agent to track backup sdk telemetry
    $success = $fa.UpdateUserAgent($ClientName, $Version );

    # this should not be a show stopper
    if(-not $success){
        Write-Warning -Level WARN -FunctionName $FunctionName -Msg "Failed to set useragent"
    }
    return $fa
}

function Purge-AzureSecretWithRetry {
    Param (
        [String] $KeyVaultName,
        [String] $SecretName
    )
    $errorOccurred = $true
    $retryCount = 0
    # Unfortunately we have to do retry because Remove-AzKeyVaultSecret is asyncronous. If purge happens directly after delete, "Secret is currently being deleted" error is thrown
    do {
        try {
            # To purge, use -InRemovedState parameter
            $retryCount = $retryCount + 1
            Remove-AzkeyVaultSecret -VaultName $KeyVaultName -Name $SecretName -InRemovedState -Force -ErrorAction Stop
            $errorOccurred = $false
        } catch {
            $errorOccurred = $true
            if ($retryCount -gt 3) {
                throw
            }
            Start-Sleep -Seconds 5
        }
    } while ($errorOccurred)
}

function Get-AvsPrivateCloudInformation {
    param (
        [Parameter(Mandatory=$true)]
        [String] $AvsResourceGroupName,
        [Parameter(Mandatory=$true)]
        [String] $AvsPrivateCloudName,

        [Parameter(Mandatory=$true)]
        $Logger
    )
    $AzContext = Get-AzContext
    $AzureSubscriptionId = $AzContext.Subscription.Id
    $Logger.LogDebug("The default subscriptionId $AzureSubscriptionId of the current Azure context will be used.")
    $RestUri = "https://management.azure.com/subscriptions/$($AzureSubscriptionId)/resourceGroups/$($avsResourceGroupName)/providers/Microsoft.AVS/privateClouds/$($avsPrivateCloudName)?api-version=2024-09-01"
    $Response = Invoke-AzRest -Uri $restUri -Method Get
    if (-not $Response -or $Response.StatusCode -ne 200) {
        throw "Failed to get AVS private cloud information. Please make sure private cloud $AvsPrivateCloudName exists in resource group $AvsResourceGroupName."
    }
    $result = $Response.Content | ConvertFrom-Json
    return $result
}

function Get-AvsClusterInformation {
    param (
        [String] $AvsResourceGroupName,
        [String] $AvsPrivateCloudName,
        [String] $AvsClusterName,

        [Parameter(Mandatory=$true)]
        $Logger
    )
    $AzContext = Get-AzContext
    $AzureSubscriptionId = $AzContext.Subscription.Id
    $Logger.LogDebug("The default subscriptionId $AzureSubscriptionId of the current Azure context will be used.")
    $RestUri = "https://management.azure.com/subscriptions/$($AzureSubscriptionId)/resourceGroups/$($avsResourceGroupName)/providers/Microsoft.AVS/privateClouds/$($avsPrivateCloudName)/clusters/$($avsClusterName)?api-version=2022-05-01"
    $Response = Invoke-AzRest -Uri $restUri -Method Get
    if (-not $Response -or $Response.StatusCode -ne 200) {
        throw "Failed to get AVS cluster information. Please make sure cluster $AvsClusterName exists. The cluster name would be case sensitive."
    }
    $result = $Response.Content | ConvertFrom-Json
    return $result
}

function Get-AvsClusterSku {
    param (
        [String] $AvsResourceGroupName,
        [String] $AvsPrivateCloudName,
        [String] $AvsClusterName,

        [Parameter(Mandatory=$true)]
        $Logger
    )

    $result = Get-AvsClusterInformation -AvsResourceGroupName $AvsResourceGroupName -AvsPrivateCloudName $AvsPrivateCloudName -AvsClusterName $AvsClusterName -Logger $Logger
    return $result.sku.name
}

function Test-AVSProvisioningState {
    <#
    .SYNOPSIS
        Validates that both the AVS private cloud and the specified cluster have a 'Succeeded' provisioning status.
    .DESCRIPTION
        Checks the provisioning state of the AVS private cloud via the Azure REST API (api-version 2024-09-01),
        then checks the provisioning state of the specified cluster. Throws a terminating error if either is not 'Succeeded'.
    .PARAMETER AvsResourceGroupName
        The name of the Azure resource group containing the AVS private cloud.
    .PARAMETER AvsPrivateCloudName
        The name of the AVS private cloud.
    .PARAMETER AvsClusterName
        The name of the AVS cluster to validate.
    #>

    param (
        [Parameter(Mandatory=$true)]
        [String] $AvsResourceGroupName,
        [Parameter(Mandatory=$true)]
        [String] $AvsPrivateCloudName,
        [Parameter(Mandatory=$true)]
        [String] $AvsClusterName,
        [Parameter(Mandatory=$true)]
        $Logger
    )

    $privateCloudResult = Get-AvsPrivateCloudInformation -AvsResourceGroupName $AvsResourceGroupName -AvsPrivateCloudName $AvsPrivateCloudName -Logger $Logger
    $privateCloudStatus = $privateCloudResult.properties.provisioningState
    if ($privateCloudStatus -ne $SUCCEEDED_PROVISIONING_STATUS) {
        throw "The current provisioning status of AVS private cloud $AvsPrivateCloudName is $privateCloudStatus. The operation can only proceed when provisioning status is '$SUCCEEDED_PROVISIONING_STATUS'"
    }

    $Logger.LogInfo("The current provisioning status of AVS private cloud $AvsPrivateCloudName is $privateCloudStatus.")

    $clusterResult = Get-AvsClusterInformation -AvsResourceGroupName $AvsResourceGroupName -AvsPrivateCloudName $AvsPrivateCloudName -AvsClusterName $AvsClusterName -Logger $Logger
    $clusterStatus = $clusterResult.properties.provisioningState
    if ($clusterStatus -ne $SUCCEEDED_PROVISIONING_STATUS) {
        throw "The current provisioning status of cluster $AvsClusterName is $clusterStatus. The operation can only proceed when provisioning status is '$SUCCEEDED_PROVISIONING_STATUS'"
    }

    $Logger.LogInfo("The current provisioning status of cluster $AvsClusterName is $clusterStatus.")
}

$MPIO_SKUS = @(
    "av36", "av36t"
    "av36p", "av36pt"
    "av52", "av52t"
)
function Test-MPIOProvisionStatus {
    param (
        [Parameter(Mandatory=$true)]
        [String] $AvsResourceGroupName,
        [Parameter(Mandatory=$true)]
        [String] $AvsPrivateCloudName,
        [Parameter(Mandatory=$true)]
        [VMware.VimAutomation.ViCore.Types.V1.Inventory.Cluster]$Cluster,
        [Parameter(Mandatory=$true)]
        $Logger
    )

    $sku = Get-AvsClusterSku -AvsResourceGroupName $AvsResourceGroupName -AvsPrivateCloudName $AvsPrivateCloudName -AvsClusterName $Cluster.Name -Logger $Logger
    if ($sku -in $MPIO_SKUS) {
        $Logger.LogInfo("MPIO is supported for cluster $Cluster. Checking vmk interfaces....")
        $VMHosts = Get-VMHost -Location $Cluster
        foreach ($VMHost in $VMHosts) {
            $MpioAdapterOne = Get-VMHostNetworkAdapter -VMHost $VMHost | Where-Object {$_.Name -eq "vmk5"}
            $MpioAdapterTwo = Get-VMHostNetworkAdapter -VMHost $VMHost | Where-Object {$_.Name -eq "vmk6"}
            if ($MpioAdapterOne -and $MpioAdapterTwo) {
                $Logger.LogInfo("MPIO is ready on $VMHost...")
            } else {
                throw "The current provisioning status of MPIO is not ready for host $VMHost. Please check if VMkernel adapter 'vmk5' and 'vmk6' are ready for host $VMhost. If not, please wait for twenty minutes and try again."
            }
        }
    } else {
        $Logger.LogInfo("MPIO is not available for SKU $sku. Skipping MPIO check for cluster $Cluster...")
    }
}

function Get-RunCommandNamedOutput {
    param (
        [String] $RunCmdExecutionName,
        [String] $AvsResourceGroupName,
        [String] $AvsPrivateCloudName,

        [Parameter(Mandatory=$true)]
        $Logger
    )
    $AzContext = Get-AzContext
    $AzureSubscriptionId = $AzContext.Subscription.Id
    $Logger.LogDebug("The default subscriptionId $AzureSubscriptionId of the current Azure context will be used.")
    $AuthHeader = Get-AzureAuthHeader -AzContext $AzContext
    $RestUri = “https://management.azure.com/subscriptions/$($AzureSubscriptionId)/resourceGroups/$($avsResourceGroupName)/providers/Microsoft.AVS/privateClouds/$($avsPrivateCloudName)/scriptExecutions/$($RunCmdExecutionName)?api-version=2021-12-01”
    $Response = Invoke-RestMethod -Uri $restUri -Method Get -Headers $AuthHeader
    return $Response.properties.namedOutputs
}

function Get-Timeout {
    param (
        [Parameter(Mandatory=$true)]
        [string]$Cluster,

        [Parameter(Mandatory=$true)]
        $InputParams
    )

    if ($InputParams.ContainsKey("TimeoutInMinutes")) {
        return $InputParams["TimeoutInMinutes"]
    }
    $HostCount = (Get-VMHost -Location $Cluster).Count
    $Timeout = [Math]::Ceiling(($HostCount * $DEFAULT_PER_HOST_TIMEOUT_IN_SECONDS)/60)
    if ($Timeout -lt $MIN_TIMEOUT_IN_MINUTES ) {
        $Timeout = $MIN_TIMEOUT_IN_MINUTES
    }
    elseif ($Timeout -gt $MAX_TIMEOUT_IN_MINUTES){
        $Timeout = $MAX_TIMEOUT_IN_MINUTES
    }

    return $Timeout
}

function Test-RunCommandPackageAvailability {
    param (
        [Parameter(Mandatory = $true)]
        [String] $SubscriptionId,

        [Parameter(Mandatory = $true)]
        [String] $RunCommandModule,

        [Parameter(Mandatory = $true)]
        [String] $RunCommandPackageVersion,

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

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

        [Parameter(Mandatory=$true)]
        $Logger
    )

    $Uri = "https://management.azure.com/subscriptions/$($SubscriptionId)/resourceGroups/$($AVSResourceGroup)/providers/Microsoft.AVS/privateClouds/$($AVSCloudName)/scriptPackages/$($RunCommandModule)@$($RunCommandPackageVersion)?api-version=2023-03-01"
    $Res = Invoke-AzRest -Uri $Uri -Method GET
    if ($Res.StatusCode -ne 200) {
        throw "Could not find '$($RunCommandModule)@$($RunCommandPackageVersion)' RunCommand package in Azure. Please make sure the package is available in Azure."
    }
    else {
        $Logger.LogInfo("Found '$($RunCommandModule)@$($RunCommandPackageVersion)' RunCommand package in Azure...")
    }
}

# SIG # Begin signature block
# MIIpRAYJKoZIhvcNAQcCoIIpNTCCKTECAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBORgmx7UfoQjrz
# 4d5HUw1+sFEmr2UtQpyWXjSXpEKt/6CCDfIwggbmMIIEzqADAgECAhB3vQ4DobcI
# +FSrBnIQ2QRHMA0GCSqGSIb3DQEBCwUAMFMxCzAJBgNVBAYTAkJFMRkwFwYDVQQK
# ExBHbG9iYWxTaWduIG52LXNhMSkwJwYDVQQDEyBHbG9iYWxTaWduIENvZGUgU2ln
# bmluZyBSb290IFI0NTAeFw0yMDA3MjgwMDAwMDBaFw0zMDA3MjgwMDAwMDBaMFkx
# CzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMS8wLQYDVQQD
# EyZHbG9iYWxTaWduIEdDQyBSNDUgQ29kZVNpZ25pbmcgQ0EgMjAyMDCCAiIwDQYJ
# KoZIhvcNAQEBBQADggIPADCCAgoCggIBANZCTfnjT8Yj9GwdgaYw90g9z9DljeUg
# IpYHRDVdBs8PHXBg5iZU+lMjYAKoXwIC947Jbj2peAW9jvVPGSSZfM8RFpsfe2vS
# o3toZXer2LEsP9NyBjJcW6xQZywlTVYGNvzBYkx9fYYWlZpdVLpQ0LB/okQZ6dZu
# bD4Twp8R1F80W1FoMWMK+FvQ3rpZXzGviWg4QD4I6FNnTmO2IY7v3Y2FQVWeHLw3
# 3JWgxHGnHxulSW4KIFl+iaNYFZcAJWnf3sJqUGVOU/troZ8YHooOX1ReveBbz/IM
# BNLeCKEQJvey83ouwo6WwT/Opdr0WSiMN2WhMZYLjqR2dxVJhGaCJedDCndSsZlR
# Qv+hst2c0twY2cGGqUAdQZdihryo/6LHYxcG/WZ6NpQBIIl4H5D0e6lSTmpPVAYq
# gK+ex1BC+mUK4wH0sW6sDqjjgRmoOMieAyiGpHSnR5V+cloqexVqHMRp5rC+QBmZ
# y9J9VU4inBDgoVvDsy56i8Te8UsfjCh5MEV/bBO2PSz/LUqKKuwoDy3K1JyYikpt
# WjYsL9+6y+JBSgh3GIitNWGUEvOkcuvuNp6nUSeRPPeiGsz8h+WX4VGHaekizIPA
# tw9FbAfhQ0/UjErOz2OxtaQQevkNDCiwazT+IWgnb+z4+iaEW3VCzYkmeVmda6tj
# cWKQJQ0IIPH/AgMBAAGjggGuMIIBqjAOBgNVHQ8BAf8EBAMCAYYwEwYDVR0lBAww
# CgYIKwYBBQUHAwMwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU2rONwCSQ
# o2t30wygWd0hZ2R2C3gwHwYDVR0jBBgwFoAUHwC/RoAK/Hg5t6W0Q9lWULvOljsw
# gZMGCCsGAQUFBwEBBIGGMIGDMDkGCCsGAQUFBzABhi1odHRwOi8vb2NzcC5nbG9i
# YWxzaWduLmNvbS9jb2Rlc2lnbmluZ3Jvb3RyNDUwRgYIKwYBBQUHMAKGOmh0dHA6
# Ly9zZWN1cmUuZ2xvYmFsc2lnbi5jb20vY2FjZXJ0L2NvZGVzaWduaW5ncm9vdHI0
# NS5jcnQwQQYDVR0fBDowODA2oDSgMoYwaHR0cDovL2NybC5nbG9iYWxzaWduLmNv
# bS9jb2Rlc2lnbmluZ3Jvb3RyNDUuY3JsMFYGA1UdIARPME0wQQYJKwYBBAGgMgEy
# MDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8vd3d3Lmdsb2JhbHNpZ24uY29tL3JlcG9z
# aXRvcnkvMAgGBmeBDAEEATANBgkqhkiG9w0BAQsFAAOCAgEACIhyJsav+qxfBsCq
# jJDa0LLAopf/bhMyFlT9PvQwEZ+PmPmbUt3yohbu2XiVppp8YbgEtfjry/RhETP2
# ZSW3EUKL2Glux/+VtIFDqX6uv4LWTcwRo4NxahBeGQWn52x/VvSoXMNOCa1Za7j5
# fqUuuPzeDsKg+7AE1BMbxyepuaotMTvPRkyd60zsvC6c8YejfzhpX0FAZ/ZTfepB
# 7449+6nUEThG3zzr9s0ivRPN8OHm5TOgvjzkeNUbzCDyMHOwIhz2hNabXAAC4ShS
# S/8SS0Dq7rAaBgaehObn8NuERvtz2StCtslXNMcWwKbrIbmqDvf+28rrvBfLuGfr
# 4z5P26mUhmRVyQkKwNkEcUoRS1pkw7x4eK1MRyZlB5nVzTZgoTNTs/Z7KtWJQDxx
# pav4mVn945uSS90FvQsMeAYrz1PYvRKaWyeGhT+RvuB4gHNU36cdZytqtq5NiYAk
# CFJwUPMB/0SuL5rg4UkI4eFb1zjRngqKnZQnm8qjudviNmrjb7lYYuA2eDYB+sGn
# iXomU6Ncu9Ky64rLYwgv/h7zViniNZvY/+mlvW1LWSyJLC9Su7UpkNpDR7xy3bzZ
# v4DB3LCrtEsdWDY3ZOub4YUXmimi/eYI0pL/oPh84emn0TCOXyZQK8ei4pd3iu/Y
# TT4m65lAYPM8Zwy2CHIpNVOBNNwwggcEMIIE7KADAgECAgxcuW61kTkv+4t8zgQw
# DQYJKoZIhvcNAQELBQAwWTELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNp
# Z24gbnYtc2ExLzAtBgNVBAMTJkdsb2JhbFNpZ24gR0NDIFI0NSBDb2RlU2lnbmlu
# ZyBDQSAyMDIwMB4XDTI0MDMxMTE0MDQxMloXDTI3MDMxMjE0MDQxMlowcjELMAkG
# A1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFDASBgNVBAcTC1NhbnRhIENs
# YXJhMRswGQYDVQQKExJQdXJlIFN0b3JhZ2UsIEluYy4xGzAZBgNVBAMTElB1cmUg
# U3RvcmFnZSwgSW5jLjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMCQ
# rioSn48IvHpTg5dofsUYj/pNTDidwjYUrcxVu78NoyhSweG8FhcxDi/SI40+8Fcc
# l3D5ZoqpjkFnGhzSwmpxU3J4AP7+fdTZht9eWD1I5qKY07esYwdPDV4yg+csPfdG
# PqI2XjRfT5UC3YkXQeUrX8KQZldD4KqvgxzpYcuBwsgHbTb/eArpi68YgFR2jgZG
# yZigfy8RuJMrL1thcBOe/VWjUyK21wVT8cuunBYFaStLHhsRBRMDcZBDuTSGC4ev
# E6oaCqlQbdMl9YFJ64mDQsKlCxrr7rmLVtcVzKGwmjp4b2xRwE+RmTh6JtrUL9Wx
# /3a3UzgAnDNimfwp85zoL48kyLtHqQ3FI8tVKGm+aBOgBZfmURoy7fbp4zKhGgqF
# bpOmILO16i4f999YsEEJQgIF3CtyH1R60/ZZWlDmoeeEgjAGrnd14muU5Hk3Cksr
# 43uPUAg+fV78Y0fDV85ibm42ZwwPuz6MI4HhYNUlGzRwIQ31vjaGuAMWHNqFKkcO
# 0JuIeHQ/gFKPnYIxnGC9H9R4Kw/uMezqtnYJwGU2epB/ABl/w7U4NgU2ZOxWB5BF
# y4frZ3f+hNgbjFUjMaXnVFotOJxXntzjdSl4znw8DaKiC5ooChteZMITG9p078p/
# TUsOJQbUtFADSY1hsfCfB7t+gJSNt5peS9GOZIMVAgMBAAGjggGxMIIBrTAOBgNV
# HQ8BAf8EBAMCB4AwgZsGCCsGAQUFBwEBBIGOMIGLMEoGCCsGAQUFBzAChj5odHRw
# Oi8vc2VjdXJlLmdsb2JhbHNpZ24uY29tL2NhY2VydC9nc2djY3I0NWNvZGVzaWdu
# Y2EyMDIwLmNydDA9BggrBgEFBQcwAYYxaHR0cDovL29jc3AuZ2xvYmFsc2lnbi5j
# b20vZ3NnY2NyNDVjb2Rlc2lnbmNhMjAyMDBWBgNVHSAETzBNMEEGCSsGAQQBoDIB
# MjA0MDIGCCsGAQUFBwIBFiZodHRwczovL3d3dy5nbG9iYWxzaWduLmNvbS9yZXBv
# c2l0b3J5LzAIBgZngQwBBAEwCQYDVR0TBAIwADBFBgNVHR8EPjA8MDqgOKA2hjRo
# dHRwOi8vY3JsLmdsb2JhbHNpZ24uY29tL2dzZ2NjcjQ1Y29kZXNpZ25jYTIwMjAu
# Y3JsMBMGA1UdJQQMMAoGCCsGAQUFBwMDMB8GA1UdIwQYMBaAFNqzjcAkkKNrd9MM
# oFndIWdkdgt4MB0GA1UdDgQWBBSzJ9KiDCa3UBiAajy+Iioj5kQjzDANBgkqhkiG
# 9w0BAQsFAAOCAgEAHsFQixeQEcoHurq9NWSUt4S39Q+UGP6crmVq3Wwy9g23YbdW
# g+SgMxoLUqdoDfA4k4B6Dyoo0jEQzn2kxnsnT9lNHKrcZHH88dv0hjfiH2qAiQWa
# zPjS3LhK2J6nhpyipJPpyRaSQG4x4aG0NB2D4WUfUz9CGAYsERJGww/wkTaaxMip
# ttKDTaI1C49u1igDfRzIO+Q8vuyyBFLiYTno/df97xtjNC+KxxFhDhl/4tawK6kw
# xaVzCMAfj48I67Wbo4DMH6pM1s19as7c3qp92i3MylGKsB6+u+o7UkbSdLNkS4AL
# I33CJOUc+GoK3Nt5IXXCFJTQFHBXkBdAur3gmlXEm8vlNG/1Sbxr0H7T1e7ABGH/
# 48o/+PeMLuCc72EeK5dJ4cX9NEQ3QnTsZHwGnYzjEOvOvP0s1c7yNsDbcUHoIqQv
# b5xS5aqMU5G+8sdPQ1nwpPf7gGaEEbAVW4w51Pam42qeN9HIPa+ZinXnsN02Kk1Q
# w0QwUqzaQy9W/gIquI0KOjw0LmoW9M/8S0lrjpEq2eEeUw9WQLhhUEIirFxGPtjq
# iCLiiS9CZ+kf2vWLJKUspkYv+OHT3q805Zg1dJsBFAzEYUFLb1mhmigDEO9bsMor
# jECIL2ijE5zHtbGkalrrsPWu8tiDT/B7P9GSYzKfOOy4PoOIfWSK0IxlS7Ixghqo
# MIIapAIBATBpMFkxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52
# LXNhMS8wLQYDVQQDEyZHbG9iYWxTaWduIEdDQyBSNDUgQ29kZVNpZ25pbmcgQ0Eg
# MjAyMAIMXLlutZE5L/uLfM4EMA0GCWCGSAFlAwQCAQUAoIGXMBkGCSqGSIb3DQEJ
# AzEMBgorBgEEAYI3AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMCsG
# CisGAQQBgjcCAQwxHTAboRmAF2h0dHBzOi8vcHVyZXN0b3JhZ2UuY29tMC8GCSqG
# SIb3DQEJBDEiBCBQqkr2FR6DlRaPoaJkgwQFk+tm3wKVULQ20wMbjOnYjzANBgkq
# hkiG9w0BAQEFAASCAgBM5zEWnoy91md9miJovGtJM16yQIN3V/CVZ2spPgf8qAKR
# FgUNqe6plVubY6Jj+caNJ1EjfHUbEzDP4FSZrQERmsAskk2yX1DHVi79wg0S5W5s
# Tjzy6/5IjbnWdlat6ZEzFPLftLRMZ+ZH45/BfGZ224CIBevNXb6w0OrknxoQI3eH
# OUjsEFEyoSvNSgZlCqTmh1DML7mYC2g64NPAi46POmoTHLFTwIaKXrcIE/2blNWA
# hZCrALUsfiB/RBxkDbY0UylBmxWoeRLjGQe/EufuOnu7Ba/t5ZMjlty8tB30BTKv
# wI8UeUxESUszbSeovbj53IGTYFLhQJ93TfrVTR9Lo0+ox8m5/eA+oL0+4dSE4KwJ
# xDB4DOwwGlWasCeTX2WpO20iHxYkPCQjpwE5cqm3wvraRGSoDizxvjv/0bkLj+pw
# FkgHd7b0lz7Wn4ZiFcZwYdvYlHBykhrhVDWtw/3QtlhsPhmbErLIxGlTr3Stq9kF
# PD5h9PcImhcERu8icmBLDdechBpUa4MZFmRDEE823OfL/KWHLPFRC2TLomsi+q1K
# t3K/nUWGlAGz5imWUoyrfE1sifPcsXvQwAtelFYpooINFh63O7oB5vELFGR9tTc8
# jFF/39JkdGGj40peKQkNInkowSYL5+woJXTQXUUjVsS17MGF+vgO/KP39di5JaGC
# F3YwghdyBgorBgEEAYI3AwMBMYIXYjCCF14GCSqGSIb3DQEHAqCCF08wghdLAgED
# MQ8wDQYJYIZIAWUDBAIBBQAwdwYLKoZIhvcNAQkQAQSgaARmMGQCAQEGCWCGSAGG
# /WwHATAxMA0GCWCGSAFlAwQCAQUABCBFdeM75nm5it6vkAA1heeUTCQZr3jQdCGy
# jL0zdUutMQIQATcTyeyAhcHBBW2MqBDOTxgPMjAyNjA1MjEwOTE3MTBaoIITOjCC
# Bu0wggTVoAMCAQICEAqA7xhLjfEFgtHEdqeVdGgwDQYJKoZIhvcNAQELBQAwaTEL
# MAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMUEwPwYDVQQDEzhE
# aWdpQ2VydCBUcnVzdGVkIEc0IFRpbWVTdGFtcGluZyBSU0E0MDk2IFNIQTI1NiAy
# MDI1IENBMTAeFw0yNTA2MDQwMDAwMDBaFw0zNjA5MDMyMzU5NTlaMGMxCzAJBgNV
# BAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNl
# cnQgU0hBMjU2IFJTQTQwOTYgVGltZXN0YW1wIFJlc3BvbmRlciAyMDI1IDEwggIi
# MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDQRqwtEsae0OquYFazK1e6b1H/
# hnAKAd/KN8wZQjBjMqiZ3xTWcfsLwOvRxUwXcGx8AUjni6bz52fGTfr6PHRNv6T7
# zsf1Y/E3IU8kgNkeECqVQ+3bzWYesFtkepErvUSbf+EIYLkrLKd6qJnuzK8Vcn0D
# vbDMemQFoxQ2Dsw4vEjoT1FpS54dNApZfKY61HAldytxNM89PZXUP/5wWWURK+If
# xiOg8W9lKMqzdIo7VA1R0V3Zp3DjjANwqAf4lEkTlCDQ0/fKJLKLkzGBTpx6EYev
# vOi7XOc4zyh1uSqgr6UnbksIcFJqLbkIXIPbcNmA98Oskkkrvt6lPAw/p4oDSRZr
# eiwB7x9ykrjS6GS3NR39iTTFS+ENTqW8m6THuOmHHjQNC3zbJ6nJ6SXiLSvw4Smz
# 8U07hqF+8CTXaETkVWz0dVVZw7knh1WZXOLHgDvundrAtuvz0D3T+dYaNcwafsVC
# GZKUhQPL1naFKBy1p6llN3QgshRta6Eq4B40h5avMcpi54wm0i2ePZD5pPIssosz
# QyF4//3DoK2O65Uck5Wggn8O2klETsJ7u8xEehGifgJYi+6I03UuT1j7FnrqVrOz
# aQoVJOeeStPeldYRNMmSF3voIgMFtNGh86w3ISHNm0IaadCKCkUe2LnwJKa8TIlw
# CUNVwppwn4D3/Pt5pwIDAQABo4IBlTCCAZEwDAYDVR0TAQH/BAIwADAdBgNVHQ4E
# FgQU5Dv88jHt/f3X85FxYxlQQ89hjOgwHwYDVR0jBBgwFoAU729TSunkBnx6yuKQ
# VvYv1Ensy04wDgYDVR0PAQH/BAQDAgeAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMI
# MIGVBggrBgEFBQcBAQSBiDCBhTAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGln
# aWNlcnQuY29tMF0GCCsGAQUFBzAChlFodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5j
# b20vRGlnaUNlcnRUcnVzdGVkRzRUaW1lU3RhbXBpbmdSU0E0MDk2U0hBMjU2MjAy
# NUNBMS5jcnQwXwYDVR0fBFgwVjBUoFKgUIZOaHR0cDovL2NybDMuZGlnaWNlcnQu
# Y29tL0RpZ2lDZXJ0VHJ1c3RlZEc0VGltZVN0YW1waW5nUlNBNDA5NlNIQTI1NjIw
# MjVDQTEuY3JsMCAGA1UdIAQZMBcwCAYGZ4EMAQQCMAsGCWCGSAGG/WwHATANBgkq
# hkiG9w0BAQsFAAOCAgEAZSqt8RwnBLmuYEHs0QhEnmNAciH45PYiT9s1i6UKtW+F
# ERp8FgXRGQ/YAavXzWjZhY+hIfP2JkQ38U+wtJPBVBajYfrbIYG+Dui4I4PCvHpQ
# uPqFgqp1PzC/ZRX4pvP/ciZmUnthfAEP1HShTrY+2DE5qjzvZs7JIIgt0GCFD9kt
# x0LxxtRQ7vllKluHWiKk6FxRPyUPxAAYH2Vy1lNM4kzekd8oEARzFAWgeW3az2xe
# jEWLNN4eKGxDJ8WDl/FQUSntbjZ80FU3i54tpx5F/0Kr15zW/mJAxZMVBrTE2oi0
# fcI8VMbtoRAmaaslNXdCG1+lqvP4FbrQ6IwSBXkZagHLhFU9HCrG/syTRLLhAezu
# /3Lr00GrJzPQFnCEH1Y58678IgmfORBPC1JKkYaEt2OdDh4GmO0/5cHelAK2/gTl
# QJINqDr6JfwyYHXSd+V08X1JUPvB4ILfJdmL+66Gp3CSBXG6IwXMZUXBhtCyIaeh
# r0XkBoDIGMUG1dUtwq1qmcwbdUfcSYCn+OwncVUXf53VJUNOaMWMts0VlRYxe5nK
# +At+DI96HAlXHAL5SlfYxJ7La54i71McVWRP66bW+yERNpbJCjyCYG2j+bdpxo/1
# Cy4uPcU3AWVPGrbn5PhDBf3Froguzzhk++ami+r3Qrx5bIbY3TVzgiFI7Gq3zWcw
# gga0MIIEnKADAgECAhANx6xXBf8hmS5AQyIMOkmGMA0GCSqGSIb3DQEBCwUAMGIx
# CzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3
# dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBH
# NDAeFw0yNTA1MDcwMDAwMDBaFw0zODAxMTQyMzU5NTlaMGkxCzAJBgNVBAYTAlVT
# MRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1
# c3RlZCBHNCBUaW1lU3RhbXBpbmcgUlNBNDA5NiBTSEEyNTYgMjAyNSBDQTEwggIi
# MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC0eDHTCphBcr48RsAcrHXbo0Zo
# dLRRF51NrY0NlLWZloMsVO1DahGPNRcybEKq+RuwOnPhof6pvF4uGjwjqNjfEvUi
# 6wuim5bap+0lgloM2zX4kftn5B1IpYzTqpyFQ/4Bt0mAxAHeHYNnQxqXmRinvuNg
# xVBdJkf77S2uPoCj7GH8BLuxBG5AvftBdsOECS1UkxBvMgEdgkFiDNYiOTx4OtiF
# cMSkqTtF2hfQz3zQSku2Ws3IfDReb6e3mmdglTcaarps0wjUjsZvkgFkriK9tUKJ
# m/s80FiocSk1VYLZlDwFt+cVFBURJg6zMUjZa/zbCclF83bRVFLeGkuAhHiGPMvS
# GmhgaTzVyhYn4p0+8y9oHRaQT/aofEnS5xLrfxnGpTXiUOeSLsJygoLPp66bkDX1
# ZlAeSpQl92QOMeRxykvq6gbylsXQskBBBnGy3tW/AMOMCZIVNSaz7BX8VtYGqLt9
# MmeOreGPRdtBx3yGOP+rx3rKWDEJlIqLXvJWnY0v5ydPpOjL6s36czwzsucuoKs7
# Yk/ehb//Wx+5kMqIMRvUBDx6z1ev+7psNOdgJMoiwOrUG2ZdSoQbU2rMkpLiQ6bG
# RinZbI4OLu9BMIFm1UUl9VnePs6BaaeEWvjJSjNm2qA+sdFUeEY0qVjPKOWug/G6
# X5uAiynM7Bu2ayBjUwIDAQABo4IBXTCCAVkwEgYDVR0TAQH/BAgwBgEB/wIBADAd
# BgNVHQ4EFgQU729TSunkBnx6yuKQVvYv1Ensy04wHwYDVR0jBBgwFoAU7NfjgtJx
# XWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUF
# BwMIMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGln
# aWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5j
# b20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNVHR8EPDA6MDigNqA0hjJo
# dHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNy
# bDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEwDQYJKoZIhvcNAQEL
# BQADggIBABfO+xaAHP4HPRF2cTC9vgvItTSmf83Qh8WIGjB/T8ObXAZz8OjuhUxj
# aaFdleMM0lBryPTQM2qEJPe36zwbSI/mS83afsl3YTj+IQhQE7jU/kXjjytJgnn0
# hvrV6hqWGd3rLAUt6vJy9lMDPjTLxLgXf9r5nWMQwr8Myb9rEVKChHyfpzee5kH0
# F8HABBgr0UdqirZ7bowe9Vj2AIMD8liyrukZ2iA/wdG2th9y1IsA0QF8dTXqvcnT
# mpfeQh35k5zOCPmSNq1UH410ANVko43+Cdmu4y81hjajV/gxdEkMx1NKU4uHQcKf
# ZxAvBAKqMVuqte69M9J6A47OvgRaPs+2ykgcGV00TYr2Lr3ty9qIijanrUR3anzE
# wlvzZiiyfTPjLbnFRsjsYg39OlV8cipDoq7+qNNjqFzeGxcytL5TTLL4ZaoBdqbh
# OhZ3ZRDUphPvSRmMThi0vw9vODRzW6AxnJll38F0cuJG7uEBYTptMSbhdhGQDpOX
# gpIUsWTjd6xpR6oaQf/DJbg3s6KCLPAlZ66RzIg9sC+NJpud/v4+7RWsWCiKi9EO
# LLHfMR2ZyJ/+xhCx9yHbxtl5TPau1j/1MIDpMPx0LckTetiSuEtQvLsNz3Qbp7wG
# WqbIiOWCnb5WqxL3/BAPvIXKUjPSxyZsq8WhbaM2tszWkPZPubdcMIIFjTCCBHWg
# AwIBAgIQDpsYjvnQLefv21DiCEAYWjANBgkqhkiG9w0BAQwFADBlMQswCQYDVQQG
# EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl
# cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcN
# MjIwODAxMDAwMDAwWhcNMzExMTA5MjM1OTU5WjBiMQswCQYDVQQGEwJVUzEVMBMG
# A1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEw
# HwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0GCSqGSIb3DQEB
# AQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3yithZwuEp
# pz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1Ifxp4VpX6+
# n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDVySAdYykt
# zuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfISKhmV1efVFiODCu3T6cw
# 2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jHtrHEtWoYOAMQjdjUN6Qu
# BX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6MUSaM0C/CNdaSaTC
# 5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCiEhtmmnTK
# 3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADMfRyVw4/3
# IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QYuKZ3AeEP
# lAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXKchYiCd98
# THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4KJpn15GkvmB0t9dmpsh3l
# GwIDAQABo4IBOjCCATYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU7NfjgtJx
# XWRM3y5nP+e6mK4cD08wHwYDVR0jBBgwFoAUReuir/SSy4IxLVGLp6chnfNtyA8w
# DgYDVR0PAQH/BAQDAgGGMHkGCCsGAQUFBwEBBG0wazAkBggrBgEFBQcwAYYYaHR0
# cDovL29jc3AuZGlnaWNlcnQuY29tMEMGCCsGAQUFBzAChjdodHRwOi8vY2FjZXJ0
# cy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3J0MEUGA1Ud
# HwQ+MDwwOqA4oDaGNGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFz
# c3VyZWRJRFJvb3RDQS5jcmwwEQYDVR0gBAowCDAGBgRVHSAAMA0GCSqGSIb3DQEB
# DAUAA4IBAQBwoL9DXFXnOF+go3QbPbYW1/e/Vwe9mqyhhyzshV6pGrsi+IcaaVQi
# 7aSId229GhT0E0p6Ly23OO/0/4C5+KH38nLeJLxSA8hO0Cre+i1Wz/n096wwepqL
# sl7Uz9FDRJtDIeuWcqFItJnLnU+nBgMTdydE1Od/6Fmo8L8vC6bp8jQ87PcDx4eo
# 0kxAGTVGamlUsLihVo7spNU96LHc/RzY9HdaXFSMb++hUD38dglohJ9vytsgjTVg
# HAIDyyCwrFigDkBjxZgiwbJZ9VVrzyerbHbObyMt9H5xaiNrIv8SuFQtJ37YOtnw
# toeW/VvRXKwYw02fc7cBqZ9Xql4o4rmUMYIDfDCCA3gCAQEwfTBpMQswCQYDVQQG
# EwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lDZXJ0
# IFRydXN0ZWQgRzQgVGltZVN0YW1waW5nIFJTQTQwOTYgU0hBMjU2IDIwMjUgQ0Ex
# AhAKgO8YS43xBYLRxHanlXRoMA0GCWCGSAFlAwQCAQUAoIHRMBoGCSqGSIb3DQEJ
# AzENBgsqhkiG9w0BCRABBDAcBgkqhkiG9w0BCQUxDxcNMjYwNTIxMDkxNzEwWjAr
# BgsqhkiG9w0BCRACDDEcMBowGDAWBBTdYjCshgotMGvaOLFoeVIwB/tBfjAvBgkq
# hkiG9w0BCQQxIgQgprPKTvFiCr1OfFlhVXwyHhRVXwpIpcfOgqtiLOB8ckMwNwYL
# KoZIhvcNAQkQAi8xKDAmMCQwIgQgSqA/oizXXITFXJOPgo5na5yuyrM/420mmqM0
# 8UYRCjMwDQYJKoZIhvcNAQEBBQAEggIAB36ZsqwzUh2tsL16ziSFW3cvWm0h+PEi
# d/KpYJamFfzVXzq+/zC53ChG9TWrbVYYR5zZS8TVsf3AypHSxCheMYvlgGTXUoGk
# ZRtqBj61KSyW4nx341GzuLA2mkAOQ/oPA+Amf7KZHTbPDSHhObAsBEPCOynTXca7
# abpioOzTD3nO8gvw41DnT8N4RrPICQZO8Ljfu1Nd0Nj20CFRX9aa1Cir+QVw0366
# xIeWiWjoi6IFIzNVwG0sYsFYwtPRLpwLQwB2c+fodNynLAAIQd+Esq4fceSQerI3
# jIl0BUV36sb+MkXL1KEjJQJNykPGww7bRHzOOYua8QPuGfNrYlqUgDU0HB/sOjlj
# 8V3ijVKeFwB6mcQOEDCgktNE3Crk98HXZr7PCa1h5Zyva+tSub6FR4X3sLAX3c5M
# zOdqEdY9WuAUZ0sqskyZGLLYrI0ENiENjIjSm/2UDXadGj68mIkVclBJy9vvvuTz
# uscthkUubiGPK8YRV1RvXNnqAZRlcrSW8iOY/ajunDqJ+9OCw96lDXoEeV4hPXEc
# SSgesYYbpg2IriJkMsZ7mGhssJiICPYxPqJotoLNGUUgZK+pHSn16g52UF/WnBuN
# iuUMJ6gG9Gw/XBJJoB522nKx2o1ENgVbhwY3jVhD/oZmLUMKkyjl5hXktNxcXzxu
# eBPqKbdQFlM=
# SIG # End signature block