AzStackHci.EnvironmentChecker.Operations.psm1

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


function Invoke-EnvironmentValidator
{
    <#
    .SYNOPSIS
    Wrapper command to Test Environment Readiness
 
    .DESCRIPTION
    Wrapper command to test environment readiness, run locally on HCI system.
 
    .PARAMETER OperationType
    Type of Operation.
 
    .PARAMETER WaitForResult
    Wait for action plan to complete.
 
    #>

    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $true)]
        [ValidateSet('AddNode','Deploy','ScaleOut','Upgrade','Update')]
        [String]
        $OperationType,

        [Parameter(Mandatory = $true, ParameterSetName = "AddNode")]
        [ValidateNotNullOrEmpty()]
        [String]
        $Name,

        [Parameter(Mandatory = $false, ParameterSetName = "AddNode", HelpMessage = 'Use Host IPv4 address to simulate AddNode. Omit for RepairNode.')]
        [ValidateNotNullOrEmpty()]
        [String]
        $HostIpv4,

        [Parameter(Mandatory = $false, HelpMessage = 'Start step for the action plan. Use Get-EnvironmentValidatorActionPlan to get the steps.')]
        [int]
        $Start,

        [Parameter(Mandatory = $false, HelpMessage = 'End step for the action plan. Use Get-EnvironmentValidatorActionPlan to get the steps.')]
        [int]
        $End,

        [Parameter(Mandatory = $false, ParameterSetName = "Update")]
        [ValidateNotNullOrEmpty()]
        [String]
        $EnvironmentCheckerResultPath,

        [Parameter(Mandatory = $false, HelpMessage = 'Wait for action plan to conclude. Shows progress if running on the ECE owning node.')]
        [switch]$Wait
    )
    $ErrorActionPreference = 'Stop'

    # Check if we are running Orchestrator as a service or lite
    $Script:OrchestratorSvc = IsOrchestratorSvc

    # Check if action plan is running
    $RunningActionPlans = Get-AzStackHciEnvironmentCheckerActionPlan | Where-Object Status -In 'Running', 'Queued'
    if ($RunningActionPlans)
    {
        throw "Environment Validator action plan in progress"
    }

    if ($OperationType -in 'AddNode', 'ScaleOut' -and [string]::IsNullOrEmpty($Name) -and [string]::IsNullOrEmpty($HostIpv4))
    {
        throw "Parameters missing: Name and HostIpv4 must be supplied when OperationType is AddNode or ScaleOut."
    }

    # Archive Log file
    ArchiveLogFiles

    $ActionType = Get-EnvironmentValidatorActionPlan -OperationType $OperationType
    Write-Verbose "Starting Environment Validator for $OperationType"

    # TODO check if Deploy, Upgrade or AddNode ops are running here?
    $params = @{
        RolePath   = 'Cloud\Infrastructure\EnvironmentValidator'
        ActionType = $ActionType
    }

    # Add needs a node name and IP, take these from the user
    # TO DO: Remove EnvironmentValidatorAddNode
    if ($ActionType -in 'EnvironmentValidatorAddNode','EnvironmentValidatorScaleOut')
    {
        if ([string]::IsNullOrEmpty($Name))
        {
            throw "Parameters missing: Name and HostIpv4 must be supplied when OperationType is AddNode."
        }

        $params += @{
            runtimeParameters = @{
                NodeName  = $Name
            }
        }
        if ($HostIpv4)
        {
            $params += @{
                runtimeParameters = @{
                    IpAddress = $HostIpv4
                }
            }
        }
        else
        {
            Write-Verbose "HostIpv4 not supplied, simiulating RepairNode"
        }
    }

    # Update needs a path to the result file, take this from the user, or set to a test value so we dont overwrite the real one.
    if ($ActionType -eq 'EnvironmentValidatorPreUpdate')
    {
        if ([string]::IsNullOrEmpty($EnvironmentCheckerResultPath))
        {
            $EnvironmentCheckerResultPath = "$($env:LocalRootFolderPath)\MasLogs\Invoke-EnvironmentValidator-EnvironmentCheckerUpdateResult.json"
        }
        $params += @{
            runtimeParameters = @{
                EnvironmentCheckerResultPath  = $EnvironmentCheckerResultPath
            }
        }
    }

    if ($Script:OrchestratorSvc)
    {
        if ($Start -and $End)
        {
            Write-Verbose "Starting Environment Validator for $OperationType from step $StartStep to $EndStep"
            $params += @{
                Start = $Start
                End = $End
            }
        }
        else
        {
            Write-Verbose "Starting Environment Validator for $OperationType"
        }

        Write-Verbose "Invoke-ActionPlanInstance with params: `r`n $($params | Out-String)"

        $ActionPlanInstanceId = Invoke-ActionPlanInstance @params 4>$null
        if (-not $wait) {
            Write-Verbose "To check progress, open a powershell session to $(GetOrchestratorOwner) and call: 'Get-AzStackHciEnvironmentCheckerProgress -Path $($env:LocalRootFolderPath)\MasLogs\AzStackHciEnvironmentProgress.json'"
            Write-Verbose "Run the following to get the action plan progress Get-ActionPlanInstance -actionPlanInstanceID $ActionPlanInstanceId"
        }
    }
    else
    {
        Invoke-EceAction @params
        if (-not $wait) {
            Write-Verbose "To check progress, call: 'Get-AzStackHciEnvironmentCheckerProgress -Path $($env:LocalRootFolderPath)\MasLogs\AzStackHciEnvironmentProgress.json'"
        }
    }

    Start-Sleep -Seconds 5
    for ($i = 0; $i -lt 30; $i++)
    {
        $ActionPlan = Get-AzStackHciEnvironmentCheckerActionPlan | Sort-Object LastModifiedDateTime -Descending | Select-Object -First 1
        switch ($ActionPlan.Status)
        {
            { $_ -in "Failed", "Cancelled" }
            {
                throw "$ActionType is $($actionPlan.Status). Please resume please review: `n $($actionPlan.ProgressAsXml)"
            }
            { $_ -in "Waiting", "Pending" }
            {
                Write-Verbose "$ActionType is currently $($actionPlan.Status)"
            }
            { $_ -eq "Running" }
            {
                Write-Verbose "$ActionType is currently $($actionPlan.Status)"
                if ($Wait)
                {
                    if ($ENV:COMPUTERNAME -eq (GetOrchestratorOwner))
                    {
                        Get-AzStackHciEnvironmentCheckerProgress | Format-Table Result, StartTime, EndTime, DurationSeconds, Name, Description -AutoSize
                    }
                }
                else
                {
                    break outer
                }
            }
            { $_ -eq 'Completed' }
            {
                if ($ENV:COMPUTERNAME -eq (GetOrchestratorOwner))
                {
                    Get-AzStackHciEnvironmentCheckerProgress | Format-Table Result, StartTime, EndTime, DurationSeconds, Name, Description -AutoSize
                }
                break outer
            }
            default
            {
                Write-Warning "$ActionType cannot be started due to unknown status of last run"
                return
            }
        }
        Start-Sleep -Seconds 30
    }
    Write-Verbose "$ActionType $($ActionPlanResult.Status) Action Plan ID: $($ActionPlanInstanceId.Guid)"
}


function Get-AzStackHciEnvironmentCheckerActionPlan
{
    <#
    .SYNOPSIS
        Get all Environment Validator action plan instances
    #>

    [CmdletBinding()]
    param ()

    $validatorLookup = 'EnvironmentValidator*'
    if (IsOrchestratorSvc)
    {
        Get-ActionPlanInstances | Where-Object { $_.ActionTypeName -like $validatorLookup }
    }
    else
    {
        Get-ActionProgress -ActionType $validatorLookup
    }
}

function Get-AzStackHciEnvironmentCheckerProgress
{
    <#
    .SYNOPSIS
        Intended for internal-use to retrieve multi-validator invocation progress using action plan
    #>

    [CmdletBinding()]
    param (
        [Parameter()]
        [string]
        $Path = "$($env:LocalRootFolderPath)\MasLogs\AzStackHciEnvironmentProgress.json"
    )
    Get-Content $Path -ErrorAction SilentlyContinue | ConvertFrom-Json
}

function ArchiveLogFiles
{
    <#
    .SYNOPSIS
        INTERNAL USE ONLY - Archives any supporting files for environment checker.
    #>

    [CmdletBinding()]
    param (
        [Parameter()]
        [string]
        $Path = "$($env:LocalRootFolderPath)\MASLogs"
    )
    # Archive any logs
    'AzStackHciEnvironmentChecker.log', 'AzStackHciEnvironmentProgress.json', 'AzStackHciEnvironmentReport.json', 'AzStackHciEnvironmentReport.xml' | ForEach-Object {
        Get-ChildItem -Path $Path -Filter $PSITEM -ErrorAction SilentlyContinue | ForEach-Object {
            Rename-Item -Path $PSITEM.FullName -NewName ($PSITEM.fullname -replace '(\.)', ('_{0}.' -f (Get-Date -Format yyyyMMdd-HHmmss)))
        }
    }
}

function IsOrchestratorSvc
{
    [CmdletBinding()]
    param ()
    $orchestratorSvc = Get-Service | Where-Object {$_.Name -eq 'ECE Windows Service' -or $_.Name -eq 'Azure Stack HCI Orchestrator Service'}
    if ($orchestratorSvc)
    {
        return $true
    }
    else
    {
        Import-Module C:\CloudDeployment\ECEngine\EnterpriseCloudEngine.psd1 -Force
        return $false
    }
}

function GetOrchestratorOwner
{
    [CmdletBinding()]
    param ()
    try
    {
        Get-ClusterGroup | Where-Object {$_.Name -eq 'ECE Windows Service Cluster Group' -or $_.Name -eq 'Azure Stack HCI Orchestrator Service Cluster Group'} | Select-Object -expandProperty OwnerNode | Select-Object -ExpandProperty Name
    }
    catch
    {
        return $null
    }
}

function Get-EnvironmentValidatorActionPlan
{
    param (
        [Parameter(Mandatory = $true)]
        [ValidateSet('AddNode','Deploy','ScaleOut','Upgrade','Update')]
        [string]
        $OperationType
    )

    $ActionType = switch ($OperationType)
    {
        'Deploy' { 'EnvironmentValidatorFull' }
        'Upgrade' { 'EnvironmentValidatorUpgrade' }
        'AddNode' { 'EnvironmentValidatorPreAddNode' }
        'ScaleOut' { 'EnvironmentValidatorScaleOut' }
        'Update' { 'EnvironmentValidatorPreUpdate' }
        Default {}
    }

    if ($Script:OrchestratorSvc)
    {
        [xml]$xml = (Get-CloudDefinition).CloudDefinitionAsXmlString
    }
    else
    {
        [xml]$xml = (Get-EceConfiguration).xml
    }
    $apDef = $xml.SelectNodes("//Action[@Type='$ActionType']")
    $Steps = $apDef.Steps.Step
    Write-Verbose ($Steps | Out-String) -Verbose
    return $ActionType
}

# If this is a CI environment log module load and session information
try {
    if (Get-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\SQMClient -Name IsCIEnv -ErrorAction SilentlyContinue)
    {
        try {$Query = quser 2>&1  } catch { $Query = "Failed to query session information" }
        try {$of = openfiles /query 2>&1} catch { $of = "Failed to query open files" }
        $pscall = Get-PSCallStack | Format-List * -force | Out-String
        $psinfo = Get-PSHostProcessInfo | Format-List * -force | Out-String
        $str = @"
        --------------------------------------------------------------
        $(Get-Date) : $(whoami)
 
        query user:
        $Query
 
        Open Files:
        $of
 
        CallStack:
        $pscall
 
        psinfo:
        $psinfo
 
        --------------------------------------------------------------
"@

        $str | out-file C:\maslogs\AzStackHciEnvironmentChecker-Debug.log -Force -Append
    }
}
catch {}

# SIG # Begin signature block
# MIIoLQYJKoZIhvcNAQcCoIIoHjCCKBoCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDHK7O93cLdD3n5
# doE6HMF+7PsdKg+IxhEC0YzXAeCPGKCCDXYwggX0MIID3KADAgECAhMzAAADrzBA
# 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
# /Xmfwb1tbWrJUnMTDXpQzTGCGg0wghoJAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw
# EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN
# aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp
# Z25pbmcgUENBIDIwMTECEzMAAAOvMEAOTKNNBUEAAAAAA68wDQYJYIZIAWUDBAIB
# BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO
# MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIKjsragMmeIQbsv5UN9+haoX
# QMBMsF/tC0/Pm98hECCCMEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A
# cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB
# BQAEggEAbjUhyCfVvL1dj5KWhvjMxgB3gztly4x4HKpp+Sk3lQj1T/7ZuG/3VNNi
# WOLzSPRuVycMsvGgcMDbW4QBZYVsjPSZyHe65ukxB/XPCm+yuqZHk7GB6GaoDMh4
# 65i7BquoY4MjXec4/ItgIWkaGUeieLVgipxgrcMrnmzaFXX8MXgzx0IGshyHs32o
# 22ay8J2FbGBFraPw3sVUWKmuNKQ2ajfGwfcDnRkWOeEgD2UAFOCYdx0YXU/PibSZ
# GgxOYk5b4dsL0XpEPgY429fCWG6AUKv2jrkeMcj+/ks2R9RJlqa7hOlZkC0YGJza
# T+zd2tY1ko5xXq3YO6Mr+odeBJDrWaGCF5cwgheTBgorBgEEAYI3AwMBMYIXgzCC
# F38GCSqGSIb3DQEHAqCCF3AwghdsAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFSBgsq
# hkiG9w0BCRABBKCCAUEEggE9MIIBOQIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl
# AwQCAQUABCBiky/1xJii8mHxVrtlQ2mXOZNxrppi4HNT96yi+jj0uAIGZuMiPkNL
# GBMyMDI0MTAwOTAxMTQ1MS4xMjVaMASAAgH0oIHRpIHOMIHLMQswCQYDVQQGEwJV
# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1l
# cmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046RTAwMi0w
# NUUwLUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2Wg
# ghHtMIIHIDCCBQigAwIBAgITMwAAAe4F0wIwspqdpwABAAAB7jANBgkqhkiG9w0B
# AQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYD
# VQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAeFw0yMzEyMDYxODQ1
# NDRaFw0yNTAzMDUxODQ1NDRaMIHLMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz
# aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENv
# cnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25z
# MScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046RTAwMi0wNUUwLUQ5NDcxJTAjBgNV
# BAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggIiMA0GCSqGSIb3DQEB
# AQUAA4ICDwAwggIKAoICAQC+8byl16KEia8xKS4vVL7REOOR7LzYCLXEtWgeqyOV
# lrzuEz+AoCa4tBGESjbHTXECeMOwP9TPeKaKalfTU5XSGjpJhpGx59fxMJoTYWPz
# zD0O2RAlyBmOBBmiLDXRDQJL1RtuAjvCiLulVQeiPI8V7+HhTR391TbC1beSxwXf
# dKJqY1onjDawqDJAmtwsA/gmqXgHwF9fZWcwKSuXiZBTbU5fcm3bhhlRNw5d04Ld
# 15ZWzVl/VDp/iRerGo2Is/0Wwn/a3eGOdHrvfwIbfk6lVqwbNQE11Oedn2uvRjKW
# EwerXL70OuDZ8vLzxry0yEdvQ8ky+Vfq8mfEXS907Y7rN/HYX6cCsC2soyXG3OwC
# tLA7o0/+kKJZuOrD5HUrSz3kfqgDlmWy67z8ZZPjkiDC1dYW1jN77t5iSl5Wp1HK
# Bp7JU8RiRI+vY2i1cb5X2REkw3WrNW/jbofXEs9t4bgd+yU8sgKn9MtVnQ65s6QG
# 72M/yaUZG2HMI31tm9mooH29vPBO9jDMOIu0LwzUTkIWflgd/vEWfTNcPWEQj7fs
# WuSoVuJ3uBqwNmRSpmQDzSfMaIzuys0pvV1jFWqtqwwCcaY/WXsb/axkxB/zCTdH
# SBUJ8Tm3i4PM9skiunXY+cSqH58jWkpHbbLA3Ofss7e+JbMjKmTdcjmSkb5oN8qU
# 1wIDAQABo4IBSTCCAUUwHQYDVR0OBBYEFBCIzT8a2dwgnr37xd+2v1/cdqYIMB8G
# A1UdIwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8GA1UdHwRYMFYwVKBSoFCG
# Tmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUy
# MFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBsBggrBgEFBQcBAQRgMF4w
# XAYIKwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2Vy
# dHMvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3J0MAwG
# A1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwDgYDVR0PAQH/BAQD
# AgeAMA0GCSqGSIb3DQEBCwUAA4ICAQB3ZyAva2EKOWSVpBnYkzX8f8GZjaOs577F
# 9o14Anh9lKy6tS34wXoPXEyQp1v1iI7rJzZVG7rpUznay2n9csfn3p6y7kYkHqtS
# ugCGmTiiBkwhFfSByKPI08MklgvJvKTZb673yGfpFwPjQwZeI6EPj/OAtpYkT7IU
# XqMki1CRMJKgeY4wURCccIujdWRkoVv4J3q/87KE0qPQmAR9fqMNxjI3ZClVxA4w
# iM3tNVlRbF9SgpOnjVo3P/I5p8Jd41hNSVCx/8j3qM7aLSKtDzOEUNs+ZtjhznmZ
# gUd7/AWHDhwBHdL57TI9h7niZkfOZOXncYsKxG4gryTshU6G6sAYpbqdME/+/g1u
# er7VGIHUtLq3W0Anm8lAfS9PqthskZt54JF28CHdsFq/7XVBtFlxL/KgcQylJNni
# a+anixUG60yUDt3FMGSJI34xG9NHsz3BpqSWueGtJhQ5ZN0K8ju0vNVgF+Dv05si
# rPg0ftSKf9FVECp93o8ogF48jh8CT/B32lz1D6Truk4Ezcw7E1OhtOMf7DHgPMWf
# 6WOdYnf+HaSJx7ZTXCJsW5oOkM0sLitxBpSpGcj2YjnNznCpsEPZat0h+6d7ulRa
# WR5RHAUyFFQ9jRa7KWaNGdELTs+nHSlYjYeQpK5QSXjigdKlLQPBlX+9zOoGAJho
# Zfrpjq4nQDCCB3EwggVZoAMCAQICEzMAAAAVxedrngKbSZkAAAAAABUwDQYJKoZI
# hvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw
# DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x
# MjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAy
# MDEwMB4XDTIxMDkzMDE4MjIyNVoXDTMwMDkzMDE4MzIyNVowfDELMAkGA1UEBhMC
# VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV
# BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp
# bWUtU3RhbXAgUENBIDIwMTAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
# AQDk4aZM57RyIQt5osvXJHm9DtWC0/3unAcH0qlsTnXIyjVX9gF/bErg4r25Phdg
# M/9cT8dm95VTcVrifkpa/rg2Z4VGIwy1jRPPdzLAEBjoYH1qUoNEt6aORmsHFPPF
# dvWGUNzBRMhxXFExN6AKOG6N7dcP2CZTfDlhAnrEqv1yaa8dq6z2Nr41JmTamDu6
# GnszrYBbfowQHJ1S/rboYiXcag/PXfT+jlPP1uyFVk3v3byNpOORj7I5LFGc6XBp
# Dco2LXCOMcg1KL3jtIckw+DJj361VI/c+gVVmG1oO5pGve2krnopN6zL64NF50Zu
# yjLVwIYwXE8s4mKyzbnijYjklqwBSru+cakXW2dg3viSkR4dPf0gz3N9QZpGdc3E
# XzTdEonW/aUgfX782Z5F37ZyL9t9X4C626p+Nuw2TPYrbqgSUei/BQOj0XOmTTd0
# lBw0gg/wEPK3Rxjtp+iZfD9M269ewvPV2HM9Q07BMzlMjgK8QmguEOqEUUbi0b1q
# GFphAXPKZ6Je1yh2AuIzGHLXpyDwwvoSCtdjbwzJNmSLW6CmgyFdXzB0kZSU2LlQ
# +QuJYfM2BjUYhEfb3BvR/bLUHMVr9lxSUV0S2yW6r1AFemzFER1y7435UsSFF5PA
# PBXbGjfHCBUYP3irRbb1Hode2o+eFnJpxq57t7c+auIurQIDAQABo4IB3TCCAdkw
# EgYJKwYBBAGCNxUBBAUCAwEAATAjBgkrBgEEAYI3FQIEFgQUKqdS/mTEmr6CkTxG
# NSnPEP8vBO4wHQYDVR0OBBYEFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMFwGA1UdIARV
# MFMwUQYMKwYBBAGCN0yDfQEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly93d3cubWlj
# cm9zb2Z0LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5Lmh0bTATBgNVHSUEDDAK
# BggrBgEFBQcDCDAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMC
# AYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvX
# zpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20v
# cGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYI
# KwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDANBgkqhkiG
# 9w0BAQsFAAOCAgEAnVV9/Cqt4SwfZwExJFvhnnJL/Klv6lwUtj5OR2R4sQaTlz0x
# M7U518JxNj/aZGx80HU5bbsPMeTCj/ts0aGUGCLu6WZnOlNN3Zi6th542DYunKmC
# VgADsAW+iehp4LoJ7nvfam++Kctu2D9IdQHZGN5tggz1bSNU5HhTdSRXud2f8449
# xvNo32X2pFaq95W2KFUn0CS9QKC/GbYSEhFdPSfgQJY4rPf5KYnDvBewVIVCs/wM
# nosZiefwC2qBwoEZQhlSdYo2wh3DYXMuLGt7bj8sCXgU6ZGyqVvfSaN0DLzskYDS
# PeZKPmY7T7uG+jIa2Zb0j/aRAfbOxnT99kxybxCrdTDFNLB62FD+CljdQDzHVG2d
# Y3RILLFORy3BFARxv2T5JL5zbcqOCb2zAVdJVGTZc9d/HltEAY5aGZFrDZ+kKNxn
# GSgkujhLmm77IVRrakURR6nxt67I6IleT53S0Ex2tVdUCbFpAUR+fKFhbHP+Crvs
# QWY9af3LwUFJfn6Tvsv4O+S3Fb+0zj6lMVGEvL8CwYKiexcdFYmNcP7ntdAoGokL
# jzbaukz5m/8K6TT4JDVnK+ANuOaMmdbhIurwJ0I9JZTmdHRbatGePu1+oDEzfbzL
# 6Xu/OHBE0ZDxyKs6ijoIYn/ZcGNTTY3ugm2lBRDBcQZqELQdVTNYs6FwZvKhggNQ
# MIICOAIBATCB+aGB0aSBzjCByzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp
# bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw
# b3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJpY2EgT3BlcmF0aW9uczEn
# MCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOkUwMDItMDVFMC1EOTQ3MSUwIwYDVQQD
# ExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiMKAQEwBwYFKw4DAhoDFQCI
# o6bVNvflFxbUWCDQ3YYKy6O+k6CBgzCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w
# IFBDQSAyMDEwMA0GCSqGSIb3DQEBCwUAAgUA6q/lRzAiGA8yMDI0MTAwODE3MDcx
# OVoYDzIwMjQxMDA5MTcwNzE5WjB3MD0GCisGAQQBhFkKBAExLzAtMAoCBQDqr+VH
# AgEAMAoCAQACAhLBAgH/MAcCAQACAhP+MAoCBQDqsTbHAgEAMDYGCisGAQQBhFkK
# BAIxKDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAIAgEAAgMBhqAwDQYJ
# KoZIhvcNAQELBQADggEBAKezx8tmdeGD4TspH4XUOGj6HD2MOP4q/c1S/J3QigeQ
# 5gPpIkxf2ZQI1MEkZABIrxkFPUaGtDWIuSlsdhWUFlC0jXWY8d8GaaLlO61UZy0F
# kEKDncmDJs8Rl7Hu9RHl2K1lbsduE9rbVQHabCFJp2TzLrxkMVETqIVSrxDll8hh
# AqT2YKYaM5kvqAwiWnBNtGmrquHucjgwBZxwRLZ2/r5U9goDNVlWVsbb7WlkclMu
# Mf6bkaaxOV728TKeKSZBtfuua+baK53n71tgtQ/zLG6hBjsfH6UbTS+/5Nt7qQYk
# 134wOgGuELZMlGNifs7l7kQcVp/O9WaLhoMPIxUlbHUxggQNMIIECQIBATCBkzB8
# MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVk
# bW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1N
# aWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAe4F0wIwspqdpwABAAAB
# 7jANBglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEE
# MC8GCSqGSIb3DQEJBDEiBCCSrk9C6HY6LK+MNnMs4X/tASMUFB+SL7D0N7ji9H3I
# 9DCB+gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EIE9QdxSVhfq+Vdf+DPs+5EIk
# Bz9oCS/OQflHkVRhfjAhMIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgT
# Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29m
# dCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENB
# IDIwMTACEzMAAAHuBdMCMLKanacAAQAAAe4wIgQgcxEzjruxa/dw/WcUqsIqucsd
# ZPtu2mr/Gu9Wk9r+Q9owDQYJKoZIhvcNAQELBQAEggIAnE4aT3nDP68mzw/+aV19
# timZNAxq+Fv7d3XouQ7gLs6Ba7qrXb4W3YX68S2nTX4RkU3KGvJaWC7/K3g2vN3K
# zsqrvjcSgnwtevV+MrxcsessfuehN9SjEXZGSX4+jqDm4Hw2+9DgBX5kUdk+Or+r
# qWZDUE30JRuVdyEyZEgZOohChL6WDogkpWb4rrItJh0wQHabzwkCr64tEv8F2Bpb
# wwPtZnGAiF6EBPjT3tN6l6EtDRVzFS6JtImC4C9MUROdpaUxCVr1yL8KqbWEUjPf
# 9Qdfzibv30vrBuvcscMFQd+Bu9L3SaE3CCdmWImSqx7QGawJ5I/xCHh7ZJ3gqKi9
# TxSMLwR3QSbiWB41YabCoHsSQs2qvQcZs3siS142QJoZrfk5PyLwbRCYJKuz/Z4C
# WRMbpW7CEwt4MsS+rAWgoU/TfuLpvBe1uB0eWiLAo9F9YN2z4OA/6d4XDnvP3m7t
# sSOmQm//7cfvHV30Twow5cSmJPmk8OSvmaf+idTpngSnb7mQv49y3QQO3He7Y+00
# a2o9AklkPH5ZBHhUOyP3SUL+yU9K5GkqHinR/yTiyyKfr/t+onpzWUsKSHqirsIJ
# YIEEE96L8TlcUcBlutdlRCZByu0qD6AVdemGRmsr0tLFnBg34LUpQa4qeTWe7b8n
# ytTu4+YYTHfmVfn/FlpOI8E=
# SIG # End signature block