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/Iuwd0IqJQIGZSivjmxu
# GBMyMDIzMTEwOTIxMzAzOS4wMDdaMASAAgH0oIHRpIHOMIHLMQswCQYDVQQGEwJV
# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1l
# cmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046RTAwMi0w
# NUUwLUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2Wg
# ghHtMIIHIDCCBQigAwIBAgITMwAAAdmcXAWSsINrPgABAAAB2TANBgkqhkiG9w0B
# AQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYD
# VQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAeFw0yMzA2MDExODMy
# NThaFw0yNDAyMDExODMyNThaMIHLMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz
# aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENv
# cnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25z
# MScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046RTAwMi0wNUUwLUQ5NDcxJTAjBgNV
# BAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggIiMA0GCSqGSIb3DQEB
# AQUAA4ICDwAwggIKAoICAQDV6SDN1rgY2305yLdCdUNvHCEE4Z0ucD6CKvL5lA7H
# M81SMkW36RU77UaBL9PScviqfVzE2r2pRbRMtDBMwEx1iaizV2EZsNGGuzeR3XNY
# ObQvJVLaCiBktAZdq75BNFIil+SfdpXgKzVQZiDBJDN50WCADNrrb48Z4Z7/Kvyz
# aD4Gb+aZeCioB2Gg1m53d+6pUTBc3WO5xHZi/rrI/XdnhiE6/bspjpU5aufClIDx
# 0QDq1QRw04adrKhcDWyGL3SaBp/hjN+4JJU7KzvsKWZVdTuXPojnaTwWcHdEGfzx
# iaF30zd8SY4YRUcMGPOQORH1IPwkwwlqQkc0HBkJCQziaXY/IpgMRw/XP4Uv+JBJ
# 8RZGKZN1zRPWT9d5vHGUSmX3m77RKoCfkgSJifIiQi6Fc0OYKS6gZOA7nd4t+liA
# rr9niqeC/UcNOuVrcVC4CbkwfJ2eHkaWh18sUt3UD8QHYLQwn95P+Hm8PZJigr1S
# RLcsm8pOPee7PBbndI/VeKJsmQdjek2aFO9VGnUtzDDBowlhXshswZMMkLJ/4jUz
# QmUBfm+JAH1516E+G02wS7NgzMwmpHWCmAaFdd7DyJIqGa6bcZrR7QALdkwIhVQD
# gzZAuNxqwvh4Ia4ZI5Voyj4b7zWAhmurpwpMpijz+ieeWwf6ZdmysRjR/yZ6UXmG
# awIDAQABo4IBSTCCAUUwHQYDVR0OBBYEFBw6wSlTZ6gFXl05w/s3Ga1f51wnMB8G
# A1UdIwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8GA1UdHwRYMFYwVKBSoFCG
# Tmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUy
# MFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBsBggrBgEFBQcBAQRgMF4w
# XAYIKwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2Vy
# dHMvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3J0MAwG
# A1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwDgYDVR0PAQH/BAQD
# AgeAMA0GCSqGSIb3DQEBCwUAA4ICAQAqNtVYLO61TMuIanC7clt0i+XRRbHwnwNo
# 05Q3s4ppFtd4nCmB/TJPDJ6uvEryxs0vw5Y+jQwUiKnhl2VGGwIq0pWDIuaW4ppM
# V1pYQfJ6dtBGkRiTP1eKVvARYZMRaITe9ZhwJJnYP83pMxHCxaEsZC4ilY3/55dq
# d4ZXTCz/cpG5anmDartnWmgysygNstTwbWJJRj85gYRkjxi/nxKAiEFxl6GfkcnX
# Vy8DRFQj1d3AiqsePoeIzxu1iuAJRwDrfe4NnKHqoTgWsv7eCWJnWjWWRt7RRGrp
# vzLQo/BxUb8i49UwRg9G5bxpd5Su1b224Gv6G1HRU+qJHB1zoe41D2r/ic2BPous
# V9neYK5qI5PHLshAn6YTQllbV9pCbOUvZO0dtdwp5HH2fw6ofJNwKcPElaqkEcxv
# rhhRWqwNgaEVTyIV4jMc8jPbx2Nh9zAztnb9NfnDFOE+/gF8cZqTa/T65TGNP3uM
# iP3gr8nIXQ2IRwMVUoLmGu2qfmhDoews3dcvk6s1aA6mXHw+MANEIDKKjw3i2G6J
# tZkEemu1OXtskg/tGnfywaMgq5CauU9b6enTtA+UE+GKnmiQW6YUHPhBI0L2QG76
# TRBre5PpNVHiyc/01bjUEMpeaB+InAH4nDxYXx18wbJE+e/IbMv0147EFL792dEL
# F0XwqqcU0TCCB3EwggVZoAMCAQICEzMAAAAVxedrngKbSZkAAAAAABUwDQYJKoZI
# 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
# ExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiMKAQEwBwYFKw4DAhoDFQDi
# HEW6Ca3n5BgZV/tQ/fCR09Tf96CBgzCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w
# IFBDQSAyMDEwMA0GCSqGSIb3DQEBCwUAAgUA6PdsRzAiGA8yMDIzMTEwOTE0MzM0
# M1oYDzIwMjMxMTEwMTQzMzQzWjB3MD0GCisGAQQBhFkKBAExLzAtMAoCBQDo92xH
# AgEAMAoCAQACAiEGAgH/MAcCAQACAhNOMAoCBQDo+L3HAgEAMDYGCisGAQQBhFkK
# BAIxKDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAIAgEAAgMBhqAwDQYJ
# KoZIhvcNAQELBQADggEBAC4jaLMUyjCjulG4c32j2Y1qJUInZWc/R5PmjTlRiljD
# lG2+bFzx1RyXeqPJgtHxxRJlxO3++/vdX4exzeuoHes/NH42rPnECLfSiEZCmXaY
# aLjrAl9C2Oxxht03P6z2Ul8EgezBz5ta7jep+Czgf2GjeGEhSbRibznMPogwXGCp
# UvH4tK+RGf1oovctg/ShNut1MGFoP+TaoQq5rDx9CCCjd3+LT3PzJDac81eNCshC
# 9BdKKvzRgux3yABW63NWwe6s3cPRId8BBjHPTzDAZisckeyYBLALBxBO3nzbzCMc
# WzbevPhId6zRJ4OVY83TrAQrHVm6muuBqmGZyWfvHlsxggQNMIIECQIBATCBkzB8
# MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVk
# bW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1N
# aWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAdmcXAWSsINrPgABAAAB
# 2TANBglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEE
# MC8GCSqGSIb3DQEJBDEiBCB4dgEax7OPQ91VfXCYKCWh7VsU2t6l/gmgnOd6/ZdE
# YTCB+gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EIJ+gFbItOm/UMDAnETzbJe0u
# 0DWd2Mgpgb0ScbQgB3nzMIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgT
# Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29m
# dCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENB
# IDIwMTACEzMAAAHZnFwFkrCDaz4AAQAAAdkwIgQg6i3rxm4DU9/cGMTIbdWWHDhj
# ByJcY5V+qBXaG8X8yFgwDQYJKoZIhvcNAQELBQAEggIAWfa0o5UZYtPvg03amClW
# z35OuBRCtsy89xHgJkgxtByN8w3RVi9f/6DzFSHOpb2COwQQ355VtYkktKugf16n
# VAPsNgkVXkmoA3T1n9kmo/b2NfnVw0B6EJcQKTj0QEqPv2x8oS884rrhTMcI4NDV
# 9t6Dw24TZfZGZ7kg62Mqe6bPbkEe+gySyyM4cYXrXe0XzqVkD3YUHJrmmxhO8RNA
# IkYGioar1vCoJxWXa9lzckn+vlnNTOWwKDs812cYsYS7sVnhTLr5DQqhZr3gD2yj
# MvYHPM9j+4ZYmtE6+IHBPThOI0dfyJzMrvB8IHhVh5tOVwJL6KM6gADdGbQberN+
# hKMZkWmxEkkG72dEEId5Uy88aO2Tc/YDQZURjlsYRX/chI8Tu6c6LkDJubz1RU/3
# Tn93N6U57PNbnbSd99R0h72kwCfeSTPp5Qpd8PEYH/N1Sbdm/JDt6zzXvlaSZWjh
# kgI/LJRzgyCQdZbcIv+SrWupGP4baBdVhQ+YrfLKZirB3MRi+NNEbXQiq/UFuWei
# 8SoYH/7YN9bzjLFxK0/2R6aUpKhuHeJ7wIvUtUP27xhPrZLMtBhqkDmIet/6cJOf
# fdYh4ckcQdCpH/sLtt37OW8kgDDp4iQ07eytIqtGIWFQpGiEmksFwIeTVpwkxlc4
# BqEdTUZCH6B+ECcbQILjLlM=
# SIG # End signature block