NetworkValidation/NetworkChecker/networktester.ps1
#Requires -RunAsAdministrator <# .SYNOPSIS Network Validation is intened to be performed using networktester.ps1 script. The script is intended to be ran from the HLH in conjuction with the configuration applied to the BMC switch from <NamePrefix>-Rack01-BMC-1-PostValidation.txt file generated by The Partner Toolkit. .DESCRIPTION The script checks the following: BMCNetwork using the DVM's IP address -Check if the BMC gateway is reachable -Check if all Point To Point IP addresses respond to ping -Check DNS resolution (AAD) -Check time sync -Check internet access to Azure Endpoints (Internet is tested in AAD Only) Public Vips network using an SLB/Public VIP IP address -Check if the gateway is reachable. The gateway is created on the BMC switch using the commands in <NamePrefix>-Rack01-BMC-1-PostValidation.txt -Check DNS resolution (AAD) -Check time sync -Check internet access to Azure Endpoints (Internet is tested in AAD Only) .EXAMPLE .\networktester.ps1 .\configurationdata.json -VirtualSwitchName "contoso" #1 Test both public vips and dvm network. .NOTES .LINK #> [CmdletBinding()] Param( [Parameter(Position = 1, Mandatory = $true, ParameterSetName = "combined" )] [ValidateScript( { test-path $_ })] # All the configuration data required for AzureStack [string] $DeploymentDataJson, # The name of the virtual switch being used on the HLH [Parameter(Mandatory = $true)] [string] $VirtualSwitchName, # No Uplinks required, if the P2P interfaces do not ping to the Border this is the override to use. [bool] $NoUplinksRequired, # Azure Resource Manager endpoint URI for custom cloud [Parameter()] [System.Uri] $CustomCloudArmEndpoint ) $ErrorActionPreference = 'Stop' Import-Module $PSScriptRoot\NetworkValidationModules -Force -Global Import-Module $PSScriptRoot\..\..\Microsoft.AzureStack.ReadinessChecker.Reporting.psm1 -Force -Global $configJson = (Get-Content -Raw $DeploymentDataJson | ConvertFrom-Json ).Configdata $fullJson = (Get-Content -Raw $DeploymentDataJson | ConvertFrom-Json ) $global:cloudId = Get-CloudID -DeploymentData $DeploymentData #AzureAD or ADFS. If ADFS no DNS or webCheck is performed. $connectToAzure = ($configJson.InputData.Cloud |Where-Object {$_.Id -eq $cloudId}).ConnectToAzure if ($connectToAzure -eq "Azure Active Directory") { Write-AzsReadinessLog -Message "Active Directory is using $connectToAzure" -ToScreen } else { Write-AzsReadinessLog -Message "Active Directory is using $connectToAzure" -ToScreen } $OEM = ($configJson.EnvironmentData.Switch | where-object {$_.Type -eq "TOR1" }).make | Sort-Object -Unique function main { <# .SYNOPSIS Create a VNic for testing public vips and dvm prior to Azure Stack Deployment .EXAMPLE main -NicName "AzSPreValNic-DVM" -VirtualSwitchName "VSwitch" .EXAMPLE main -NicName "AzSPreValNic-Public" -VirtualSwitchName "VSwitch" #> [CmdletBinding()] param( # The name of the virtual NIC [Parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] [string] $NicName, # The name of the virtual switch [Parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] [string] $VirtualSwitchName, # OEM for AzureStack [Parameter(Mandatory = $True)] [string] $OEM, # AAD, ADFS, ADFS Connected [Parameter(Mandatory = $True)] [string] $ConnectToAzure, # ConfigurtionDataJson produced from the PTK [Parameter(Mandatory = $True)] [PSCustomObject] $ConfigJson, # Full Json produced from the PTK [Parameter(Mandatory = $True)] [PSCustomObject] $FullJson, # No Uplinks required, if the P2P interfaces do not ping to the Border this is the override to use. [bool] $NoUplinksRequired, # Azure Resource Manager endpoint URI for custom cloud [Parameter()] [System.Uri] $CustomCloudArmEndpoint ) try { $validationResults = @() $scaleUnits = $fullJson.ScaleUnits $routing = $ConfigJson.InputData.BorderConnectivity $extendedStorage = $ConfigJson.InputData.IsExtendedStorage $check = Invoke-CheckForVnic -Name $NicName # Bool value used to determine if there is no BMC switch $noBmc = $ConfigJson.InputData.IsNoBmc if (!$Check.Success) { Write-AzsReadinessLog -Message "$($Check.Message)" -Type 'Error' -ToScreen return $Check } Write-AzsReadinessLog -Message "Creating new vNIC $NicName" if ($NicName -eq "AzSPreValNic-Public") { $global:ipInfo = Get-PublicVipInfo -ConfigJson $ConfigJson } else { $global:ipInfo = Get-DvmIpInfo -ScaleUnits $scaleUnits -ConfigJson $ConfigJson } Write-AzsReadinessLog -Message "Executing Network Tests for BMC Management Network using the DVM's IP $($ipInfo.testIPv4Address)." -ToScreen $adapterInfo = Invoke-CreateNet -Name $NicName -IPAddr $ipInfo.testIPv4Address -SubnetMaskPrefix $ipInfo.IPv4MaskBits -VirtualSwitchName $VirtualSwitchName -Verbose:$VerbosePreference if ($adapterInfo.Success -eq $False) { Write-AzsReadinessLog -Message "$($adapterInfo.Message)" -Type 'Error' -ToScreen return $adapterInfo } $nicReady = Invoke-TestNicReady -IPAddr $ipInfo.testIPv4Address -NicName $NicName -Verbose:$VerbosePreference if (!$nicReady.Success) { Write-AzsReadinessLog -Message "removing vNic $NicName and associated configuration due to address conflict" Remove-VMNetworkAdapter -ManagementOS -Name $NicName Write-AzsReadinessLog -Message "$($nicReady.Message)" -Type 'Error' -ToScreen return $nicReady } Write-AzsReadinessLog -Message "vNIC $NicName is now active" Write-AzsReadinessLog -Message "Testing connectivity to vNIC $NicName gateway $($ipInfo.IPV4Gateway)..." $nicTest = Invoke-TestNicGateway -gateway $ipInfo.IPV4Gateway -networkName $ipInfo.networkName if (!$nicTest.Success) { Write-AzsReadinessLog -Message "$($nicTest.Message)" -Type 'Error' -ToScreen return $nicTest } else { Write-AzsReadinessLog -Message $($nicTest.Message) } if ($NicName -ne "AzSPreValNic-Public") { #PingTest only from DVM IP to avoid being blocked by ACL's $splat = @{ IpConfigData = $ConfigJson.IPConfigData AdapterInfo = $adapterInfo.adapterInfo IpInfo = $ipInfo OEM = $OEM Routing = $routing ExtendedStorage = $extendedStorage IsNoBMC = $noBmc NoUplinksRequired = $NoUplinksRequired Verbose = $VerbosePreference } $pingTesting = Invoke-PingTesting @splat if ($pingTesting.Success -eq $false) { return $pingTesting } } #test dvm ntp time sync ability $timeServer = [string]$($($ConfigJson.inputData.cloud | Where-Object {$_.ID -match $cloudId} ).TimeServer ) #set routes for ntp server and test Write-AzsReadinessLog -Message "Source IP $($ipInfo.testIPv4Address) for testing Customer Provided NTP Server: $($timeServer)" $ntpResult = Invoke-NtpTesting -adapterInfo ($adapterInfo.adapterInfo) -timeServer $timeServer -ipInfo $ipInfo -Verbose:$VerbosePreference if ($ntpResult.Success) { Write-AzsReadinessLog -Message "$($ntpResult.Message)" $hash = @{ Test = 'dvm_ntpServer' Result = 'OK' FailureDetail = $null OutputObject = $null } Write-AzsResult -in $hash $validationResults += $hash } else { Write-AzsReadinessLog -Message "$($ntpResult.Message)" -Type 'Error' $traceRT = @{ "TraceRoute" = (Trace-ToDestination -configJson $ConfigJson -DestIP $timeServer -BmcGatewayIP $ipInfo.IPV4Gateway).TraceRoute } $ntpResult += $traceRT $ntpMessage = $ntpResult.Message Write-AzsReadinessLog -Message "$($ntpMessage)" -Type 'Error' $hash = @{ Test = 'dvm_ntpServer' Result = 'Fail' FailureDetail = $ntpMessage OutputObject = $null } Write-AzsResult -in $hash $validationResults += $hash return $ntpResult } $dnsServers = $($($ConfigJson.inputData.cloud | Where-object {$_.ID -match $cloudId} ).DNSForwarder ) if ( $ConnectToAzure -eq "Azure Active Directory" ) { #Get the Azure Env from ConfigJson $azureEnvironment = ($configJson.InputData.Cloud |Where-Object {$_.Id -eq $cloudId}).InfraAzureEnvironment if ($azureEnvironment -eq 'CustomCloud') { # Test DNS to CustomCloudArmEndpoint $customCloudRoutes = Invoke-DnsTesting -AdapterInfo $adapterInfo.adapterInfo -DnsServers $dnsServers -IpInfo $ipInfo -HostnameToResolve (([System.Uri]$CustomCloudArmEndpoint).Host) -Verbose:$VerbosePreference $checkCustomCloudRoutes = Invoke-DnsCheck -Routes $customCloudRoutes -ConfigJson $ConfigJson -IpInfo $ipInfo if ($checkCustomCloudRoutes.Success -eq $False) { $hash = @{ Test = "dvm_dnsServer" Result = 'Fail' FailureDetail = $customCloudRoutes.Message OutputObject = $null } Write-AzsResult -in $hash $validationResults += $hash return $checkCustomCloudRoutes } # Then Get-AzureURIs because DNS and routing need to be established prior to making the webrequest to Azure. $azureEndpoints = Get-AzureURIs -AzureEnvironment $azureEnvironment -CustomCloudARMEndpoint $CustomCloudArmEndpoint -Ips $customCloudRoutes.ips } else { $azureEndpoints = Get-AzureURIs -AzureEnvironment $azureEnvironment } foreach ($endPoint in $azureEndpoints.keys) { $uri = $azureEndpoints.$endPoint if ([system.uri]::IsWellFormedUriString($uri,[System.UriKind]::Absolute) -eq $false){ $msg = "The URI is not valid or is null: URI=$uri" Write-AzsReadinessLog -Message $msg Throw $msg } Write-AzsReadinessLog -Message "Attempting DNS resolution for URI: $uri" $routes = Invoke-DnsTesting -AdapterInfo $adapterInfo.adapterInfo -DnsServers $dnsServers -IpInfo $ipInfo -HostnameToResolve (([System.Uri]$uri).Host) -Verbose:$VerbosePreference $checkDns = Invoke-DnsCheck -Routes $routes -ConfigJson $ConfigJson -IpInfo $ipInfo if ($checkDns.Success -eq $False) { $hash = @{ Test = "dvm_dnsServer" Result = 'Fail' FailureDetail = $routes.Message OutputObject = $null } Write-AzsResult -in $hash $validationResults += $hash return $checkDns } #Azure Active Directory requires internet access for deployment Write-AzsReadinessLog -Message "Source IP $($ipInfo.testIPv4Address) for Testing $uri($($routes.ips[0]))" $webCheck = Invoke-WebCheck -Ips $routes.ips -IpInfo $ipInfo -Domain (([System.Uri]$uri).Host) -Verbose:$VerbosePreference Write-AzsReadinessLog -Message $($webCheck.Message) Invoke-WebResults -WebCheck $webCheck -Routes $routes -ConfigJson $ConfigJson -IpInfo $ipInfo if (!$webCheck.Success) { # also include the dns results $hash = @{ Test = "dvm_dnsServer" Result = 'OK' FailureDetail = $null OutputObject = $null } Write-AzsResult -in $hash $validationResults += $hash # WebRequest Results. $hash = @{ Test = "dvm_webRequests" Result = 'Fail' FailureDetail = $webCheck.Message OutputObject = $null } Write-AzsResult -in $hash $validationResults += $hash return $webCheck } Remove-Variable webCheck } # after the loop we know dns and webrequests are working. $hash = @{ Test = "dvm_dnsServer" Result = 'OK' FailureDetail = $null OutputObject = $null } Write-AzsResult -in $hash $validationResults += $hash $hash = @{ Test = "dvm_webRequests" Result = 'OK' FailureDetail = $null OutputObject = $null } Write-AzsResult -in $hash $validationResults += $hash $returnData = @{ "success" = $True } } else { #no web check needed, setting success to true. $returnData = @{ "success" = $True } } Write-AzsReadinessLog -Message "removing vNic $nicName and associated configuration" Remove-VMNetworkAdapter -ManagementOS -Name $nicName $returnData.ValidationResults = $validationResults return $returnData } catch { $file = $PSItem.InvocationInfo.ScriptName $line = $($PSItem.InvocationInfo.ScriptLineNumber) $message = $($PSItem.Exception.Message) Write-Verbose -Message ("ERROR: " + $file + ":" + $line) -Verbose Write-Verbose -Message $message -Verbose Write-Error -Message $message } } #dvm check first if ($CustomCloudArmEndpoint){ $dvm = main -NicName "$CloudID-AzSPreValNic-DVM" -VirtualSwitchName $VirtualSwitchName -OEM $OEM -ConfigJson $configJson -ConnectToAzure $connectToAzure -FullJson $fullJson -NoUplinksRequired $NoUplinksRequired -CustomCloudArmEndpoint $CustomCloudArmEndpoint } else { $dvm = main -NicName "$CloudID-AzSPreValNic-DVM" -VirtualSwitchName $VirtualSwitchName -OEM $OEM -ConfigJson $configJson -ConnectToAzure $connectToAzure -FullJson $fullJson -NoUplinksRequired $NoUplinksRequired } if (!$dvm.Success) { $returnData = [PSCustomObject]@{ Success = $False Message = $dvm.Message ValidationResults = $dvm.ValidationResults } return $returnData } #All network connectivity works at this point. $returnData = [PSCustomObject]@{ Success = $True Message = "BMC Management Network using the DVM($($ipInfo.testIPv4Address)) has the proper network connectivity." ValidationResults = $dvm.ValidationResults } Write-AzsReadinessLog -Message $($returnData.Message) return $returnData # SIG # Begin signature block # MIIjhgYJKoZIhvcNAQcCoIIjdzCCI3MCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDbNqCpWKWRM9ni # WsWzLsUTXLOuYlBrJ3NAkDMTGciW76CCDYEwggX/MIID56ADAgECAhMzAAACUosz # qviV8znbAAAAAAJSMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjEwOTAyMTgzMjU5WhcNMjIwOTAxMTgzMjU5WjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQDQ5M+Ps/X7BNuv5B/0I6uoDwj0NJOo1KrVQqO7ggRXccklyTrWL4xMShjIou2I # sbYnF67wXzVAq5Om4oe+LfzSDOzjcb6ms00gBo0OQaqwQ1BijyJ7NvDf80I1fW9O # L76Kt0Wpc2zrGhzcHdb7upPrvxvSNNUvxK3sgw7YTt31410vpEp8yfBEl/hd8ZzA # v47DCgJ5j1zm295s1RVZHNp6MoiQFVOECm4AwK2l28i+YER1JO4IplTH44uvzX9o # RnJHaMvWzZEpozPy4jNO2DDqbcNs4zh7AWMhE1PWFVA+CHI/En5nASvCvLmuR/t8 # q4bc8XR8QIZJQSp+2U6m2ldNAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUNZJaEUGL2Guwt7ZOAu4efEYXedEw # UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1 # ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDY3NTk3MB8GA1UdIwQYMBaAFEhu # ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu # bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w # Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3 # Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx # MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAFkk3 # uSxkTEBh1NtAl7BivIEsAWdgX1qZ+EdZMYbQKasY6IhSLXRMxF1B3OKdR9K/kccp # kvNcGl8D7YyYS4mhCUMBR+VLrg3f8PUj38A9V5aiY2/Jok7WZFOAmjPRNNGnyeg7 # l0lTiThFqE+2aOs6+heegqAdelGgNJKRHLWRuhGKuLIw5lkgx9Ky+QvZrn/Ddi8u # TIgWKp+MGG8xY6PBvvjgt9jQShlnPrZ3UY8Bvwy6rynhXBaV0V0TTL0gEx7eh/K1 # o8Miaru6s/7FyqOLeUS4vTHh9TgBL5DtxCYurXbSBVtL1Fj44+Od/6cmC9mmvrti # yG709Y3Rd3YdJj2f3GJq7Y7KdWq0QYhatKhBeg4fxjhg0yut2g6aM1mxjNPrE48z # 6HWCNGu9gMK5ZudldRw4a45Z06Aoktof0CqOyTErvq0YjoE4Xpa0+87T/PVUXNqf # 7Y+qSU7+9LtLQuMYR4w3cSPjuNusvLf9gBnch5RqM7kaDtYWDgLyB42EfsxeMqwK # WwA+TVi0HrWRqfSx2olbE56hJcEkMjOSKz3sRuupFCX3UroyYf52L+2iVTrda8XW # esPG62Mnn3T8AuLfzeJFuAbfOSERx7IFZO92UPoXE1uEjL5skl1yTZB3MubgOA4F # 8KoRNhviFAEST+nG8c8uIsbZeb08SeYQMqjVEmkwggd6MIIFYqADAgECAgphDpDS # AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK # V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 # IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0 # ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla # MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS # ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT # H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB # AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG # OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S # 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz # y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7 # 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u # M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33 # X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl # XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP # 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB # l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF # RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM # CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ # BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud # DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO # 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0 # LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y # Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p # Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y # Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB # FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw # cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA # XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY # 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj # 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd # d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ # Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf # wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ # aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j # NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B # xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96 # eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7 # r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I # RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIVWzCCFVcCAQEwgZUwfjELMAkG # A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx # HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z # b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAlKLM6r4lfM52wAAAAACUjAN # BglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor # BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgo3Rqf50a # qMEaigZSxbBcgyUrjVGY3f9CCGDz6OutYMYwQgYKKwYBBAGCNwIBDDE0MDKgFIAS # AE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTAN # BgkqhkiG9w0BAQEFAASCAQAdB6sdOr7behJ8N5PBJ9h6wn5i6wO0QTRnR9Qyv5kt # aDuaqYPTaFrHhN44tWKEwFeG+Fjhj20lI9tFEELR1W+nSKPwG5CAcReQ+5iTXrjR # 283V9o3AfmflHN3ka9/HPl5ePfNzltiOmcD+QbqVDnVP6YeN6YSPW8MpKOKZYWc1 # 3sBfnuEvgWhu2s+YBR1VMc6XOYIFc1btN87L0EokKg/wOogc10te1Q5x8x3RLgT9 # xB9YFqe2nWcw8t34/l5P7aCUuZDeNMQj/25CQ8TeDQOeYwRiMLLxkt9XFWzXhbxp # aiPBjAjZxd9Zl9qNSXv4lVjkfP5HLor4kzyBEpAjiBzSoYIS5TCCEuEGCisGAQQB # gjcDAwExghLRMIISzQYJKoZIhvcNAQcCoIISvjCCEroCAQMxDzANBglghkgBZQME # AgEFADCCAVEGCyqGSIb3DQEJEAEEoIIBQASCATwwggE4AgEBBgorBgEEAYRZCgMB # MDEwDQYJYIZIAWUDBAIBBQAEID1En1UtkYvtuetYeU0eB0H3TkC/t6J9YKCz+dk5 # C7yVAgZhgBtpOX0YEzIwMjExMTE1MTQ1NjEwLjc3NFowBIACAfSggdCkgc0wgcox # CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt # b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJTAjBgNVBAsTHE1p # Y3Jvc29mdCBBbWVyaWNhIE9wZXJhdGlvbnMxJjAkBgNVBAsTHVRoYWxlcyBUU1Mg # RVNOOjEyQkMtRTNBRS03NEVCMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFt # cCBTZXJ2aWNloIIOPDCCBPEwggPZoAMCAQICEzMAAAFT0oJyRWxX44sAAAAAAVMw # DQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0 # b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3Jh # dGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwHhcN # MjAxMTEyMTgyNjA1WhcNMjIwMjExMTgyNjA1WjCByjELMAkGA1UEBhMCVVMxEzAR # BgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1p # Y3Jvc29mdCBDb3Jwb3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJpY2Eg # T3BlcmF0aW9uczEmMCQGA1UECxMdVGhhbGVzIFRTUyBFU046MTJCQy1FM0FFLTc0 # RUIxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggEiMA0G # CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC167Z0B7Mb+bNCAuXC6EGJsFvgVB6i # 30/jl4gmtUwoQG5z3WpxQHNJj+t2I4fpETQQLoxioHSiR+nbJFRnT3WCjc6UwbGB # MnQJgzgzVXofVPHICimiYNEiHy86PvCEpWT8vb6jfFcaOVoMe+4wN2NqblOWA4o6 # wmEQUFmrbpKZB+cRVJF7WE1L6NDfRiPE9XRfRk+zzZd+MJhs3eWRr7jVdG10vVEl # V3hq0YFzfho7guP3L0bMxikRy3pPe4W6g7Qwf5McxDpM0Aatz9CNkscAPyh4jZOF # 0f7diL1BGET/c4vp8iQTlfKqEhCbt3Fi/Hq54cwXjE1aUxzQva5b/ebtAgMBAAGj # ggEbMIIBFzAdBgNVHQ4EFgQUxqKLc0gqqbMwGUC34HBtrDFWRHUwHwYDVR0jBBgw # FoAU1WM6XIoxkPNDe3xGG8UzaFqFbVUwVgYDVR0fBE8wTTBLoEmgR4ZFaHR0cDov # L2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMvTWljVGltU3RhUENB # XzIwMTAtMDctMDEuY3JsMFoGCCsGAQUFBwEBBE4wTDBKBggrBgEFBQcwAoY+aHR0 # cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNUaW1TdGFQQ0FfMjAx # MC0wNy0wMS5jcnQwDAYDVR0TAQH/BAIwADATBgNVHSUEDDAKBggrBgEFBQcDCDAN # BgkqhkiG9w0BAQsFAAOCAQEAlGi9eXz6IEQ+xwFzSaJR3JxHsaiegym9DsRXcpfd # 7frlTUZ/5iVkyFq8YXyKoEI+7bVOzJlTf3PF8UdYCy4ysWae6szI+bsz29boYoRl # wSaxdQOg9hwlX1MkfdMnhJ+Iiw35EVYp7rMVjdN9i6aZHldKP/PDQNu+uzEHobKN # LpZsxe+LI/gdxuIiFJDO3O9eTun07xWHfhnIxJ2wS7y5PEHpdxZTQX2nvWR0bjdm # t5r6hy5X0sND1XZiaU2apl1Wb9Ha2bnO4A8lre7wZjS+i8XBVVJaE4LXmcTFeQll # Zldvq8yBmvDo4wvjFOFVckqyBSpCMMJpRqzkodGDxJ06NDCCBnEwggRZoAMCAQIC # CmEJgSoAAAAAAAIwDQYJKoZIhvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRp # ZmljYXRlIEF1dGhvcml0eSAyMDEwMB4XDTEwMDcwMTIxMzY1NVoXDTI1MDcwMTIx # NDY1NVowfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNV # BAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQG # A1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwggEiMA0GCSqGSIb3 # DQEBAQUAA4IBDwAwggEKAoIBAQCpHQ28dxGKOiDs/BOX9fp/aZRrdFQQ1aUKAIKF # ++18aEssX8XD5WHCdrc+Zitb8BVTJwQxH0EbGpUdzgkTjnxhMFmxMEQP8WCIhFRD # DNdNuDgIs0Ldk6zWczBXJoKjRQ3Q6vVHgc2/JGAyWGBG8lhHhjKEHnRhZ5FfgVSx # z5NMksHEpl3RYRNuKMYa+YaAu99h/EbBJx0kZxJyGiGKr0tkiVBisV39dx898Fd1 # rL2KQk1AUdEPnAY+Z3/1ZsADlkR+79BL/W7lmsqxqPJ6Kgox8NpOBpG2iAg16Hgc # sOmZzTznL0S6p/TcZL2kAcEgCZN4zfy8wMlEXV4WnAEFTyJNAgMBAAGjggHmMIIB # 4jAQBgkrBgEEAYI3FQEEAwIBADAdBgNVHQ4EFgQU1WM6XIoxkPNDe3xGG8UzaFqF # bVUwGQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud # EwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU1fZWy4/oolxiaNE9lJBb186aGMQwVgYD # VR0fBE8wTTBLoEmgR4ZFaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwv # cHJvZHVjdHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3JsMFoGCCsGAQUFBwEB # BE4wTDBKBggrBgEFBQcwAoY+aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9j # ZXJ0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcnQwgaAGA1UdIAEB/wSBlTCB # kjCBjwYJKwYBBAGCNy4DMIGBMD0GCCsGAQUFBwIBFjFodHRwOi8vd3d3Lm1pY3Jv # c29mdC5jb20vUEtJL2RvY3MvQ1BTL2RlZmF1bHQuaHRtMEAGCCsGAQUFBwICMDQe # MiAdAEwAZQBnAGEAbABfAFAAbwBsAGkAYwB5AF8AUwB0AGEAdABlAG0AZQBuAHQA # LiAdMA0GCSqGSIb3DQEBCwUAA4ICAQAH5ohRDeLG4Jg/gXEDPZ2joSFvs+umzPUx # vs8F4qn++ldtGTCzwsVmyWrf9efweL3HqJ4l4/m87WtUVwgrUYJEEvu5U4zM9GAS # inbMQEBBm9xcF/9c+V4XNZgkVkt070IQyK+/f8Z/8jd9Wj8c8pl5SpFSAK84Dxf1 # L3mBZdmptWvkx872ynoAb0swRCQiPM/tA6WWj1kpvLb9BOFwnzJKJ/1Vry/+tuWO # M7tiX5rbV0Dp8c6ZZpCM/2pif93FSguRJuI57BlKcWOdeyFtw5yjojz6f32WapB4 # pm3S4Zz5Hfw42JT0xqUKloakvZ4argRCg7i1gJsiOCC1JeVk7Pf0v35jWSUPei45 # V3aicaoGig+JFrphpxHLmtgOR5qAxdDNp9DvfYPw4TtxCd9ddJgiCGHasFAeb73x # 4QDf5zEHpJM692VHeOj4qEir995yfmFrb3epgcunCaw5u+zGy9iCtHLNHfS4hQEe # gPsbiSpUObJb2sgNVZl6h3M7COaYLeqN4DMuEin1wC9UJyH3yKxO2ii4sanblrKn # QqLJzxlBTeCG+SqaoxFmMNO7dDJL32N79ZmKLxvHIa9Zta7cRDyXUHHXodLFVeNp # 3lfB0d4wwP3M5k37Db9dT+mdHhk4L7zPWAUu7w2gUDXa7wknHNWzfjUeCLraNtvT # X4/edIhJEqGCAs4wggI3AgEBMIH4oYHQpIHNMIHKMQswCQYDVQQGEwJVUzETMBEG # A1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWlj # cm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBP # cGVyYXRpb25zMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjoxMkJDLUUzQUUtNzRF # QjElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZaIjCgEBMAcG # BSsOAwIaAxUAikpN7ccO1k9PKCGCvaQWKyJ50ZuggYMwgYCkfjB8MQswCQYDVQQG # EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG # A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQg # VGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQUFAAIFAOU8ZdQwIhgPMjAy # MTExMTUxMjUxMzJaGA8yMDIxMTExNjEyNTEzMlowdzA9BgorBgEEAYRZCgQBMS8w # LTAKAgUA5Txl1AIBADAKAgEAAgIMrAIB/zAHAgEAAgIRdjAKAgUA5T23VAIBADA2 # BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIBAAIDB6EgoQowCAIB # AAIDAYagMA0GCSqGSIb3DQEBBQUAA4GBAJiSmEKxZKbVbzMex1HG4XPsEfMLikUv # Q/fvVrUDhIlfxbOKqNQM2xLSo0sIN1hivBPHalobhtqF+atSL0L9NWgODeh4uOHn # yAQxG4qulUoZLm/M+vd/jaB6yWSk6MBfLtJvz3ua5Bhpf5VtKJEzuiWnLD6DXeMS # hqx1HM2UfGHTMYIDDTCCAwkCAQEwgZMwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgT # Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29m # dCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENB # IDIwMTACEzMAAAFT0oJyRWxX44sAAAAAAVMwDQYJYIZIAWUDBAIBBQCgggFKMBoG # CSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0BCQQxIgQgf8do8YEh # pQ1iTUUwdITxzBGcONAVklOJC/7zzFm08fYwgfoGCyqGSIb3DQEJEAIvMYHqMIHn # MIHkMIG9BCBQwQqPObQgCAn8l6GQQ6aaHSyDHG9sDh9qxAVojbWPADCBmDCBgKR+ # MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS # ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMT # HU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAABU9KCckVsV+OLAAAA # AAFTMCIEIJUuCnsxyaow3KJWmJLGLoxYb83AqyU/h9IUnL2j03EcMA0GCSqGSIb3 # DQEBCwUABIIBAI2d5uJCq5gJHx+J3OI4YVaUeyzYW2MOBYN+EwoDge3DfSwYUPw5 # h4rKJTxlbJqz2ZbMGXmv4KQ74zzHahGzgjlvtQjXUZmQtUk6LND3n+O8J1tIoVzW # 6vPhGG8pnUD51sAVCyRenXIxfvm9JNGsagw74W3pkdzD88V86tz2Ueq+vtmSCVg5 # eaJkzMhtEAK56yFFWvVmplGgEzIFkLjW9T0NkWTOq+X6SbBsCwmE47W3387dwHm2 # MaNMHEJg8bSeRrChDbLDBDIOk0Y1FJwNJSbUqNqOzHtNDaUt3WW75JyDCVaEsBCu # 9CVQXKTcEQyt7lIW/TRXfSGLTeXjDr1nk5c= # SIG # End signature block |