AzStackHciObservability/AzStackHci.Observability.Helpers.psm1

Import-LocalizedData -BindingVariable lvsTxt -FileName AzStackHci.Observability.Strings.psd1

function Test-ObservabilityVolume
{
    <#
    .SYNOPSIS
        Check volume free space meets the required observability threshold size
    .DESCRIPTION
        Check volume free space meets the required observability threshold size
    .PARAMETER PsSession
        Specify the PsSession(s) used to validation from.
    .PARAMETER OperationType
        Specify the Operation Type to target for observability validation. e.g. Deployment, Update, etc
    .PARAMETER PhysicalDriveLetter
        Specify PhysicalDriveLetter used to validation observability Volume. Default C drive is used as observability volume.
    #>

    [CmdletBinding()]
    param (

        [Parameter(Mandatory = $true)]
        [System.Management.Automation.Runspaces.PSSession[]]
        $PsSession,

        [Parameter(Mandatory = $false)]
        [string[]]
        $OperationType,

        [Parameter(Mandatory=$false, ParameterSetName="DefaultSet")]
        [string] $PhysicalDriveLetter = "C"
    )

    try
    {
        $defalutHC4ObservabilityVolumeSize = 40
        $defalutVMObservabilityVolumeSize = 30
        Log-Info -Message ($lvsTxt.ObservabilityVolumeStartInfo) -Type Info
        Log-Info -Message ($lvsTxt.ObservabilityVolumeDriveInfo -f $PhysicalDriveLetter)
        $lowDiskMsg = ($lvsTxt.LowDiskSpaceMsg -f $PhysicalDriveLetter)

        # Scriptblock to test observabilityVolumeSize on each server
        $testVolumeSb = {
            $AdditionalData = @()
            $status = "SUCCESS"
            $errorMsg = $null
            $hardwareType = $null
            $expectedObservabilityVolumeSizeInGB = $args[0]
            $freeSpaceInGB = 1
            $resource = $null

            try
            {
                # Check if env is Virtual
                $hardwareType = (Get-WmiObject -Class Win32_ComputerSystem).Model
                if ($hardwareType -eq "Virtual Machine")
                {
                    $expectedObservabilityVolumeSizeInGB = $args[1]
                }

                # Check free space on physical volume
                $totalFreeSpace = (Get-Volume -DriveLetter $args[2]).SizeRemaining
                $freeSpaceInGB = [int]($totalFreeSpace / 1GB)
                if ($freeSpaceInGB -lt $expectedObservabilityVolumeSizeInGB)
                {
                    $resource = "Observability volume '$($args[2])' needs, $($expectedObservabilityVolumeSizeInGB) GB free space."
                    throw $args[3]
                }
            }
            catch
            {
                $errorMsg = $_.Exception.Message
                $resource = "Error occurred in Environment Validator Observability Volume test."
                $status = "FAILURE"
            }
            finally
            {
                $AdditionalData += @{
                    HardwareType  = $hardwareType
                    ExpectedObservabilityVolumeSize = $expectedObservabilityVolumeSizeInGB
                    CurrentObservabilityVolumeSize = $freeSpaceInGB
                    Status    = $status
                    Source    = $ENV:COMPUTERNAME
                    Resource  = $resource
                    Detail    = $errorMsg
                }
            }
            return $AdditionalData
        }

        # Run scriptblock
        $observabilityVolumeSizeResult = Invoke-Command -Session $PsSession -ScriptBlock $testVolumeSb -ArgumentList $defalutHC4ObservabilityVolumeSize, $defalutVMObservabilityVolumeSize, $PhysicalDriveLetter, $lowDiskMsg
        $volumeResult = @()
        foreach ($oV in $observabilityVolumeSizeResult)
        {
            $params = @{
                Name               = 'AzStackHci_Observability_Volume'
                Title              = 'Observability Volume Requirement'
                DisplayName        = 'Observability Volume Requirement'
                Severity           = 'CRITICAL'
                Description        = 'Test to check observability volume size requirement is met'
                Tags               = @{
                    'OperationType' = $OperationType
                }
                Remediation        = 'Free up disk space for Observability Volume'
                TargetResourceID   = $oV.Source
                TargetResourceName = $oV.Source
                TargetResourceType = $oV.HardwareType | Get-Unique
                Timestamp          = [datetime]::UtcNow
                Status             = $oV.Status
                AdditionalData     = $oV
                HealthCheckSource  = $ENV:EnvChkrId
            }
            $volumeResult += New-AzStackHciResultObject @params
        }
        return $volumeResult
    }
    catch
    {
        throw $_
    }
}

function Test-LogCollection
{
    <#
    .SYNOPSIS
        Check log collection component mets observability condition for update\upgrade
    .DESCRIPTION
        Check if log collection is in progress, if yes then indicate result with Warning
    .PARAMETER PsSession
        Specify the PsSession used to validation from.
    .PARAMETER OperationType
        Specify the Operation Type to target for observability validation. e.g. Deployment, Update, etc
    #>

    [CmdletBinding()]
    param (

        [Parameter(Mandatory = $true)]
        $PsSession,

        [Parameter(Mandatory = $false)]
        [string[]]
        $OperationType
    )

    try
    {
        Log-Info -Message ($lvsTxt.LogCollectiontInfo) -Type Info
        $logCollectiontErrMsg = $($lvsTxt.LogCollectiontInfo)

        # Scriptblock to check log collection status
        $testlogCollectionSb = {
            $AdditionalData = @()
            $status = 'SUCCESS'
            $errorMsg = $null
            $hardwareType = $null
            $logCollectionTime = $null
            $logCollectionStatus = $null
            $resource = $null

            try
            {
                # Get HardwareType
                    $hardwareType = (Get-WmiObject -Class Win32_ComputerSystem).Model

                    # Check check log collection status
                    $logCollectionHistory = Get-LogCollectionHistory -Verbose:$false -ErrorAction SilentlyContinue
                    if ($logCollectionHistory -ne $null `
                         -and $logCollectionHistory[0] -ne $null `
                         -and $logCollectionHistory[0].Status -eq "Running")
                    {
                        $logCollectionTime = $logCollectionHistory[0].TimeCollected
                        $logCollectionStatus = $logCollectionHistory[0].Status
                        $status = 'FAILURE'
                        $resource = "Log Collection in progress"
                        throw $args[0]
                    }
            }
            catch
            {
                $errorMsg = $_.Exception.Message
                $resource = "Error occurred in Environment Validator Log Collection test."
                $status = 'FAILURE'
            }
            finally
            {
                $AdditionalData += @{
                    LogCollectionTime = $logCollectionTime
                    LogCollectionStatus = $logCollectionStatus
                    HardwareType  = $hardwareType
                    Status    = $status
                    Source    = $ENV:COMPUTERNAME
                    Resource  = $resource
                    Detail    = $errorMsg
                }
            }
            return $AdditionalData
        }

        # Run scriptblock
        $logCollectionResult = Invoke-Command -Session $PsSession -ScriptBlock $testlogCollectionSb -ArgumentList $logCollectiontErrMsg

        # build result
        $logCollectionResultSet = @()
        foreach ($lc in $logCollectionResult)
        {
            $params = @{
                Name               = 'AzStackHci_Observability_LogCollection'
                Title              = 'Observability Log collection Requirement'
                DisplayName        = 'Observability Log collection Requirement'
                Severity           = 'WARNING'
                Description        = 'Test to check observability log collection requirement is met'
                Tags               = @{
                    'OperationType' = $OperationType
                }
                Remediation        = 'Stop or Wait for log collection to be completed'
                TargetResourceID   = $lc.Source
                TargetResourceName = $lc.Source
                TargetResourceType = $lc.HardwareType | Get-Unique
                Timestamp          = [datetime]::UtcNow
                Status             = $lc.Status
                AdditionalData     = $lc
                HealthCheckSource  = $ENV:EnvChkrId
            }
            $logCollectionResultSet += New-AzStackHciResultObject @params
        }
        return $logCollectionResultSet
    }
    catch
    {
        throw $_
    }
}

function Test-RemoteSupport
{
    <#
    .SYNOPSIS
        Test Remote Support Session Terminal is active
    .DESCRIPTION
        Test if active Remote Support Session is in progress
    .PARAMETER PsSession
        Specify the PsSession used to validation from.
    .PARAMETER OperationType
        Specify the Operation Type to target for observability validation. e.g. Deployment, Update, etc
    #>

    [CmdletBinding()]
    param (

        [Parameter(Mandatory = $true)]
        $PsSession,

        [Parameter(Mandatory = $false)]
        [string[]]
        $OperationType
    )

    try
    {
        Log-Info -Message ($lvsTxt.RemoteSupportStartInfo) -Type Info
        $remoteSupportErrMsg = $($lvsTxt.RemoteSupportErrMsg)

        # Scriptblock to check remote support status
        $remoteSupportSessionSb = {
            $AdditionalData = @()
            $status = 'SUCCESS'
            $errorMsg = $null
            $hardwareType = $null
            $nodeName  = $null
            $startTime = $null
            $endTime = $null
            $resource = $null

            try
            {
                # Get HardwareType information
                $hardwareType = (Get-WmiObject -Class Win32_ComputerSystem).Model

                # Check remote support access state
                $getRemoteSupportAccessState = $(Get-AzStackHCIRemoteSupportAccess -Verbose:$false -ErrorAction SilentlyContinue).State
                if ($getRemoteSupportAccessState -eq "Active")
                {
                    $remoteSupportSessionHistory = Get-AzStackHCIRemoteSupportSessionHistory -Verbose:$false -ErrorAction SilentlyContinue | where { $_.EndTime -gt $(Get-Date) }
                    if ($remoteSupportSessionHistory -ne $null -and $($remoteSupportSessionHistory.count) -gt 0)
                    {
                        $startTime = $remoteSupportSessionHistory.StartTime
                        $endTime = $remoteSupportSessionHistory.EndTime
                        $nodeName = $remoteSupportSessionHistory.NodeName
                        $status = 'FAILURE'
                        $resource = "Open Remote Support Session terminal."
                        throw $args[0]
                    }
                }
            }
            catch
            {
                $errorMsg = $_.Exception.Message
                $resource = "Error occurred in Environment Validator Remote Support test."
                $status = 'FAILURE'
            }
            finally
            {
                $AdditionalData += @{
                    HardwareType  = $hardwareType
                    Status    = $status
                    RemoteSupportSessionEndTime  = $endTime
                    RemoteSupportSessionStartTime = $startTime
                    RemoteSupportSessionNodeName = $nodeName
                    Source = $ENV:COMPUTERNAME
                    Resource = $resource
                    Detail = $errorMsg
                }
            }
            return $AdditionalData
        }

        # Run scriptblock
        $remoteSupportSessionResult = Invoke-Command -Session $PsSession -ScriptBlock $remoteSupportSessionSb -ArgumentList $remoteSupportErrMsg
        $remoteSupportSessionSet = @()
        foreach ($rSS in $remoteSupportSessionResult)
        {
            $params = @{
                Name               = 'AzStackHci_Observability_RemoteSupport'
                Title              = 'Observability RemoteSupport Requirement'
                DisplayName        = 'Observability RemoteSupport Requirement'
                Severity           = 'CRITICAL'
                Description        = 'Test to check observability RemoteSupport requirement is met'
                Tags               = @{
                    'OperationType' = $OperationType
                }
                Remediation        = 'Active remote session terminal is open, please contact Microsoft Support'
                TargetResourceID   = $rSS.Source
                TargetResourceName = $rSS.Source
                TargetResourceType = $rSS.HardwareType | Get-Unique
                Timestamp          = [datetime]::UtcNow
                Status             = $rSS.Status
                AdditionalData     = $rSS
                HealthCheckSource  = $ENV:EnvChkrId
            }
            $remoteSupportSessionSet += New-AzStackHciResultObject @params
        }
        return $remoteSupportSessionSet
    }
    catch
    {
        throw $_
    }
}

Export-ModuleMember -Function Test-ObservabilityVolume
Export-ModuleMember -Function Test-LogCollection
Export-ModuleMember -Function Test-RemoteSupport
# SIG # Begin signature block
# MIIoRQYJKoZIhvcNAQcCoIIoNjCCKDICAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCD6TEVHJZ3SOOQu
# eBpeJOlFSXVmX+gOpN6kiUl+RBdQy6CCDXYwggX0MIID3KADAgECAhMzAAAEBGx0
# Bv9XKydyAAAAAAQEMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMjQwOTEyMjAxMTE0WhcNMjUwOTExMjAxMTE0WjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQC0KDfaY50MDqsEGdlIzDHBd6CqIMRQWW9Af1LHDDTuFjfDsvna0nEuDSYJmNyz
# NB10jpbg0lhvkT1AzfX2TLITSXwS8D+mBzGCWMM/wTpciWBV/pbjSazbzoKvRrNo
# DV/u9omOM2Eawyo5JJJdNkM2d8qzkQ0bRuRd4HarmGunSouyb9NY7egWN5E5lUc3
# a2AROzAdHdYpObpCOdeAY2P5XqtJkk79aROpzw16wCjdSn8qMzCBzR7rvH2WVkvF
# HLIxZQET1yhPb6lRmpgBQNnzidHV2Ocxjc8wNiIDzgbDkmlx54QPfw7RwQi8p1fy
# 4byhBrTjv568x8NGv3gwb0RbAgMBAAGjggFzMIIBbzAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQU8huhNbETDU+ZWllL4DNMPCijEU4w
# RQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEW
# MBQGA1UEBRMNMjMwMDEyKzUwMjkyMzAfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzci
# tW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEG
# CCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQu
# Y29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0
# MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBAIjmD9IpQVvfB1QehvpC
# Ge7QeTQkKQ7j3bmDMjwSqFL4ri6ae9IFTdpywn5smmtSIyKYDn3/nHtaEn0X1NBj
# L5oP0BjAy1sqxD+uy35B+V8wv5GrxhMDJP8l2QjLtH/UglSTIhLqyt8bUAqVfyfp
# h4COMRvwwjTvChtCnUXXACuCXYHWalOoc0OU2oGN+mPJIJJxaNQc1sjBsMbGIWv3
# cmgSHkCEmrMv7yaidpePt6V+yPMik+eXw3IfZ5eNOiNgL1rZzgSJfTnvUqiaEQ0X
# dG1HbkDv9fv6CTq6m4Ty3IzLiwGSXYxRIXTxT4TYs5VxHy2uFjFXWVSL0J2ARTYL
# E4Oyl1wXDF1PX4bxg1yDMfKPHcE1Ijic5lx1KdK1SkaEJdto4hd++05J9Bf9TAmi
# u6EK6C9Oe5vRadroJCK26uCUI4zIjL/qG7mswW+qT0CW0gnR9JHkXCWNbo8ccMk1
# sJatmRoSAifbgzaYbUz8+lv+IXy5GFuAmLnNbGjacB3IMGpa+lbFgih57/fIhamq
# 5VhxgaEmn/UjWyr+cPiAFWuTVIpfsOjbEAww75wURNM1Imp9NJKye1O24EspEHmb
# DmqCUcq7NqkOKIG4PVm3hDDED/WQpzJDkvu4FrIbvyTGVU01vKsg4UfcdiZ0fQ+/
# V0hf8yrtq9CkB8iIuk5bBxuPMIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkq
# hkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x
# EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv
# bjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5
# IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEwOTA5WjB+MQswCQYDVQQG
# EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG
# A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQg
# Q29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
# CgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+laUKq4BjgaBEm6f8MMHt03
# a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc6Whe0t+bU7IKLMOv2akr
# rnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4Ddato88tt8zpcoRb0Rrrg
# OGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+lD3v++MrWhAfTVYoonpy
# 4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nkkDstrjNYxbc+/jLTswM9
# sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6A4aN91/w0FK/jJSHvMAh
# dCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmdX4jiJV3TIUs+UsS1Vz8k
# A/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL5zmhD+kjSbwYuER8ReTB
# w3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zdsGbiwZeBe+3W7UvnSSmn
# Eyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3T8HhhUSJxAlMxdSlQy90
# lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS4NaIjAsCAwEAAaOCAe0w
# ggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRIbmTlUAXTgqoXNzcitW2o
# ynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYD
# VR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBDuRQFTuHqp8cx0SOJNDBa
# BgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2Ny
# bC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3JsMF4GCCsG
# AQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3dy5taWNyb3NvZnQuY29t
# L3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3J0MIGfBgNV
# HSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEFBQcCARYzaHR0cDovL3d3
# dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1hcnljcHMuaHRtMEAGCCsG
# AQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkAYwB5AF8AcwB0AGEAdABl
# AG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn8oalmOBUeRou09h0ZyKb
# C5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7v0epo/Np22O/IjWll11l
# hJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0bpdS1HXeUOeLpZMlEPXh6
# I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/KmtYSWMfCWluWpiW5IP0
# wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvyCInWH8MyGOLwxS3OW560
# STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBpmLJZiWhub6e3dMNABQam
# ASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJihsMdYzaXht/a8/jyFqGa
# J+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYbBL7fQccOKO7eZS/sl/ah
# XJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbSoqKfenoi+kiVH6v7RyOA
# 9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sLgOppO6/8MO0ETI7f33Vt
# Y5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtXcVZOSEXAQsmbdlsKgEhr
# /Xmfwb1tbWrJUnMTDXpQzTGCGiUwghohAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw
# EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN
# aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp
# Z25pbmcgUENBIDIwMTECEzMAAAQEbHQG/1crJ3IAAAAABAQwDQYJYIZIAWUDBAIB
# BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO
# MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIPnTm2z/LLvbijcA7bbhz9XU
# W99PYggBCJMaLtvsnxgEMEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A
# cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB
# BQAEggEANAnGrrebI931nLv0dZC0RURGYQNurxsMDpKNn2zWgNRSEg93tuL7JwWm
# NB/7C5y3fQAPfvkUxAl6T7ao2vMkkwEDkd8Fqmxv5Yy2M3gfHc24poaQCvfVoHlG
# Bc2veBtKevFl/3Lxuu1wT891crX301f7WSYNmVp8hMBscnUWS0tWT3luZpxjEv3L
# /Y8porv5JZWfN3572vt/E6PplXX7rVMRXX1EsUtiUgu/JeDhkroJeJ0i6ae2BcNC
# 9p4lbiX/Ro7vOq2vkWqWSJOTOl5QKkBzJ2EWSDMc4aJVnBYOYwzPbkaT2NQfgf7l
# X4+1zgENPJx5PT/zC4XpcmMkYTkqN6GCF68wgherBgorBgEEAYI3AwMBMYIXmzCC
# F5cGCSqGSIb3DQEHAqCCF4gwgheEAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFZBgsq
# hkiG9w0BCRABBKCCAUgEggFEMIIBQAIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl
# AwQCAQUABCAO2u14NM1zykeZRFkHCfcpYf+TIc4AgMHdX7Btohej8AIGZ0nWNWtV
# GBIyMDI0MTIwNDE1MDM0Ni45NFowBIACAfSggdmkgdYwgdMxCzAJBgNVBAYTAlVT
# MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK
# ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVs
# YW5kIE9wZXJhdGlvbnMgTGltaXRlZDEnMCUGA1UECxMeblNoaWVsZCBUU1MgRVNO
# OjM2MDUtMDVFMC1EOTQ3MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBT
# ZXJ2aWNloIIR/jCCBygwggUQoAMCAQICEzMAAAH3WCB1BMr7wvQAAQAAAfcwDQYJ
# KoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x
# EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv
# bjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwHhcNMjQw
# NzI1MTgzMTA2WhcNMjUxMDIyMTgzMTA2WjCB0zELMAkGA1UEBhMCVVMxEzARBgNV
# BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv
# c29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMkTWljcm9zb2Z0IElyZWxhbmQgT3Bl
# cmF0aW9ucyBMaW1pdGVkMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046MzYwNS0w
# NUUwLUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2Uw
# ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDQ50dME2ibr+5cpoQo/2s8
# hORPpDEXXW2PMHQ2TVvIOk+sVMeFreHHBJ1NyvxjRreToRCXCrLpE7PjZ7RHl4Nb
# 50KhBqmKkMgVQ5ineg26rBf/F6kBMSRjXszJcXHqtBbY1xZQlbdCjYC4nQc61uVK
# ki1Bk8aYecaqS38MHjkXDGTpWhK/E1xAqEoROS7Ou3xToNFxxCbUV2GY8qAPOBx8
# M8zmj4afNuIy7rLTr0DgQeYsyaR5xKRW8GZxnxWfMUdMOQYt2mcNXkVeNU5sCBtI
# zRyephIZ9GntUYcFGrKixy9HhtxD4JX2kONsnpLmtmfW4DyFGGPT0ezfcdF6+3ih
# YBVgYi2ASwb4GsJhumBYwMQhWcCA9kSI8BojzAEZ6YTh94SS7PtMDCCREFxTMuBD
# i68+pEPUD4mS3br6kOpZhKfQwDyPTNpxCT2r8C9yI9cP0i3Z7P6aoTOAVFGwkYu1
# x/0eSy8rwmx3ojnMVKGWqLlunN/Vjg06I06HlDBbWki8DmKuVqXuoWGQB555mqai
# nz643FlfEUJAbdHezmldbz0WIKH2uZetWo4LCBxcUglABCSWUqwj5Qmoar2uZEAE
# nPmUcpMViYXBwznYpZaM3HfPqh3DPaH6zFrF7BOh70aq0PHf9pT7Ko1FwHzDS1Jd
# R/7KU3i6TnEcSkunH5k02wIDAQABo4IBSTCCAUUwHQYDVR0OBBYEFN9GpDM/eb09
# la4t/Wnz+Z4V+SaYMB8GA1UdIwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8G
# A1UdHwRYMFYwVKBSoFCGTmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMv
# Y3JsL01pY3Jvc29mdCUyMFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBs
# BggrBgEFBQcBAQRgMF4wXAYIKwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0
# LmNvbS9wa2lvcHMvY2VydHMvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUy
# MDIwMTAoMSkuY3J0MAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUH
# AwgwDgYDVR0PAQH/BAQDAgeAMA0GCSqGSIb3DQEBCwUAA4ICAQA3RqNp8gt4vpJA
# gwgwBczVB3rFqhyLaY6ulHy8pbLJOwvdvzcDtcYuIBtDFOuqde9VZZ42y3lhAPyx
# o75ROA4sl1N19QAOEtegr5GXCN+d2KYglP0wf21RhcvMlcqFkzT2i4/A2yufxg4s
# il0CLlM/I3wKXXU4ZlKU/2vwme+iZbTQCgng+X2uWDQbmVxCScBeodr2dB1anVnF
# eo137QmwqaVHy1wA1ffcKUz02doKUkTEtAeIp4dRRa2rIsyXrlNbrBEzteUXtj49
# OcLx241afi4ueD4439nf0Y7qoGPsgRnGirijdq8SH1trjdRTpODNVloGbxVoDTBL
# BR7+mqlM5gVY3rZcveCX8kLanN8g/E/rpd9EsjFp+MFVebwpUOfZwwv0i9ErTaz3
# jVjn5FHiBIA6EuJBDoDTdU1G6n6ykxrST5dM8CL7ZowfnFrVmNv8ry71/0zTlTT9
# tQwlckM/77KxakltVEOIcbuzNpxr6vceJQ+NAnJCXY2I5xhMZX8NwussIErbMbnT
# cUZvTg3kp/XReADAVpeWh3kH14qH3k+dcrHYs0GAvAbzlqeWGEbHEFDmYWwkaQGf
# Q9k+0DNnJ+v3qrHOmnakf0MklyMoIOsyZnOJdrOlrlVU3foI7WQNTgAGRJhNc4zx
# GYle5CbuZQXdtaaP6GMAlvinPqFPlTCCB3EwggVZoAMCAQICEzMAAAAVxedrngKb
# SZkAAAAAABUwDQYJKoZIhvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQI
# EwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3Nv
# ZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmlj
# YXRlIEF1dGhvcml0eSAyMDEwMB4XDTIxMDkzMDE4MjIyNVoXDTMwMDkzMDE4MzIy
# NVowfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcT
# B1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UE
# AxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwggIiMA0GCSqGSIb3DQEB
# AQUAA4ICDwAwggIKAoICAQDk4aZM57RyIQt5osvXJHm9DtWC0/3unAcH0qlsTnXI
# yjVX9gF/bErg4r25PhdgM/9cT8dm95VTcVrifkpa/rg2Z4VGIwy1jRPPdzLAEBjo
# YH1qUoNEt6aORmsHFPPFdvWGUNzBRMhxXFExN6AKOG6N7dcP2CZTfDlhAnrEqv1y
# aa8dq6z2Nr41JmTamDu6GnszrYBbfowQHJ1S/rboYiXcag/PXfT+jlPP1uyFVk3v
# 3byNpOORj7I5LFGc6XBpDco2LXCOMcg1KL3jtIckw+DJj361VI/c+gVVmG1oO5pG
# ve2krnopN6zL64NF50ZuyjLVwIYwXE8s4mKyzbnijYjklqwBSru+cakXW2dg3viS
# kR4dPf0gz3N9QZpGdc3EXzTdEonW/aUgfX782Z5F37ZyL9t9X4C626p+Nuw2TPYr
# bqgSUei/BQOj0XOmTTd0lBw0gg/wEPK3Rxjtp+iZfD9M269ewvPV2HM9Q07BMzlM
# jgK8QmguEOqEUUbi0b1qGFphAXPKZ6Je1yh2AuIzGHLXpyDwwvoSCtdjbwzJNmSL
# W6CmgyFdXzB0kZSU2LlQ+QuJYfM2BjUYhEfb3BvR/bLUHMVr9lxSUV0S2yW6r1AF
# emzFER1y7435UsSFF5PAPBXbGjfHCBUYP3irRbb1Hode2o+eFnJpxq57t7c+auIu
# rQIDAQABo4IB3TCCAdkwEgYJKwYBBAGCNxUBBAUCAwEAATAjBgkrBgEEAYI3FQIE
# FgQUKqdS/mTEmr6CkTxGNSnPEP8vBO4wHQYDVR0OBBYEFJ+nFV0AXmJdg/Tl0mWn
# G1M1GelyMFwGA1UdIARVMFMwUQYMKwYBBAGCN0yDfQEBMEEwPwYIKwYBBQUHAgEW
# M2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5
# Lmh0bTATBgNVHSUEDDAKBggrBgEFBQcDCDAZBgkrBgEEAYI3FAIEDB4KAFMAdQBi
# AEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV
# 9lbLj+iiXGJo0T2UkFvXzpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3Js
# Lm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAx
# MC0wNi0yMy5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8v
# d3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2
# LTIzLmNydDANBgkqhkiG9w0BAQsFAAOCAgEAnVV9/Cqt4SwfZwExJFvhnnJL/Klv
# 6lwUtj5OR2R4sQaTlz0xM7U518JxNj/aZGx80HU5bbsPMeTCj/ts0aGUGCLu6WZn
# OlNN3Zi6th542DYunKmCVgADsAW+iehp4LoJ7nvfam++Kctu2D9IdQHZGN5tggz1
# bSNU5HhTdSRXud2f8449xvNo32X2pFaq95W2KFUn0CS9QKC/GbYSEhFdPSfgQJY4
# rPf5KYnDvBewVIVCs/wMnosZiefwC2qBwoEZQhlSdYo2wh3DYXMuLGt7bj8sCXgU
# 6ZGyqVvfSaN0DLzskYDSPeZKPmY7T7uG+jIa2Zb0j/aRAfbOxnT99kxybxCrdTDF
# NLB62FD+CljdQDzHVG2dY3RILLFORy3BFARxv2T5JL5zbcqOCb2zAVdJVGTZc9d/
# HltEAY5aGZFrDZ+kKNxnGSgkujhLmm77IVRrakURR6nxt67I6IleT53S0Ex2tVdU
# CbFpAUR+fKFhbHP+CrvsQWY9af3LwUFJfn6Tvsv4O+S3Fb+0zj6lMVGEvL8CwYKi
# excdFYmNcP7ntdAoGokLjzbaukz5m/8K6TT4JDVnK+ANuOaMmdbhIurwJ0I9JZTm
# dHRbatGePu1+oDEzfbzL6Xu/OHBE0ZDxyKs6ijoIYn/ZcGNTTY3ugm2lBRDBcQZq
# ELQdVTNYs6FwZvKhggNZMIICQQIBATCCAQGhgdmkgdYwgdMxCzAJBgNVBAYTAlVT
# MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK
# ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVs
# YW5kIE9wZXJhdGlvbnMgTGltaXRlZDEnMCUGA1UECxMeblNoaWVsZCBUU1MgRVNO
# OjM2MDUtMDVFMC1EOTQ3MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBT
# ZXJ2aWNloiMKAQEwBwYFKw4DAhoDFQBvbwoMb/Fds0GOYzv+erDduCsQ5qCBgzCB
# gKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQH
# EwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNV
# BAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMA0GCSqGSIb3DQEBCwUA
# AgUA6vrsAzAiGA8yMDI0MTIwNDE0NTYwM1oYDzIwMjQxMjA1MTQ1NjAzWjB3MD0G
# CisGAQQBhFkKBAExLzAtMAoCBQDq+uwDAgEAMAoCAQACAgvsAgH/MAcCAQACAhJu
# MAoCBQDq/D2DAgEAMDYGCisGAQQBhFkKBAIxKDAmMAwGCisGAQQBhFkKAwKgCjAI
# AgEAAgMHoSChCjAIAgEAAgMBhqAwDQYJKoZIhvcNAQELBQADggEBADy7g1Ft4KjG
# EKR3Skbk6kil1pITw/C1Itr9fU8egH4ujgD4E1THlUjkfX7QAYyfrWGj2L64cmDD
# 2KJcGzH1cSMAQL1iTfqn8c3+TqGJZh2ErSsfJpghzsRgXPfhG8FSYvtzkzi8QTdq
# AQx4BI9FsWr9fRbhcZkHdPyRXKgdHSAHAKAuytR9LKnzp8e9BQtzCBOxCNH4WDXF
# 7W1TmoC09wppMpj7+wOidqMwxYNwWimXEJMLi6c6d63bfDKAYH7MBBksXQyiC0Vs
# CuHE7NcmkrSv0f+kqAbs8sg5NYFjmUy9IH1CC4J1UKOFLhP2F78g07dKzIZ59tPD
# MVOzawBVtX8xggQNMIIECQIBATCBkzB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMK
# V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0
# IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0Eg
# MjAxMAITMwAAAfdYIHUEyvvC9AABAAAB9zANBglghkgBZQMEAgEFAKCCAUowGgYJ
# KoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMC8GCSqGSIb3DQEJBDEiBCBRiTBgG+kW
# Viw/AxmuuE0txydLSsG8gwjNeGSANlhXkDCB+gYLKoZIhvcNAQkQAi8xgeowgecw
# geQwgb0EICHamNprdxrR5xi6G7rS5gc/8gqc9t51tVAnlKggflniMIGYMIGApH4w
# fDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1Jl
# ZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMd
# TWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAAH3WCB1BMr7wvQAAQAA
# AfcwIgQgIc8W9VizmhmgA4vnWKuQl8LcBsNos66vH+abRw8CprEwDQYJKoZIhvcN
# AQELBQAEggIASrhlJQG1Uxwv755XP4dBk2Kc4Q7kbOO7RkGL7qCJb8D2MhluV6Wu
# FUBDkasGulGonoaZC/IpUf6k94ZKaHvEMNTtQweEGgkfxZwMs+OzHEnsYBkIs9CO
# 317FKQaFC6doyw3TIrv4N0xjumOP36L7E98ULDyKLqN12uzNyzqbsqNq5Gmgze+U
# ALNjhBjXHWI9ZXpC2fE3uw1xATjtCz35ohtmhMUNvrdEz+lQ0I9R49QPIpD/tYLt
# vVBZIyf5/3E6YKh5VF+M5Wv/kaYnrk8lSXoqMU9BGPn9WrMCR/iAzUELeDPoFc17
# KtnN7jcJsDP3dnkyLMq626I0DvVgSAWm02DFY0qieuI/1hXtVC0VkR1jaj1EEDC+
# kXg2AXeiJPjVnCh/nW4R7Mmf39fkMgP6TVdoNBrKXenssH2zBoKMlrOBIOixtStv
# 5bFXWpbyeDCs8FnVs/LAjMB3U4YWjrQeFiIt+FANhUc6D8nt9Kx5lJUB4ELhFlEB
# 3D3Y2CNdXqk9YqF5idpeC04Z6Re+1ArQazsbNIDrO8ZVx03sbJsVJSskULPZYhxw
# NYFC6j6qEtCONsG8YfWZj9misz8R0Zue8JVlXBNmFT+1I0op8jRuvLaXYgDZDycP
# iIhx7GpoJKkL218qQDBTdHAMPCeOXNVs+AUKsIDJRO/RYDx3Ml35lwg=
# SIG # End signature block