scenarios/LLDP/LLDP.ps1

param
(
    [object]$EventInfo
)

Write-Host $EventInfo.EventArgs
Write-Host $EventInfo.EventArgs.Data

Add-Type -Path "$PSScriptRoot\..\..\Microsoft.NetworkHud.Module.dll" -ErrorAction SilentlyContinue
$dictionary = [Microsoft.NetworkHud.Module.SharedConcurrentDictionary]::GetInstance("LLDP")

$config = (Import-PowerShellDataFile "$PSScriptRoot\..\Config.psd1").PublishLLDP
Import-LocalizedData -BindingVariable "LocMessage" -FileName "LocalizationMessages" -BaseDirectory $PSScriptRoot\..\..\

$logName = 'NetworkHud'
$script:AdapterName = ""

$helpersPath = Join-Path -Path $PSScriptRoot -ChildPath '..\helpers'
Import-Module "$helpersPath\helper.HealthFaults.psm1"
Import-Module "$helpersPath\helper.ClusterRegistry.psm1"
Add-FaultApi
Add-HciClusterRegistryUtils

#Used to trigger downstream scenarios
function Write-CustomTriggerEvent {
    param (
        [int] $EventID,
        [string] $PropertyName,
        [string] $Adapters
    )

    $logName = 'NetworkHud'
    Write-EventLog  -LogName  $logName `
        -Source    $($config.PropertyChange.Source) `
        -EventID   $EventID `
        -EntryType $($config.PropertyChange.EntryType) `
        -Message   $($LocMessage.$($config.PropertyChange.MessageKey) -f $PropertyName, $Adapters)

}

function Write-CustomFault{
    param(
        [string] $MissingProperty
    )

    $hostname = hostname
    $faultDescription = $LocMessage.$($config.FaultDescriptionKey) -f $script:AdapterName, $hostname, $MissingProperty
    $faultAction = $LocMessage.$($config.FaultActionKey)
    $ID = "$($env:ComputerName): $($script:AdapterName)"

    [Microsoft.NetworkHud.Module.HciHealthUtils]::HciModifyFault($config.EntityType, $ID, "", "", "", $FRApiActionModify, $config.FaultType, $HealthUrgencyUnHealthy, "", $faultDescription, $faultAction, 0) | Out-Null
    [Microsoft.NetworkHud.Module.HciHealthUtils]::HciModifyRelationship($config.EntityType, $ID, "", "", "", $FRApiActionModify, "Microsoft.Health.EntityType.Server", $env:COMPUTERNAME, "", "", "", $HealthRelationshipGroupKey, $HealthUrgencyUnHealthy, $HealthRelationshipCollection, 0) | Out-Null
  
}

function Get-TlvChassisIdData
{

    param (
        [byte[]] $chasBytes
    )

    if(-not $chasBytes){
        return "Unknown"
    }

    # get the Chassis ID Subtype
    [int]$chasIdType = "0x$($chasBytes[0])"

    switch -Regex ($chasIdType)
    {
        "[1-3]"{return ( [System.Text.Encoding]::ASCII.GetString($chasBytes[1..($chasBytes.Length - 1)]) )}
        "4"{return ("{0:X2}:{1:X2}:{2:X2}:{3:X2}:{4:X2}:{5:X2}" -f $chasBytes[1..($chasBytes.Length - 1)])}
        Default
        {
            Write-Warning "Unknown Chassis ID type: $chasIdType"
            return "Unknown"
        }
    }
}

function Get-TlvPortIdData
{
    param (
        [byte[]] $portBytes
    )

    if(-not $portBytes){
        return "Unknown"
    }

    # get the Chassis ID Subtype
    [int]$portIdType = "0x$($portBytes[0])"

    Write-Verbose "Get-TlvPortId - Port Type: $portIdType"

    switch -Regex ($portIdType)
    {
        "[1-2]|[5]"{return ( [System.Text.Encoding]::ASCII.GetString($portBytes[1..($portBytes.Length - 1)]) )}
        "3"{return ("{0:X2}:{1:X2}:{2:X2}:{3:X2}:{4:X2}:{5:X2}" -f $portBytes[1..($portBytes.Length - 1)])}
        Default
        {
            Write-Warning "Unknown Port ID type: $portIdType"
            return "Unknown"
        }
    }
}

function Get-TlvVLANIdData{
    param (
        [object[]] $Data
    )

    if(-not $Data){
        Write-CustomFault -MissingProperty "VLANID"
        return "Unknown"
    }

    $list = @()

    foreach($i in $Data){
        #Bitshift the first byte by 8, add the second byte
        $list+= [convert]::ToSingle($i.data[0]) * [math]::Pow(2,8) + [convert]::ToSingle($i.data[1])
    }

    return $list
}

function Get-MaxFrameSize{
    param (
        [object[]] $Data
    )

    if(-not $Data){
        Write-CustomFault -MissingProperty "MaxFrameSize"
        return "Unknown"
    }

    return $Data
}

function Get-NativeVLAN{
    param (
        [object[]] $Data
    )

    if(-not $Data){
        Write-CustomFault -MissingProperty "NativeVLAN"
        return "Unknown"
    }

    return $Data
}

function Get-LinkAggregationData{
    param (
        [object[]] $Data
    )

    if(-not $Data){
        Write-CustomFault -MissingProperty "LinkAggregation"
        return "Unknown"
    }

    return $Data
}

function Get-ETSConfigurationData{
    param (
        [object[]] $Data
    )

    if(-not $Data){
        Write-CustomFault -MissingProperty "ETSConfiguration"
        return "Unknown"
    }

    return $Data
}

function Get-PFCConfigurationData{
    param (
        [object[]] $Data
    )

     if(-not $Data){
         Write-CustomFault -MissingProperty "PFCConfiguration"
        return "Unknown"
    }

     return $Data[1]
}

#Takes in adapter obj, retunes hashtable, may require helpers
function Parse-Adapter {
    param (
        [object] $Adapter
    )

    $adapterInfo = Get-NetAdapter -Name $Adapter.Name
    $script:AdapterName = $Adapter.Name
    $LLDPInfo = (Get-NetLldpAgent -NetAdapterName $Adapter.Name).Neighbor
    $TLVinfo = $LLDPInfo.Tlvs

    $output = New-Object "System.Collections.Generic.Dictionary[System.String,System.String]"
    $output["InterfaceName"] = $Adapter.Name
    $output["InterfaceIndex"] = $adapterInfo.InterfaceIndex
    $output["Destination"] = "01:80:c2:00:00:0e"
    $output["SourceMac"] = $LLDPInfo.PhysicalAddress
    $output["EtherType"] = "0x88CC"
    $output["ChassisID"] = Get-TlvChassisIdData ($TLVinfo | Where-Object TLVType -eq 1).data
    $output["PortID"] =  Get-TlvPortIdData ($TLVinfo | Where-Object TLVType -eq 2).data
    $output["TimeToLive"] = ($TLVinfo | Where-Object TLVType -eq 3).data
    $output["VLANID"] =  Get-TlvVLANIdData -data ($TLVinfo | Where-Object { $_.TLVType -eq 127 -and $_.OuiSubtype -eq 3 -and $_.TlvName -notlike "Link Aggregation"})
    $output["MaxFrameSize"] =  Get-MaxFrameSize -data ($TLVinfo | Where-Object { $_.TLVType -eq 127 -and $_.OuiSubtype -eq 4 })
    $output["NativeVLAN"] =  Get-NativeVLAN -data ($TLVinfo | Where-Object { $_.TLVType -eq 127 -and $_.OuiSubtype -eq 1 })
    $output["LinkAggregation"] =  Get-LinkAggregationData -data ($TLVinfo | Where-Object { $_.TLVType -eq 127 -and $_.OuiSubtype -eq 7 })
    $output["ETS"] =  Get-ETSConfigurationData -data ($TLVinfo | Where-Object { $_.TLVType -eq 127 -and $_.OuiSubtype -eq 9 })
    $output["PFC"] =  Get-PFCConfigurationData -data ($TLVinfo | Where-Object { $_.TLVType -eq 127 -and $_.OuiSubtype -eq 11 }).Data

    return $output
}

#CluderDB open
$clusterKey = OpenCluster
$keyHandle = CreateAndOpenKey -keyHandle $clusterKey -KeyName "NetworkHUD\\LLDP"

#Parse and concurrent dictionsary
$nodeID = Hostname
$propertiesChanged  = @{}
$adapterList = Get-NetAdapter | Where-Object {($_.MediaType -eq '802.3') -and ($_.Status -eq 'Up')}
$adapterTable = New-Object "System.Collections.Generic.Dictionary[System.String,System.Collections.Generic.Dictionary[System.String,System.String]]"
foreach($Adapter in $adapterList){

    #Skip over the virtual adapters
    if($Adapter.InterfaceDescription -match "Hyper-V"){
        continue
    }

    #Enable LLDP
    Enable-NetLLDPAgent -NetAdapterName $Adapter.name

    #Check if it exists in the dictionsary
    $existingtable = $dictionary[$Adapter.name]
    if(-not $existingtable){

        Write-Debug "No existing data found in memory for adapter {$($Adapter.name)}"

        $clusterTable = (Get-ItemProperty -Path "HKLM:\Cluster\NetworkHud\LLDP").$nodeID
        if($clusterTable){
            $existingtable = $clusterTable
        }
        else{
            Write-Debug "No existing data found in the clusterDB for adapter {$($Adapter.name)}"
            $dictionary[$Adapter.Name] = New-Object "System.Collections.Generic.Dictionary[System.String,System.String]"
            $existingtable = $dictionary[$Adapter.Name]
        }

    }

    $parsedTable = Parse-Adapter -Adapter $Adapter
    $adapterTable[$Adapter.name] = $parsedTable
    $dictionary[$Adapter.Name] = $parsedTable
    
    foreach($property in $parsedTable.keys){

        $parsedvalue = $parsedTable[$property]
        $existingvalue = $existingtable[$property]

        if($parsedvalue -ne $existingvalue){
            Write-Debug "different value found for the property {$property}, Parsed value: {$parsedvalue} is different from the existing value {$existingvalue} for adapter {$($Adapter.Name)}. Triggering a downstream scenario"
            if(-not $propertiesChanged[$property]) {$propertiesChanged[$property] = New-Object -TypeName "System.Collections.ArrayList" }
            $propertiesChanged[$property] += $Adapter.name 
        }
    }
}

#Write into clusterDB and close handle
SetValue -KeyHandle $keyHandle -KeyName $nodeID -Data ($dictionary | ConvertTo-Json).toString()
Write-Debug "Dictionary $(($dictionary | ConvertTo-Json).toString())"
CloseKey -KeyHandle $keyHandle
CloseCluster -ClusterHandle $clusterKey

#Trigger consumer scenarios
foreach ($property in $propertiesChanged.keys){
    switch ($property) {
        ChassisID { Write-CustomTriggerEvent -EventID 10001 -PropertyName "ChassisID" -Adapters ($propertiesChanged[$property] -join ", ")}
        EtherHead{ Write-CustomTriggerEvent -EventID 10002 -PropertyName "EtherHead" -Adapters ($propertiesChanged[$property] -join ", ")}
        VLANID { Write-CustomTriggerEvent -EventID 10003 -PropertyName "VLANID" -Adapters ($propertiesChanged[$property] -join ", ")}
        NativeVLAN { Write-CustomTriggerEvent -EventID 10004 -PropertyName "NativeVLAN" -Adapters ($propertiesChanged[$property] -join ", ")}
        LinkAggregation { Write-CustomTriggerEvent -EventID 10005 -PropertyName "LinkAggregation" -Adapters ($propertiesChanged[$property] -join ", ")}
        ETS { Write-CustomTriggerEvent -EventID 10006 -PropertyName "ETSConfiguration" -Adapters ($propertiesChanged[$property] -join ", ")}
        PFC { Write-CustomTriggerEvent -EventID 10007 -PropertyName "PFCConfiguration" -Adapters ($propertiesChanged[$property] -join ", ")}
        Default { }
    }
}




# SIG # Begin signature block
# MIIoLQYJKoZIhvcNAQcCoIIoHjCCKBoCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCd3eifcgSV+u+1
# 0ooD6Iud1C3lDECFcPzeuKgx/z/YNaCCDXYwggX0MIID3KADAgECAhMzAAADTrU8
# esGEb+srAAAAAANOMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMjMwMzE2MTg0MzI5WhcNMjQwMzE0MTg0MzI5WjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQDdCKiNI6IBFWuvJUmf6WdOJqZmIwYs5G7AJD5UbcL6tsC+EBPDbr36pFGo1bsU
# p53nRyFYnncoMg8FK0d8jLlw0lgexDDr7gicf2zOBFWqfv/nSLwzJFNP5W03DF/1
# 1oZ12rSFqGlm+O46cRjTDFBpMRCZZGddZlRBjivby0eI1VgTD1TvAdfBYQe82fhm
# WQkYR/lWmAK+vW/1+bO7jHaxXTNCxLIBW07F8PBjUcwFxxyfbe2mHB4h1L4U0Ofa
# +HX/aREQ7SqYZz59sXM2ySOfvYyIjnqSO80NGBaz5DvzIG88J0+BNhOu2jl6Dfcq
# jYQs1H/PMSQIK6E7lXDXSpXzAgMBAAGjggFzMIIBbzAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUnMc7Zn/ukKBsBiWkwdNfsN5pdwAw
# RQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEW
# MBQGA1UEBRMNMjMwMDEyKzUwMDUxNjAfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzci
# tW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEG
# CCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQu
# Y29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0
# MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBAD21v9pHoLdBSNlFAjmk
# mx4XxOZAPsVxxXbDyQv1+kGDe9XpgBnT1lXnx7JDpFMKBwAyIwdInmvhK9pGBa31
# TyeL3p7R2s0L8SABPPRJHAEk4NHpBXxHjm4TKjezAbSqqbgsy10Y7KApy+9UrKa2
# kGmsuASsk95PVm5vem7OmTs42vm0BJUU+JPQLg8Y/sdj3TtSfLYYZAaJwTAIgi7d
# hzn5hatLo7Dhz+4T+MrFd+6LUa2U3zr97QwzDthx+RP9/RZnur4inzSQsG5DCVIM
# pA1l2NWEA3KAca0tI2l6hQNYsaKL1kefdfHCrPxEry8onJjyGGv9YKoLv6AOO7Oh
# JEmbQlz/xksYG2N/JSOJ+QqYpGTEuYFYVWain7He6jgb41JbpOGKDdE/b+V2q/gX
# UgFe2gdwTpCDsvh8SMRoq1/BNXcr7iTAU38Vgr83iVtPYmFhZOVM0ULp/kKTVoir
# IpP2KCxT4OekOctt8grYnhJ16QMjmMv5o53hjNFXOxigkQWYzUO+6w50g0FAeFa8
# 5ugCCB6lXEk21FFB1FdIHpjSQf+LP/W2OV/HfhC3uTPgKbRtXo83TZYEudooyZ/A
# Vu08sibZ3MkGOJORLERNwKm2G7oqdOv4Qj8Z0JrGgMzj46NFKAxkLSpE5oHQYP1H
# tPx1lPfD7iNSbJsP6LiUHXH1MIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkq
# 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
# Z25pbmcgUENBIDIwMTECEzMAAANOtTx6wYRv6ysAAAAAA04wDQYJYIZIAWUDBAIB
# BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO
# MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIMD+/DgyRMYnKUqvVZmtQb6d
# rdDso2i5eylyDod007GlMEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A
# cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB
# BQAEggEAaViqw/9L9eSBYWRfCSaxsvBP8asEmKo2wby3+OqenEQbKxqu5TOFJcAq
# LGOmL2A+hneHBAtEwRy/gagkk2kZuDbaBOiw8fo8EKMaZ08AD1HQvNa4WYxSmflf
# SxgOs8w3I0AgUtnAYvu2DyF4RyBdzk3NKHHymTtvmM9EFiOc8RwPl0ctxtjGxQIL
# qOooWircBv/QBkplDztjSrWG4zoDMI2hTeu770fQoJDe0NsGSQu0Ty2xnsjBfDDx
# ZANcZ2Cmsvv6zLAqoh3slY/5s7eLi2eAGKGKRiQP1WXY8Rd+XCQbAt3Wk88rZcf6
# BnGXBzghEGNtgaaok1UIN6VwfHLBc6GCF5cwgheTBgorBgEEAYI3AwMBMYIXgzCC
# F38GCSqGSIb3DQEHAqCCF3AwghdsAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFSBgsq
# hkiG9w0BCRABBKCCAUEEggE9MIIBOQIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl
# AwQCAQUABCClcdScinvban5k18mFNN2HApaLgkj4Yn30/Iuwd0IqJQIGZSiNJ3Th
# GBMyMDIzMTAxOTIzNTkxOC45MDRaMASAAgH0oIHRpIHOMIHLMQswCQYDVQQGEwJV
# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1l
# cmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046REMwMC0w
# NUUwLUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2Wg
# ghHtMIIHIDCCBQigAwIBAgITMwAAAdIhJDFKWL8tEQABAAAB0jANBgkqhkiG9w0B
# AQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYD
# VQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAeFw0yMzA1MjUxOTEy
# MjFaFw0yNDAyMDExOTEyMjFaMIHLMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz
# aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENv
# cnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25z
# MScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046REMwMC0wNUUwLUQ5NDcxJTAjBgNV
# BAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggIiMA0GCSqGSIb3DQEB
# AQUAA4ICDwAwggIKAoICAQDcYIhC0QI/SPaT5+nYSBsSdhBPO2SXM40Vyyg8Fq1T
# PrMNDzxChxWUD7fbKwYGSsONgtjjVed5HSh5il75jNacb6TrZwuX+Q2++f2/8CCy
# u8TY0rxEInD3Tj52bWz5QRWVQejfdCA/n6ZzinhcZZ7+VelWgTfYC7rDrhX3TBX8
# 9elqXmISOVIWeXiRK8h9hH6SXgjhQGGQbf2bSM7uGkKzJ/pZ2LvlTzq+mOW9iP2j
# cYEA4bpPeurpglLVUSnGGQLmjQp7Sdy1wE52WjPKdLnBF6JbmSREM/Dj9Z7okxRN
# UjYSdgyvZ1LWSilhV/wegYXVQ6P9MKjRnE8CI5KMHmq7EsHhIBK0B99dFQydL1vd
# uC7eWEjzz55Z/DyH6Hl2SPOf5KZ4lHf6MUwtgaf+MeZxkW0ixh/vL1mX8VsJTHa8
# AH+0l/9dnWzFMFFJFG7g95nHJ6MmYPrfmoeKORoyEQRsSus2qCrpMjg/P3Z9WJAt
# FGoXYMD19NrzG4UFPpVbl3N1XvG4/uldo1+anBpDYhxQU7k1gfHn6QxdUU0TsrJ/
# JCvLffS89b4VXlIaxnVF6QZh+J7xLUNGtEmj6dwPzoCfL7zqDZJvmsvYNk1lcbyV
# xMIgDFPoA2fZPXHF7dxahM2ZG7AAt3vZEiMtC6E/ciLRcIwzlJrBiHEenIPvxW15
# qwIDAQABo4IBSTCCAUUwHQYDVR0OBBYEFCC2n7cnR3ToP/kbEZ2XJFFmZ1kkMB8G
# A1UdIwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8GA1UdHwRYMFYwVKBSoFCG
# Tmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUy
# MFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBsBggrBgEFBQcBAQRgMF4w
# XAYIKwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2Vy
# dHMvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3J0MAwG
# A1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwDgYDVR0PAQH/BAQD
# AgeAMA0GCSqGSIb3DQEBCwUAA4ICAQCw5iq0Ey0LlAdz2PcqchRwW5d+fitNISCv
# qD0E6W/AyiTk+TM3WhYTaxQ2pP6Or4qOV+Du7/L+k18gYr1phshxVMVnXNcdjecM
# tTWUOVAwbJoeWHaAgknNIMzXK3+zguG5TVcLEh/CVMy1J7KPE8Q0Cz56NgWzd9ur
# G+shSDKkKdhOYPXF970Mr1GCFFpe1oXjEy6aS+Heavp2wmy65mbu0AcUOPEn+hYq
# ijgLXSPqvuFmOOo5UnSV66Dv5FdkqK7q5DReox9RPEZcHUa+2BUKPjp+dQ3D4c9I
# H8727KjMD8OXZomD9A8Mr/fcDn5FI7lfZc8ghYc7spYKTO/0Z9YRRamhVWxxrIsB
# N5LrWh+18soXJ++EeSjzSYdgGWYPg16hL/7Aydx4Kz/WBTUmbGiiVUcE/I0aQU2U
# /0NzUiIFIW80SvxeDWn6I+hyVg/sdFSALP5JT7wAe8zTvsrI2hMpEVLdStFAMqan
# FYqtwZU5FoAsoPZ7h1ElWmKLZkXk8ePuALztNY1yseO0TwdueIGcIwItrlBYg1Xp
# Pz1+pMhGMVble6KHunaKo5K/ldOM0mQQT4Vjg6ZbzRIVRoDcArQ5//0875jOUvJt
# Yyc7Hl04jcmvjEIXC3HjkUYvgHEWL0QF/4f7vLAchaEZ839/3GYOdqH5VVnZrUIB
# QB6DTaUILDCCB3EwggVZoAMCAQICEzMAAAAVxedrngKbSZkAAAAAABUwDQYJKoZI
# 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
# MCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOkRDMDAtMDVFMC1EOTQ3MSUwIwYDVQQD
# ExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiMKAQEwBwYFKw4DAhoDFQCJ
# ptLCZsE06NtmHQzB5F1TroFSBqCBgzCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w
# IFBDQSAyMDEwMA0GCSqGSIb3DQEBCwUAAgUA6NudHTAiGA8yMDIzMTAxOTEyMTgz
# N1oYDzIwMjMxMDIwMTIxODM3WjB3MD0GCisGAQQBhFkKBAExLzAtMAoCBQDo250d
# AgEAMAoCAQACAgPvAgH/MAcCAQACAiKPMAoCBQDo3O6dAgEAMDYGCisGAQQBhFkK
# BAIxKDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAIAgEAAgMBhqAwDQYJ
# KoZIhvcNAQELBQADggEBAHCTm0QcG2R9tKeF042bwBbVXq7DQkndwG8kjLhiRouE
# JKDkTU7FMOb2A59zfZIT1CExaPYVVm4QxcL1c4l26MyG8zFLV2EOh/o2GrzHiH6M
# c2dTV5MCjH84gpwdhthUM/0Dmg7p22NNE813lsRMectWCUNMW6FWf8DszQwejC2B
# qmpk/lJDGdfny+w1H4ItT9i5MgWS2HZOurs14Z361K9gen1ZNyOut4vKL/Vl91cK
# YmZzF6hYw6qsj5/rFj+OR4KFhPqs8D5UG1rSYFREfhYIMC2t6mXkZf68WcwpLiqY
# 3Maaq47nuCkwcO1/6fEr8V3qQAVmHFum+qqRxeZwAuYxggQNMIIECQIBATCBkzB8
# MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVk
# bW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1N
# aWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAdIhJDFKWL8tEQABAAAB
# 0jANBglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEE
# MC8GCSqGSIb3DQEJBDEiBCAZCAnQTXYcap+C7MNXozKN2v+vm6F1Tu/4Qzjjq3mY
# fjCB+gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EIMeAIJPf30i9ZbOExU557GwW
# NaLH0Z5s65JFga2DeaROMIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgT
# Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29m
# dCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENB
# IDIwMTACEzMAAAHSISQxSli/LREAAQAAAdIwIgQg9vZU/Xq6Mz7wOGXD4OcEnE3F
# v5+W3IzZXkB3F3zyv/0wDQYJKoZIhvcNAQELBQAEggIAlScHyVcSK0B/7eOH3pS7
# AZyeHQVcwhf1l4H4omDD5AG7idjtB1/kZiT5ZMuboGh7WxMl0mlloxo5sDaDz3n8
# mM+m0YwkOOtrlixeI/RB4dCo/PNlpl3SpiiNcwN0G2yxUbwX4kqhk+HO8+dUfvL6
# 7f4VRLepWx5FLS6v5rg3otQYDXeeFG5cMSZd4MufiED+wjAiLl9Hh42DfEoY8EGY
# aYmuVPAGsme8StcEHlJPGHAJU5X2qfxWpdGlelfhJcGgAQa6t5iQcatseVzEcKDJ
# spQef6qKw+0mZQaQS/7OR6VW37XN20jbi2ZrZDap/MbsxRFTt5rnJrqwxANHXP1+
# xJyS0+uqOEXB5+77rwfJZSWnMsUSICrz3V/o64tV3UqssZhduLFwIpPHv+tvdS3w
# K9Uq1GAnCWvM+/CFtvypqsmldQ+m5AyCXM8BGBXvTBhvCQRYn8afnDLf4TcVziV2
# uZjwUljkHAgy2xuzZV9WAcOBgWzqC04vwPnUFPnF59PKiojgFEEJ4NxGFOpfSo0b
# hRT6p6C2nZQVmhpWzcX3rr+2zo/p5zXMvxervp+Ejt05iD2hEjGwbGLqrsyWzkQp
# fayhKCDUvzk2TJFgH+A02Ud1PoAOxUgdX0D/DDYyaVSCmlD09lzbW/AcZYbgjhbS
# z0meHEX11jSLfEuep7tHE6c=
# SIG # End signature block