AzStackHciSBEHealth/AzStackHciSBEHealth.psm1

<#############################################################
 # #
 # Copyright (C) Microsoft Corporation. All rights reserved. #
 # #
 #############################################################>

 $MetaData = @{
    "OperationType" =  @("Deployment", "Update", "PreUpdate", "PostUpdate")
    "UIName" = 'Azure Stack HCI SBE Health'
    "UIDescription" = 'Check SBE Health requirements'
}

Import-Module $PSScriptRoot\..\AzStackHci.EnvironmentChecker.Reporting.psm1 -Force -DisableNameChecking -Global
Import-Module $PSScriptRoot\AzStackHci.SBEHealth.Helpers.psm1 -Force -DisableNameChecking -Global

function Test-AzStackHciSBEHealth
{
    param (
        [parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        $Parameters,

        [parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        $OperationType,

        [parameter(Mandatory = $false)]
        [switch]
        $FailFast
    )
    try
    {
        $env:EnvChkrOp = $OperationType
        Trace-Execution "Starting SBE Health validation for $OperationType"
        $allResult = @()
        if ($OperationType -eq 'Deployment')
        {
            Trace-Execution "Checking SBE Configuration"
            $sbeConfigNuget = Get-ASArtifactPath -NugetName "Microsoft.AzureStack.SBEConfiguration"
            # Only run tests if the SBE Package is 4.1.x.x or later
            $sbeVerMatch = "[4-9]\.[1-9]\."
            if ($sbeConfigNuget -notmatch $sbeVerMatch)
            {
                $detailedMessage = "SBE Configuration nuget '$($sbeConfigNuget)' does not support this test. Must be version 4.1.x.x or later."
                Trace-Execution $detailedMessage
                return (New-Object -TypeName PsObject -Property @{Result = 'INFORMATIONAL'; FailedResult = $null; ExecutionDetail = $detailedMessage})
            }
            $sbeMetadataPath = Join-Path -Path $sbeConfigNuget -ChildPath "content"
            $oemMetadataXmlPath = Join-Path -Path $sbeMetadataPath -ChildPath "oemMetadata.xml"
            if ($false -eq (Test-Path -Path $oemMetadataXmlPath))
            {
                throw "Unable to locate oemMetadata.xml file at $oemMetadataXmlPath"
            }
            [xml]$oemMetadata = Get-Content -Path $oemMetadataXmlPath
            $sbeVersion = $oemMetadata.UpdatePackageManifest.UpdateInfo.Version

            Trace-Execution "Getting SBE Package source local path"
            $sbeSourcePath = $Parameters.Roles["SBE"].PublicConfiguration.PublicInfo.SBEContentPaths.SBESeedNodePath
            if ($null -eq $sbeSourcePath)
            {
                $detailedMessage = "SBE Seed Node Path property did not get populated. Please retry the deployment."
                Trace-Execution $detailedMessage
                throw $detailedMessage
            }
        }
        elseif ($OperationType -eq 'PreUpdate')
        {
            # First, get the installed SBE info - we might need to fall back to this SBE
            $sbeVersion = '1.0'
            $sbeSourcePath = [System.Environment]::GetEnvironmentVariable("SBEInstalledContent", "Machine")
            $sbeMetadataPath = [System.Environment]::GetEnvironmentVariable("SBEInstalledMetadata", "Machine")
            if ($null -ne $sbeMetadataPath)
            {
                $oemMetadataXmlPath = Join-Path -Path $sbeMetadataPath -ChildPath "oemMetadata.xml"
                if ($true -eq (Test-Path -Path $oemMetadataXmlPath))
                {
                    [xml]$oemMetadata = Get-Content -Path $oemMetadataXmlPath
                    $sbeVersion = $oemMetadata.UpdatePackageManifest.UpdateInfo.Version
                }
            }
            $sbeInstalled = $false

            $installSBEResult = New-SBEHealthResultObject -TestName 'Test-Installed-SBE-Env-Vars' -TargetName $env:ComputerName -Status 'SUCCESS' -Description "Validate Installed SBE Env Vars"

            if (($null -ne $sbeSourcePath) -and ($null -ne $sbeMetadataPath) -and ($null -ne $sbeVersion))
            {
                $detailedMessage = "Detected SBE $sbeVersion is installed. TBD if we will use this or a newer version for checks."
                $sbeInstalled = $true
            }
            elseif ((($null -eq $sbeSourcePath) -and ($null -eq $sbeMetadataPath) -and ($null -eq $sbeVersion)) -or ($sbeVersion -eq '1.0'))
            {
                Trace-Execution "SBE ENV vars - content: [$sbeSourcePath], metadata: [$sbeMetadataPath], sbeVersion [$sbeVersion]"
                $detailedMessage = "No SBE installed. May be skipping checks if this isn't an update including an SBE."
            }
            else
            {
                $detailedMessage = "Inconsistent SBE ENV vars!! content: [$sbeSourcePath], metadata: [$sbeMetadataPath], sbeVersion [$sbeVersion]"
                $installSBEResult.Severity = 'WARNING'
                $installSBEResult.Remediation = "Update to latest available Solution Builder Extension to restore consistent SBE state."
            }
            Trace-Execution $detailedMessage
            $installSBEResult.AdditionalData.Detail = $detailedMessage
            $allResult += $installSBEResult

            # Figure out if this is a period "system" healthcheck or "preUpdate" HealthCheck
            $runtimeParameters = $Parameters.RunInformation['RuntimeParameter']
            $packagePath = $runtimeParameters["UpdatePackagePath"]
            $updateVersion = $runtimeParameters["UpdateVersion"]
            if ($null -eq $packagePath)
            {
                Trace-Execution "System HealthCheck detected."
                if ($sbeInstalled)
                {
                    Trace-Execution -Message "Using installed SBE - content: [$sbeSourcePath], metadata: [$sbeMetadataPath], sbeVersion [$sbeVersion]"
                }
                else
                {
                    # Early exit, there is no SBE to test with
                    return $allResult
                }
            }
            else
            {
                Trace-Execution "Update package @: [$packagePath]"
                Trace-Execution "Update version @: [$updateVersion]"

                # The package path is expected to be a full path to an update package file (e.g. a self-
                # extracting .exe, metadata.xml, or oemMetadata.xml file). The content is in the same dir.

                # Also paths from runtime params often have extra \\ in them like: C:\\ClusterStorage\\Infrastructure_1\\Shares\\SU1_Infrastructure_1\\Updates\\Packages\Solution99.9999.9.11\metadata.xml
                $updatePackageBaseDir = (Split-Path $packagePath -Parent).Replace( '\\', '\')
                if ($updateVersion -match '^\d{2}\.\d{4}\..*')
                {
                    # Version is something like 10.2405.1.13 and indicates this is a solution update
                    $potentialSBEPath = "$updatePackageBaseDir\SBE"
                    if (Test-Path -Path $potentialSBEPath)
                    {
                        $sbeSourcePath = $potentialSBEPath
                        $sbeMetadataPath = $potentialSBEPath
                        $oemMetadataXmlPath = Join-Path -Path $sbeMetadataPath -ChildPath "oemMetadata.xml"
                        if ($false -eq (Test-Path -Path $oemMetadataXmlPath))
                        {
                            throw "Unable to locate oemMetadata.xml file at $oemMetadataXmlPath"
                        }
                        [xml]$oemMetadata = Get-Content -Path $oemMetadataXmlPath
                        $sbeVersion = $oemMetadata.UpdatePackageManifest.UpdateInfo.Version
                        Trace-Execution "Using SBE from Solution Update - content: [$sbeSourcePath], metadata: [$sbeMetadataPath], sbeVersion [$sbeVersion]"
                    }
                    elseif ($sbeInstalled)
                    {
                        Trace-Execution -Message "Using installed SBE - content: [$sbeSourcePath], metadata: [$sbeMetadataPath], sbeVersion [$sbeVersion]"
                    }
                    else
                    {
                        # Early exit, this is a Solution update w/o an SBE and there is no SBE installed
                        Trace-Execution "Skipping tests as no SBE is installed and thre is no SBE from this Solution Update"
                        return $allResult
                    }
                }
                elseif ($updateVersion -match '\d\.\d\.\d{4}\..*')
                {
                    $sbeSourcePath = $updatePackageBaseDir
                    $sbeMetadataPath = $updatePackageBaseDir
                    $sbeVersion = $updateVersion
                    Trace-Execution "Using SBE from SBE-only Update - content: [$sbeSourcePath], metadata: [$sbeMetadataPath], sbeVersion [$sbeVersion]"
                }
            }
            $sbeVersionFormatted = "1.0"
            if ([System.Version]::TryParse($sbeVersion,[ref]$sbeVersionFormatted))
            {
                if($sbeVersionFormatted -lt [System.Version]"4.1")
                {
                    $versionResult = New-SBEHealthResultObject -TestName 'Test-Version-Supports-$OperationType-Tests' -TargetName $env:ComputerName -Status 'SUCCESS' -Description "Validate SBE Version supports $OperationType type tests."
                    $detailedMessage = "Skipping tests. SBE '$OperationType' type Health Checks are only supported with version 4.1.x.x or later (SBE version was $sbeVersion)."
                    Trace-Execution $detailedMessage
                    $versionResult.AdditionalData.Detail = $detailedMessage
                    $allResult += $versionResult
                    return $allResult
                }
            }
        }
        elseif ($OperationType -eq 'PreAddNode')
        {
            # TODO : Future placeholder for when AddNode tests are implemented
            Trace-Execution "SBE Health validation for $OperationType has not been implemented yet"
            return (New-Object -TypeName PsObject -Property @{Result = 'INFORMATIONAL'; FailedResult = $null; ExecutionDetail = 'SBE Health validation has been skipped as this OperationType is not yet supported.'})
        }
        else
        {
            Trace-Execution "OperationType $OperationType is not implemented"
            return (New-Object -TypeName PsObject -Property @{Result = 'INFORMATIONAL'; FailedResult = $null; ExecutionDetail = 'SBE Health validation has been skipped as this OperationType is not yet supported.'})
        }
        Trace-Execution "Getting PSSessions for all hosts"
        $psSession = [EnvironmentValidator]::NewPsSessionAllHosts($Parameters)

        # Update mapping as needed each time envChecker adds a new OperationType that we want to translate to an SBE healthCheck "tag"
        $envCheckerToSBEPrecheckTagMap = @{
            PreUpdate = "Update"
            PostUpdate = "Update"
            PreAddNode = "AddNode"
        }
        $tag = $OperationType
        if ($null -ne $envCheckerToSBEPrecheckTagMap.$OperationType)
        {
            # EnvChecker has invented some new test types that SBE doesn't support - use mapping table to translate to SBE equivalent
            $tag = $envCheckerToSBEPrecheckTagMap.$OperationType
        }
        $params = @{
            PsSession = $psSession
            PassThru = $true
            OutputPath = "$($env:LocalRootFolderPath)\MASLogs\"
            Tag = $tag
            SBESourcePath = $sbeSourcePath
            SBEMetadataPath = $sbeMetadataPath
            SBEVersion = $sbeVersion
            ECEParameters = $Parameters
        }
        # Run SBE Health Check
        [array]$partnerResults = Invoke-AzStackHciSBEHealthValidation @params
        if ($null -ne $partnerResults -and $partnerResults.Count -gt 0)
        {
            $allResult += $partnerResults
        }
        return [EnvironmentValidator]::ParseResult($allResult, 'SBEHealth', $FailFast)
    }
    catch
    {
        Trace-Execution "Validator failed. $PSItem"
        Trace-Execution "$($PSItem.ScriptStackTrace)"
        throw $PSItem
    }
    finally
    {
        if ($psSession)
        {
            $psSession | Microsoft.PowerShell.Core\Remove-PSSession
        }
    }
}

Export-ModuleMember -Function Test-AzStackHciSBEHealth -Variable MetaData
# SIG # Begin signature block
# MIIoRgYJKoZIhvcNAQcCoIIoNzCCKDMCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBDT9NfMuR/hhfe
# ouqqbelwnQh6W9wEZgYN3VGZ9afptKCCDXYwggX0MIID3KADAgECAhMzAAADrzBA
# DkyjTQVBAAAAAAOvMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMjMxMTE2MTkwOTAwWhcNMjQxMTE0MTkwOTAwWjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQDOS8s1ra6f0YGtg0OhEaQa/t3Q+q1MEHhWJhqQVuO5amYXQpy8MDPNoJYk+FWA
# hePP5LxwcSge5aen+f5Q6WNPd6EDxGzotvVpNi5ve0H97S3F7C/axDfKxyNh21MG
# 0W8Sb0vxi/vorcLHOL9i+t2D6yvvDzLlEefUCbQV/zGCBjXGlYJcUj6RAzXyeNAN
# xSpKXAGd7Fh+ocGHPPphcD9LQTOJgG7Y7aYztHqBLJiQQ4eAgZNU4ac6+8LnEGAL
# go1ydC5BJEuJQjYKbNTy959HrKSu7LO3Ws0w8jw6pYdC1IMpdTkk2puTgY2PDNzB
# tLM4evG7FYer3WX+8t1UMYNTAgMBAAGjggFzMIIBbzAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQURxxxNPIEPGSO8kqz+bgCAQWGXsEw
# RQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEW
# MBQGA1UEBRMNMjMwMDEyKzUwMTgyNjAfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzci
# tW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEG
# CCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQu
# Y29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0
# MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBAISxFt/zR2frTFPB45Yd
# mhZpB2nNJoOoi+qlgcTlnO4QwlYN1w/vYwbDy/oFJolD5r6FMJd0RGcgEM8q9TgQ
# 2OC7gQEmhweVJ7yuKJlQBH7P7Pg5RiqgV3cSonJ+OM4kFHbP3gPLiyzssSQdRuPY
# 1mIWoGg9i7Y4ZC8ST7WhpSyc0pns2XsUe1XsIjaUcGu7zd7gg97eCUiLRdVklPmp
# XobH9CEAWakRUGNICYN2AgjhRTC4j3KJfqMkU04R6Toyh4/Toswm1uoDcGr5laYn
# TfcX3u5WnJqJLhuPe8Uj9kGAOcyo0O1mNwDa+LhFEzB6CB32+wfJMumfr6degvLT
# e8x55urQLeTjimBQgS49BSUkhFN7ois3cZyNpnrMca5AZaC7pLI72vuqSsSlLalG
# OcZmPHZGYJqZ0BacN274OZ80Q8B11iNokns9Od348bMb5Z4fihxaBWebl8kWEi2O
# PvQImOAeq3nt7UWJBzJYLAGEpfasaA3ZQgIcEXdD+uwo6ymMzDY6UamFOfYqYWXk
# ntxDGu7ngD2ugKUuccYKJJRiiz+LAUcj90BVcSHRLQop9N8zoALr/1sJuwPrVAtx
# HNEgSW+AKBqIxYWM4Ev32l6agSUAezLMbq5f3d8x9qzT031jMDT+sUAoCw0M5wVt
# CUQcqINPuYjbS1WgJyZIiEkBMIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkq
# 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
# /Xmfwb1tbWrJUnMTDXpQzTGCGiYwghoiAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw
# EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN
# aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp
# Z25pbmcgUENBIDIwMTECEzMAAAOvMEAOTKNNBUEAAAAAA68wDQYJYIZIAWUDBAIB
# BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO
# MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIEC7eOi/LzjoPYAbCso2CE8g
# RLI2QJaq+BvJVDHNR8zJMEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A
# cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB
# BQAEggEApqmiOsKP/7iLW0dnTs29OBfazIkst6SpA0W6QgzSBP4nJKmma7jABnye
# 0OdmDcs2Tdguh0EWAF6etzFerlWE/8QMMah1JVU3aNmx9sCPr4DxjtEqEo+mlCSk
# jiiiLjVVxDetdT3Rlm79nkUbYb49KAE9aGD2gVniF+nBpKXKoe29dKiTWpEkgw1O
# XPtl16U8j/UVhTIq6WLjBecDeT5dFSj+ByG4t0K1bLLKPEg0IssKTO+KRG20OdKA
# lMRSsiaTZrLNPW7EkrZ7LFNjqkOsuNwGgX0dooUML1jna/s2B9sT241uU1Zvr0GE
# FoofD3qZ9Eg4b9rm2O/jSirsEkxXp6GCF7AwghesBgorBgEEAYI3AwMBMYIXnDCC
# F5gGCSqGSIb3DQEHAqCCF4kwgheFAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFaBgsq
# hkiG9w0BCRABBKCCAUkEggFFMIIBQQIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl
# AwQCAQUABCCt/2VSacNRaijeYZ9p6dAUbp3qZUKkUkdjOljZLWJPcwIGZut9Knz2
# GBMyMDI0MTAwOTAxMTUxMS4zMDNaMASAAgH0oIHZpIHWMIHTMQswCQYDVQQGEwJV
# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJl
# bGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJzAlBgNVBAsTHm5TaGllbGQgVFNTIEVT
# Tjo2RjFBLTA1RTAtRDk0NzElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAg
# U2VydmljZaCCEf4wggcoMIIFEKADAgECAhMzAAAB/Bigr8xpWoc6AAEAAAH8MA0G
# CSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u
# MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp
# b24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMB4XDTI0
# MDcyNTE4MzExNFoXDTI1MTAyMjE4MzExNFowgdMxCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9w
# ZXJhdGlvbnMgTGltaXRlZDEnMCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOjZGMUEt
# MDVFMC1EOTQ3MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNl
# MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp1DAKLxpbQcPVYPHlJHy
# W7W5lBZjJWWDjMfl5WyhuAylP/LDm2hb4ymUmSymV0EFRQcmM8BypwjhWP8F7x4i
# O88d+9GZ9MQmNh3jSDohhXXgf8rONEAyfCPVmJzM7ytsurZ9xocbuEL7+P7EkIwo
# OuMFlTF2G/zuqx1E+wANslpPqPpb8PC56BQxgJCI1LOF5lk3AePJ78OL3aw/Ndlk
# vdVl3VgBSPX4Nawt3UgUofuPn/cp9vwKKBwuIWQEFZ837GXXITshd2Mfs6oYfxXE
# tmj2SBGEhxVs7xERuWGb0cK6afy7naKkbZI2v1UqsxuZt94rn/ey2ynvunlx0R6/
# b6nNkC1rOTAfWlpsAj/QlzyM6uYTSxYZC2YWzLbbRl0lRtSz+4TdpUU/oAZSB+Y+
# s12Rqmgzi7RVxNcI2lm//sCEm6A63nCJCgYtM+LLe9pTshl/Wf8OOuPQRiA+stTs
# g89BOG9tblaz2kfeOkYf5hdH8phAbuOuDQfr6s5Ya6W+vZz6E0Zsenzi0OtMf5RC
# a2hADYVgUxD+grC8EptfWeVAWgYCaQFheNN/ZGNQMkk78V63yoPBffJEAu+B5xlT
# PYoijUdo9NXovJmoGXj6R8Tgso+QPaAGHKxCbHa1QL9ASMF3Os1jrogCHGiykfp1
# dKGnmA5wJT6Nx7BedlSDsAkCAwEAAaOCAUkwggFFMB0GA1UdDgQWBBSY8aUrsUaz
# hxByH79dhiQCL/7QdjAfBgNVHSMEGDAWgBSfpxVdAF5iXYP05dJlpxtTNRnpcjBf
# BgNVHR8EWDBWMFSgUqBQhk5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3Bz
# L2NybC9NaWNyb3NvZnQlMjBUaW1lLVN0YW1wJTIwUENBJTIwMjAxMCgxKS5jcmww
# bAYIKwYBBQUHAQEEYDBeMFwGCCsGAQUFBzAChlBodHRwOi8vd3d3Lm1pY3Jvc29m
# dC5jb20vcGtpb3BzL2NlcnRzL01pY3Jvc29mdCUyMFRpbWUtU3RhbXAlMjBQQ0El
# MjAyMDEwKDEpLmNydDAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUF
# BwMIMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQsFAAOCAgEAT7ss/ZAZ0bTa
# FsrsiJYd//LQ6ImKb9JZSKiRw9xs8hwk5Y/7zign9gGtweRChC2lJ8GVRHgrFkBx
# ACjuuPprSz/UYX7n522JKcudnWuIeE1p30BZrqPTOnscD98DZi6WNTAymnaS7it5
# qAgNInreAJbTU2cAosJoeXAHr50YgSGlmJM+cN6mYLAL6TTFMtFYJrpK9TM5Ryh5
# eZmm6UTJnGg0jt1pF/2u8PSdz3dDy7DF7KDJad2qHxZORvM3k9V8Yn3JI5YLPuLs
# o2J5s3fpXyCVgR/hq86g5zjd9bRRyyiC8iLIm/N95q6HWVsCeySetrqfsDyYWStw
# L96hy7DIyLL5ih8YFMd0AdmvTRoylmADuKwE2TQCTvPnjnLk7ypJW29t17Yya4V+
# Jlz54sBnPU7kIeYZsvUT+YKgykP1QB+p+uUdRH6e79Vaiz+iewWrIJZ4tXkDMmL2
# 1nh0j+58E1ecAYDvT6B4yFIeonxA/6Gl9Xs7JLciPCIC6hGdliiEBpyYeUF0ohZF
# n7NKQu80IZ0jd511WA2bq6x9aUq/zFyf8Egw+dunUj1KtNoWpq7VuJqapckYsmvm
# mYHZXCjK1Eus7V1I+aXjrBYuqyM9QpeFZU4U01YG15uWwUCaj0uZlah/RGSYMd84
# y9DCqOpfeKE6PLMk7hLnhvcOQrnxP6kwggdxMIIFWaADAgECAhMzAAAAFcXna54C
# m0mZAAAAAAAVMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UE
# CBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9z
# b2Z0IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZp
# Y2F0ZSBBdXRob3JpdHkgMjAxMDAeFw0yMTA5MzAxODIyMjVaFw0zMDA5MzAxODMy
# MjVaMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQH
# EwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNV
# BAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMIICIjANBgkqhkiG9w0B
# AQEFAAOCAg8AMIICCgKCAgEA5OGmTOe0ciELeaLL1yR5vQ7VgtP97pwHB9KpbE51
# yMo1V/YBf2xK4OK9uT4XYDP/XE/HZveVU3Fa4n5KWv64NmeFRiMMtY0Tz3cywBAY
# 6GB9alKDRLemjkZrBxTzxXb1hlDcwUTIcVxRMTegCjhuje3XD9gmU3w5YQJ6xKr9
# cmmvHaus9ja+NSZk2pg7uhp7M62AW36MEBydUv626GIl3GoPz130/o5Tz9bshVZN
# 7928jaTjkY+yOSxRnOlwaQ3KNi1wjjHINSi947SHJMPgyY9+tVSP3PoFVZhtaDua
# Rr3tpK56KTesy+uDRedGbsoy1cCGMFxPLOJiss254o2I5JasAUq7vnGpF1tnYN74
# kpEeHT39IM9zfUGaRnXNxF803RKJ1v2lIH1+/NmeRd+2ci/bfV+AutuqfjbsNkz2
# K26oElHovwUDo9Fzpk03dJQcNIIP8BDyt0cY7afomXw/TNuvXsLz1dhzPUNOwTM5
# TI4CvEJoLhDqhFFG4tG9ahhaYQFzymeiXtcodgLiMxhy16cg8ML6EgrXY28MyTZk
# i1ugpoMhXV8wdJGUlNi5UPkLiWHzNgY1GIRH29wb0f2y1BzFa/ZcUlFdEtsluq9Q
# BXpsxREdcu+N+VLEhReTwDwV2xo3xwgVGD94q0W29R6HXtqPnhZyacaue7e3Pmri
# Lq0CAwEAAaOCAd0wggHZMBIGCSsGAQQBgjcVAQQFAgMBAAEwIwYJKwYBBAGCNxUC
# BBYEFCqnUv5kxJq+gpE8RjUpzxD/LwTuMB0GA1UdDgQWBBSfpxVdAF5iXYP05dJl
# pxtTNRnpcjBcBgNVHSAEVTBTMFEGDCsGAQQBgjdMg30BATBBMD8GCCsGAQUFBwIB
# FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL0RvY3MvUmVwb3NpdG9y
# eS5odG0wEwYDVR0lBAwwCgYIKwYBBQUHAwgwGQYJKwYBBAGCNxQCBAweCgBTAHUA
# YgBDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU
# 1fZWy4/oolxiaNE9lJBb186aGMQwVgYDVR0fBE8wTTBLoEmgR4ZFaHR0cDovL2Ny
# bC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMvTWljUm9vQ2VyQXV0XzIw
# MTAtMDYtMjMuY3JsMFoGCCsGAQUFBwEBBE4wTDBKBggrBgEFBQcwAoY+aHR0cDov
# L3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXRfMjAxMC0w
# Ni0yMy5jcnQwDQYJKoZIhvcNAQELBQADggIBAJ1VffwqreEsH2cBMSRb4Z5yS/yp
# b+pcFLY+TkdkeLEGk5c9MTO1OdfCcTY/2mRsfNB1OW27DzHkwo/7bNGhlBgi7ulm
# ZzpTTd2YurYeeNg2LpypglYAA7AFvonoaeC6Ce5732pvvinLbtg/SHUB2RjebYIM
# 9W0jVOR4U3UkV7ndn/OOPcbzaN9l9qRWqveVtihVJ9AkvUCgvxm2EhIRXT0n4ECW
# OKz3+SmJw7wXsFSFQrP8DJ6LGYnn8AtqgcKBGUIZUnWKNsIdw2FzLixre24/LAl4
# FOmRsqlb30mjdAy87JGA0j3mSj5mO0+7hvoyGtmW9I/2kQH2zsZ0/fZMcm8Qq3Uw
# xTSwethQ/gpY3UA8x1RtnWN0SCyxTkctwRQEcb9k+SS+c23Kjgm9swFXSVRk2XPX
# fx5bRAGOWhmRaw2fpCjcZxkoJLo4S5pu+yFUa2pFEUep8beuyOiJXk+d0tBMdrVX
# VAmxaQFEfnyhYWxz/gq77EFmPWn9y8FBSX5+k77L+DvktxW/tM4+pTFRhLy/AsGC
# onsXHRWJjXD+57XQKBqJC4822rpM+Zv/Cuk0+CQ1ZyvgDbjmjJnW4SLq8CdCPSWU
# 5nR0W2rRnj7tfqAxM328y+l7vzhwRNGQ8cirOoo6CGJ/2XBjU02N7oJtpQUQwXEG
# ahC0HVUzWLOhcGbyoYIDWTCCAkECAQEwggEBoYHZpIHWMIHTMQswCQYDVQQGEwJV
# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJl
# bGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJzAlBgNVBAsTHm5TaGllbGQgVFNTIEVT
# Tjo2RjFBLTA1RTAtRDk0NzElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAg
# U2VydmljZaIjCgEBMAcGBSsOAwIaAxUATkEpJXOaqI2wfqBsw4NLVwqYqqqggYMw
# gYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYD
# VQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQsF
# AAIFAOqvr74wIhgPMjAyNDEwMDgxMzE4NTRaGA8yMDI0MTAwOTEzMTg1NFowdzA9
# BgorBgEEAYRZCgQBMS8wLTAKAgUA6q+vvgIBADAKAgEAAgIaLQIB/zAHAgEAAgIU
# sjAKAgUA6rEBPgIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAow
# CAIBAAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBCwUAA4IBAQAEAnaF4fTA
# XxZBtpscWKV1QQe1WFCg7AcIhB/9zWVZZMCwsFTuszZztk9qXqFlSeqvCaovRP2c
# 1U1r/NgBpID9fgeFizzz1IV7NX0TGgTrGqPQ7geHwE4CRXFhQKpdUBNk3Il8Y0x+
# dzvUx4IIyAViC36PkZmGRwbHYIPFaxeLJiK1ziJaUyf3m55c4m8mhX/u7RcVDiC/
# txcIDBTPc2dGj2ZSnSaQrSzEGt5zcYVX/C/tPA+PAJa/cDNW1xp3uWNQbrvfjRAD
# D7RIpOhV5gAaQ7aodtCR6gNLqu4oiGzyr2cZg5Cx8rU/rp9MzhOOPDCPo251+UEK
# Ou8SFhsjmUtJMYIEDTCCBAkCAQEwgZMwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgT
# Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29m
# dCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENB
# IDIwMTACEzMAAAH8GKCvzGlahzoAAQAAAfwwDQYJYIZIAWUDBAIBBQCgggFKMBoG
# CSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0BCQQxIgQgC1hC+OPG
# Rw0PrUJqAPxqgnRMSbnv1nm5cjQfT5G3AXwwgfoGCyqGSIb3DQEJEAIvMYHqMIHn
# MIHkMIG9BCCVQq+Qu+/h/BOVP4wweUwbHuCUhh+T7hq3d5MCaNEtYjCBmDCBgKR+
# MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS
# ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMT
# HU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAB/Bigr8xpWoc6AAEA
# AAH8MCIEIMxYfNmHhx9iKzrkDVNqTQiVCk7BuKPik5Q9vehPX2zcMA0GCSqGSIb3
# DQEBCwUABIICACR0mMIS5g+mC3NzpGKop/Bvlzq1PD/Pw4D54UaXNdLYWOMj9Qyu
# 9y65QZC6F35BG7zUppzLHUROCCdaBsaBnuxnPWYQ1E/ucBZCosDZFVF5WfzgYtYu
# MLuCXda80wLPXc6Dk86bFldN1qVPA2cfavteUxtngtcQxG6xSrzPXh8t83TEKhNE
# kSBYiFwt9BrfWKqkWGUQR34y0qu5TksUXsTfzhNEl9j6O1LHqy0VrtQgvcQBU3Qs
# U0Q80N4REFku9rR4N2lZJ9/8OdOZc37ZnKKJtymzHIlYJCmGQOc1ccKdBzlXl+a/
# GXG1cva8KZq/+UXy9mK1mycwACOmbfU+R4ELrC6b1vK88fXMRG6n+PyjUnxZMHs4
# KKsuJjNVIRmQZEfyL41Na+7L+qQ8uX+VvTNla0TXAV3R31EJrR31bqxrzzYMBehB
# EfdQBlcdOqq1ZpCoUO8AMTTf1T0wgU7/XHQ8oVJYkZbrH6Ol2Vofe35VpcFqSbBD
# bYYeRXoT/ywqe/PEu8zDByzIPowlFETWUW5uUb015K1xPJqPgkRjQmpra7Rc4HXs
# w9BJ3lzZnd5U6Mw1AUYRSRywPl4co0g4njotDgVoPJ68+rtLBA8V6GDe1Lp73GSA
# S+7AELgkbEe5FUx87Nogr5w7eg0zYGZDqeiH6GTq0y+nnoBKFWGB0nPG
# SIG # End signature block