modules/AzStack.Network/AzStack.Network.psm1
|
################################################################# # # # Copyright (C) Microsoft Corporation. All rights reserved. # # # ################################################################# ################################################################# # # # STARTUP ACTIONS ON IMPORT # # # ################################################################# Import-LocalizedData -BindingVariable 'msg' -BaseDirectory "$PSScriptRoot\locale" -UICulture (Get-Culture) Import-Module $PSScriptRoot\..\AzStack.Utilities\AzStack.Utilities.psm1 -WarningAction SilentlyContinue Import-Module $PSScriptRoot\..\AzStack.Common\AzStack.Common.psm1 -WarningAction SilentlyContinue # Import SdnDiagnostics module if not already imported if (-not (Get-Module -Name 'SdnDiagnostics')) { $sdnDiagModule = Get-Item -Path "$PSScriptRoot\..\..\packages\SdnDiagnostics.*\SdnDiagnostics.psd1" -ErrorAction SilentlyContinue if ($sdnDiagModule) { Import-Module $sdnDiagModule.FullName } else { Trace-Output -Level:Error -Message "SdnDiagnostics module not found at the specified path." } } ################################################################# # # # ENUMS AND CLASSES # # # ################################################################# ################################################################# # # # FUNCTIONS # # # ################################################################# function Set-AzsSupportNetworkATCIntentApplyWithTracing { <# .SYNOPSIS Applies a Network ATC intent with tracing enabled to capture diagnostic information. .DESCRIPTION This function enables Network ATC tracing, applies a retry state to a specified network intent, waits for the intent to complete, and then captures the trace data. If the intent fails to apply successfully, it optionally collects additional diagnostic data. .PARAMETER IntentName The name of the Network ATC intent to apply. This intent must already exist. .PARAMETER OutputDirectory Optional. The directory where trace files and diagnostic data will be saved. .EXAMPLE Set-AzsSupportNetworkATCIntentApplyWithTracing -IntentName "Compute_Management" Applies the "Compute_Management" intent with tracing enabled and saves output to default directory. .EXAMPLE Set-AzsSupportNetworkATCIntentApplyWithTracing -IntentName "Storage" -OutputDirectory "C:\Traces" Applies the "Storage" intent with tracing enabled and saves output to C:\Traces. #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string]$IntentName, [Parameter(Mandatory = $false)] [string]$OutputDirectory = "$(Get-AzsSupportWorkingDirectory)\HostNetwork" ) $resultObject = [PSCustomObject]@{ IntentName = $IntentName IntentConfigStatus = $null IntentProvisioningStatus = $null OutputDirectory = $null TraceFilePath = $null AdditionalDiagnosticData = $null OccurrenceTimeUtc = (Get-Date).ToUniversalTime().ToString("o") Result = $null } $traceFilePath = "C:\Windows\NetworkATCTrace.etl" # NetworkATC always writes to this path # Validate that the intent exists $existingIntent = Get-NetIntent -Name $IntentName -ErrorAction SilentlyContinue if (-not $existingIntent) { Trace-Output -Level:Error -Message "NetIntent '$IntentName' not found." return } $OutputDirectory = Join-Path -Path $OutputDirectory -ChildPath "NetworkATC-IntentRetry_$($IntentName)" $null = Initialize-DataCollection -FilePath $OutputDirectory -MinimumMB 500 # Cleanup existing trace file, if it exists if (Test-Path -Path $traceFilePath) { $confirm = Confirm-UserInput -Message "Trace file already exists at $traceFilePath. Would you like to delete the existing trace file?" if ($confirm) { Trace-Output -Level:Information -Message "Deleting existing trace file at $traceFilePath" Remove-Item -Path $traceFilePath -Force } else { Trace-Output -Level:Information -Message "Keeping existing trace file at $traceFilePath" } } $null = Set-NetIntentTracing -ComputerName $env:COMPUTERNAME *>&1 Trace-Output -Level:Information -Message "Enabled network intent tracing" # Retry applying the intent Trace-Output -Level:Information -Message "Applying intent retry state for '$IntentName'" $null = Set-NetIntentRetryState -Name $IntentName -NodeName $env:COMPUTERNAME *>&1 # Wait for the intent to be applied $iter = 1 $maxIter = 12 $sleepTime = 15 Trace-Output -Level:Information -Message "Waiting for intent '$IntentName' to be applied. Maximum iterations: $maxIter, Sleep time between iterations: $sleepTime seconds." while ($iter -le $maxIter) { $status = Get-NetIntentStatus -Name $IntentName -ErrorAction SilentlyContinue | Where-Object { $_.Host -like $env:COMPUTERNAME } Trace-Output -Level:Information -Message "[$iter/$maxIter] Intent '$IntentName' ConfigurationStatus: $($status.ConfigurationStatus), ProvisioningStatus: $($status.ProvisioningStatus)" if ($status.ConfigurationStatus -eq "Success" -and $status.ProvisioningStatus -eq "Completed") { Trace-Output -Level:Information -Message "Intent '$IntentName' applied successfully." break } Start-Sleep -Seconds $sleepTime $iter++ } $null = Set-NetIntentTracing -StopTracing *>&1 Trace-Output -Level:Information -Message "Stopped network intent tracing" # Copy the trace file to output directory and convert to text if (Test-Path -Path $traceFilePath) { $destinationPath = Join-Path -Path $OutputDirectory -ChildPath "NetworkATC_Trace.etl" $txtOutputPath = Join-Path -Path $OutputDirectory -ChildPath "NetworkATC_Trace.txt" Trace-Output -Level:Verbose -Message "Copying trace file to output directory: $destinationPath" Copy-Item -Path $traceFilePath -Destination $destinationPath -Force Trace-Output -Level:Verbose -Message "Converting trace file to text format: $txtOutputPath" $null = netsh trace convert $destinationPath output=$txtOutputPath *>&1 } # Summarize results $intentStatus = Get-NetIntentStatus -Name $IntentName -ErrorAction Ignore | Where-Object { $_.Host -like $env:COMPUTERNAME } Trace-Output -Level:Information -Message "Intent '$IntentName' Retry Result" $statusLevel = if ($intentStatus.ConfigurationStatus -eq "Success" -and $intentStatus.ProvisioningStatus -eq "Completed") { "Success" } else { "Warning" } $resultObject.IntentConfigStatus = $intentStatus.ConfigurationStatus $resultObject.IntentProvisioningStatus = $intentStatus.ProvisioningStatus $resultObject.OutputDirectory = $OutputDirectory $resultObject.TraceFilePath = $txtOutputPath $resultObject.Result = $statusLevel # If the intent was not applied successfully, collect the additional support data if ($intentStatus.ConfigurationStatus -ne "Success" -or $intentStatus.ProvisioningStatus -ne "Completed") { $confirm = Confirm-UserInput -Message "Would you like to collect additional Network ATC Diagnostic Data for this node?" if ($confirm) { $null = Get-AzsSupportHostNetworkDiagnosticData -OutputDirectory $OutputDirectory -SkipCompression $compressedData = Compress-AzsSupportArchive -Path $OutputDirectory -Destination "$OutputDirectory.zip" Show-UploadInstruction -DataType 'Network ATC Diagnostic Data' -SourcePath $compressedData.FullName $resultObject.AdditionalDiagnosticData = $compressedData.FullName } } else { Trace-Output -Level:Success -Message "Intent '$IntentName' applied successfully. No additional diagnostic data is needed." } return $resultObject } function Get-AzsSupportHostNetworkDiagnosticData { <# .SYNOPSIS Collects comprehensive network diagnostic data from the local host. .DESCRIPTION This function gathers extensive network diagnostic information including Network ATC configuration, Windows Event Logs, cluster configuration, network adapter settings, and host configuration. The collected data is saved to JSON files and optionally compressed into a ZIP archive. .PARAMETER FilePrefix Optional. A prefix to add to the diagnostic data folder name for easier identification. .PARAMETER OutputDirectory Optional. The directory where diagnostic data will be saved. .PARAMETER SkipCompression Optional. If specified, the diagnostic data will not be compressed into a ZIP file. By default, a ZIP archive is created for easier transport. .EXAMPLE Get-AzsSupportHostNetworkDiagnosticData Collects diagnostic data to the default Network folder and creates a ZIP archive. .EXAMPLE Get-AzsSupportHostNetworkDiagnosticData -FilePrefix "Issue123" -OutputDirectory "C:\Diagnostics" Collects diagnostic data with "Issue123" prefix to C:\Diagnostics and creates a ZIP archive. .EXAMPLE Get-AzsSupportHostNetworkDiagnosticData -SkipCompression Collects diagnostic data without creating a ZIP archive. #> [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [string]$FilePrefix = $null, [Parameter(Mandatory = $false)] [string]$OutputDirectory = "$(Get-AzsSupportWorkingDirectory)\HostNetwork", [Parameter(Mandatory = $false)] [switch]$SkipCompression ) $winEventsToExport = @( "System", "Application", "Microsoft-Windows-Networking-NetworkAtc/Operational", "Microsoft-Windows-Networking-NetworkAtc/Admin", "Microsoft-Windows-FailoverClustering/Operational", "AzStackHciEnvironmentChecker" ) $diagPath = Join-Path -Path $OutputDirectory -ChildPath "DiagnosticData" $null = Initialize-DataCollection -FilePath $diagPath -MinimumMB 500 # Collect Network ATC Configuration Invoke-CommandToJSON -Command { Get-NetIntent } -OutputDirectory $diagPath -FileName "Get-NetIntent.json" Invoke-CommandToJSON -Command { Get-NetIntentStatus } -OutputDirectory $diagPath -FileName "Get-NetIntentStatus.json" Invoke-CommandToJSON -Command { Get-NetIntent -GlobalOverrides } -OutputDirectory $diagPath -FileName "Get-NetIntent-GlobalOverrides.json" Invoke-CommandToJSON -Command { Get-NetIntentAllGoalStates } -OutputDirectory $diagPath -FileName "Get-NetIntentAllGoalStates.json" # Collect Cluster Configuration Invoke-CommandToJSON -Command { Get-Cluster } -OutputDirectory $diagPath -FileName "Get-Cluster.json" Invoke-CommandToJSON -Command { Get-ClusterNode } -OutputDirectory $diagPath -FileName "Get-ClusterNode.json" Invoke-CommandToJSON -Command { Get-ClusterResource } -OutputDirectory $diagPath -FileName "Get-ClusterResource.json" Invoke-CommandToJSON -Command { Get-ClusterNetwork } -OutputDirectory $diagPath -FileName "Get-ClusterNetwork.json" Invoke-CommandToJSON -Command { Get-ClusterResourceType "Virtual Machine" | Get-ClusterParameter -Name "migration*" } -OutputDirectory $diagPath -FileName "Get-ClusterResourceType-Migration.json" # Collect Network Configuration Invoke-CommandToJSON -Command { Get-NetIPAddress } -OutputDirectory $diagPath -FileName "Get-NetIPAddress.json" Invoke-CommandToJSON -Command { Get-NetIPConfiguration 4>$null } -OutputDirectory $diagPath -FileName "Get-NetIPConfiguration.json" Invoke-CommandToJSON -Command { Get-NetAdapter } -OutputDirectory $diagPath -FileName "Get-NetAdapter.json" Invoke-CommandToJSON -Command { Get-NetAdapterAdvancedProperty } -OutputDirectory $diagPath -FileName "Get-NetAdapterAdvancedProperty.json" Invoke-CommandToJSON -Command { Get-VMSwitch } -OutputDirectory $diagPath -FileName "Get-VMSwitch.json" Invoke-CommandToJSON -Command { Get-VMSwitch | Get-VMSwitchTeam -ErrorAction SilentlyContinue } -OutputDirectory $diagPath -FileName "Get-VMSwitchTeam.json" Invoke-CommandToJSON -Command { Get-VMNetworkAdapter -ManagementOS } -OutputDirectory $diagPath -FileName "Get-VMNetworkAdapter-ManagementOS.json" Invoke-CommandToJSON -Command { Get-VMNetworkAdapterVlan -ManagementOS } -OutputDirectory $diagPath -FileName "Get-VMNetworkAdapterVlan-ManagementOS.json" Invoke-CommandToJSON -Command { Get-VMNetworkAdapterIsolation -ManagementOS } -OutputDirectory $diagPath -FileName "Get-VMNetworkAdapterIsolation-ManagementOS.json" # Collect Host Configuration Invoke-CommandToJSON -Command { Get-ComputerInfo } -OutputDirectory $diagPath -FileName "Get-ComputerInfo.json" Invoke-CommandToJSON -Command { Get-HotFix } -OutputDirectory $diagPath -FileName "Get-HotFix.json" # collect event logs $exportResults = Export-AzsSupportEventLog -LogName $winEventsToExport -Destination $diagPath $exportResults | Export-ObjectToFile -OutputDirectory $diagPath -FileName "Export-AzsSupportEventLog_Summary.json" # compress the results unless operator has request to skip compression if (!$SkipCompression) { $result = Compress-AzsSupportArchive -Path $diagPath -Destination "$diagPath.zip" } else { $result = Get-Item -Path $diagPath } return $result } function Show-AzsSupportSDNStateSummary { <# .SYNOPSIS Checks the current status of SDN, including if FCNC is deployed and if SDN services are online. .DESCRIPTION This function checks the network controller configuration in ECE. Additionally, it checks the status of the SDN services. .OUTPUTS None. This function displays formatted output to the console with color-coded information. .EXAMPLE Show-AzsSupportSDNStateSummary #> Import-Module ECEClient -DisableNameChecking $eceClient = Create-ECEClusterServiceClient -Verbose:$false -ErrorAction Stop # Check for our FCNC fully deployed + MOC hydrated variable $FCNCCompleteFlag = $false $params = [xml] ($eceClient.GetCloudParameters().GetAwaiter().GetResult().CloudDefinitionAsXmlString) $SDNParams = $params.Parameters.Category | Where-Object { $_.Name -eq "SDNIntegration" } $eceNCConfig = $SDNParams.Parameter | Where-Object { $_.Name -eq "NetworkControllerConfiguration" } # ECE NC configuration pre-existed if ($eceNCConfig.Value) { $eceNCConfigJSON = ConvertFrom-Json -InputObject $eceNCConfig.Value $FCNCCompleteFlag = $eceNCConfigJSON.FCNCInstallFlag } # Check for SDN services try { $NcOwnerGroupNames = @( "ApiService", "ControllerService", "FirewallService", "FnmService", "GatewayManager", "SlbManagerService", "VSwitchService" ) $ExpectedResourceCount = $NcOwnerGroupNames.Count $cluster = Get-Cluster $ncResources = Get-ClusterResource -Cluster $cluster | Where-Object { $_.OwnerGroup -in $NcOwnerGroupNames } # Capture details as PSObjects (stable for formatting) $clusterResources = $ncResources | ForEach-Object { [pscustomobject]@{ Name = $_.Name OwnerGroup = $_.OwnerGroup OwnerNode = $_.OwnerNode State = $_.State ResourceType = $_.ResourceType StatusInformation = $_.StatusInformation } } $failedResources = $clusterResources | Where-Object { $_.State -ne 'Online' } } catch { $_ | Trace-Exception Trace-Output -Level:Error -Message "Failed to query cluster/SDN services." } # Get NC URL try { $res = Get-NetworkControllerOnFailoverCluster -Verbose:$false 4>$null $NCURL = "https://$($res.RestCertificateSubjectName)/" } catch { $_ | Trace-Exception Trace-Output -Level:Error -Message "Failed to get SDN URL" } # Get NC services Write-Host "" Write-Host "========== SDN State Summary ==========" -ForegroundColor Green Write-Host ("FCNC installed + MOC hydrated : " + ($(if ($FCNCCompleteFlag) { "Yes" }else { "No" }))) Write-Host ("NC URL : " + ($(if ($ncUrl) { $ncUrl }else { "(not found)" }))) if ($failedResources.Count -gt 0) { Write-Host "" Write-Host "== Failed SDN resources:" -ForegroundColor Red Write-Host ($failedResources | Format-Table Name, OwnerGroup, OwnerNode, State, ResourceType -AutoSize | Out-String) } elseif ($FCNCCompleteFlag -eq $true) { Write-Host "" Write-Host "All SDN resources online." -ForegroundColor Green } if ($FCNCCompleteFlag -eq $true) { Write-Host "" Write-Host "SDN Services:" -ForegroundColor Cyan Write-Host ($clusterResources | Format-Table Name, OwnerGroup, OwnerNode, State, ResourceType -AutoSize | Out-String) } } # SIG # Begin signature block # MIIoUgYJKoZIhvcNAQcCoIIoQzCCKD8CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAXRoHb+0g/3hwV # 3WHev0zzOexRxiUIycqCXQHI2QuQDKCCDYUwggYDMIID66ADAgECAhMzAAAEhJji # EuB4ozFdAAAAAASEMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjUwNjE5MTgyMTM1WhcNMjYwNjE3MTgyMTM1WjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQDtekqMKDnzfsyc1T1QpHfFtr+rkir8ldzLPKmMXbRDouVXAsvBfd6E82tPj4Yz # aSluGDQoX3NpMKooKeVFjjNRq37yyT/h1QTLMB8dpmsZ/70UM+U/sYxvt1PWWxLj # MNIXqzB8PjG6i7H2YFgk4YOhfGSekvnzW13dLAtfjD0wiwREPvCNlilRz7XoFde5 # KO01eFiWeteh48qUOqUaAkIznC4XB3sFd1LWUmupXHK05QfJSmnei9qZJBYTt8Zh # ArGDh7nQn+Y1jOA3oBiCUJ4n1CMaWdDhrgdMuu026oWAbfC3prqkUn8LWp28H+2S # LetNG5KQZZwvy3Zcn7+PQGl5AgMBAAGjggGCMIIBfjAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUBN/0b6Fh6nMdE4FAxYG9kWCpbYUw # VAYDVR0RBE0wS6RJMEcxLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJh # dGlvbnMgTGltaXRlZDEWMBQGA1UEBRMNMjMwMDEyKzUwNTM2MjAfBgNVHSMEGDAW # gBRIbmTlUAXTgqoXNzcitW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8v # d3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIw # MTEtMDctMDguY3JsMGEGCCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDov # L3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDEx # XzIwMTEtMDctMDguY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIB # AGLQps1XU4RTcoDIDLP6QG3NnRE3p/WSMp61Cs8Z+JUv3xJWGtBzYmCINmHVFv6i # 8pYF/e79FNK6P1oKjduxqHSicBdg8Mj0k8kDFA/0eU26bPBRQUIaiWrhsDOrXWdL # m7Zmu516oQoUWcINs4jBfjDEVV4bmgQYfe+4/MUJwQJ9h6mfE+kcCP4HlP4ChIQB # UHoSymakcTBvZw+Qst7sbdt5KnQKkSEN01CzPG1awClCI6zLKf/vKIwnqHw/+Wvc # Ar7gwKlWNmLwTNi807r9rWsXQep1Q8YMkIuGmZ0a1qCd3GuOkSRznz2/0ojeZVYh # ZyohCQi1Bs+xfRkv/fy0HfV3mNyO22dFUvHzBZgqE5FbGjmUnrSr1x8lCrK+s4A+ # bOGp2IejOphWoZEPGOco/HEznZ5Lk6w6W+E2Jy3PHoFE0Y8TtkSE4/80Y2lBJhLj # 27d8ueJ8IdQhSpL/WzTjjnuYH7Dx5o9pWdIGSaFNYuSqOYxrVW7N4AEQVRDZeqDc # fqPG3O6r5SNsxXbd71DCIQURtUKss53ON+vrlV0rjiKBIdwvMNLQ9zK0jy77owDy # XXoYkQxakN2uFIBO1UNAvCYXjs4rw3SRmBX9qiZ5ENxcn/pLMkiyb68QdwHUXz+1 # fI6ea3/jjpNPz6Dlc/RMcXIWeMMkhup/XEbwu73U+uz/MIIHejCCBWKgAwIBAgIK # YQ6Q0gAAAAAAAzANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNV # BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv # c29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlm # aWNhdGUgQXV0aG9yaXR5IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEw # OTA5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE # BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYD # VQQDEx9NaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG # 9w0BAQEFAAOCAg8AMIICCgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+la # UKq4BjgaBEm6f8MMHt03a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc # 6Whe0t+bU7IKLMOv2akrrnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4D # dato88tt8zpcoRb0RrrgOGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+ # lD3v++MrWhAfTVYoonpy4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nk # kDstrjNYxbc+/jLTswM9sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6 # A4aN91/w0FK/jJSHvMAhdCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmd # X4jiJV3TIUs+UsS1Vz8kA/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL # 5zmhD+kjSbwYuER8ReTBw3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zd # sGbiwZeBe+3W7UvnSSmnEyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3 # T8HhhUSJxAlMxdSlQy90lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS # 4NaIjAsCAwEAAaOCAe0wggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRI # bmTlUAXTgqoXNzcitW2oynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTAL # BgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBD # uRQFTuHqp8cx0SOJNDBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jv # c29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf # MDNfMjIuY3JsMF4GCCsGAQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3 # dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf # MDNfMjIuY3J0MIGfBgNVHSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEF # BQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1h # cnljcHMuaHRtMEAGCCsGAQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkA # YwB5AF8AcwB0AGEAdABlAG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn # 8oalmOBUeRou09h0ZyKbC5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7 # v0epo/Np22O/IjWll11lhJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0b # pdS1HXeUOeLpZMlEPXh6I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/ # KmtYSWMfCWluWpiW5IP0wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvy # CInWH8MyGOLwxS3OW560STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBp # mLJZiWhub6e3dMNABQamASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJi # hsMdYzaXht/a8/jyFqGaJ+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYb # BL7fQccOKO7eZS/sl/ahXJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbS # oqKfenoi+kiVH6v7RyOA9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sL # gOppO6/8MO0ETI7f33VtY5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtX # cVZOSEXAQsmbdlsKgEhr/Xmfwb1tbWrJUnMTDXpQzTGCGiMwghofAgEBMIGVMH4x # CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt # b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01p # Y3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTECEzMAAASEmOIS4HijMV0AAAAA # BIQwDQYJYIZIAWUDBAIBBQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQw # HAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIF0k # txxDGesgAfum/9jqu8SkvUQbJpJB08bHq6lwTjiiMEIGCisGAQQBgjcCAQwxNDAy # oBSAEgBNAGkAYwByAG8AcwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20wDQYJKoZIhvcNAQEBBQAEggEAVo8SX5wpcjRXsStn0OrkK0pTWw2ccGhx/mfO # wHMBr5TQuKvtUGJhepTnsITNShdNuH5+iWiyl7jKscwSoHkhnsaIWFvmkf/aDI/y # 3BOfwkVr9etYv3Lm1fgkRWhXOv3FjKK10QMPtYVw4XKvN5Z9bnzykmpxsVfp/JJU # GzN/FZQ28j//bCueVcYCTEnWcqe5rBuvg7Qv3+04xAdLw7ZyxkGPW5sfQXP3i3b0 # 7GuwlHGfgKBjwpNFu8hG6OWw+XxUatpX3wvx6GPW9pBSLbgq0KW7SORDeAh0OGqu # J+YXfEdD+FcUFEg9JIl7y/Oiw6/GWEeUD1MoRW73iJqUpRmFJaGCF60wghepBgor # BgEEAYI3AwMBMYIXmTCCF5UGCSqGSIb3DQEHAqCCF4YwgheCAgEDMQ8wDQYJYIZI # AWUDBAIBBQAwggFaBgsqhkiG9w0BCRABBKCCAUkEggFFMIIBQQIBAQYKKwYBBAGE # WQoDATAxMA0GCWCGSAFlAwQCAQUABCDbU/T8H4VT0TWZHNa86b8KfFRUTdJzL8HZ # hOhKsUEuDAIGaZhQYhrdGBMyMDI2MDIyNDE4NDcxMi4xOThaMASAAgH0oIHZpIHW # MIHTMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH # UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQL # EyRNaWNyb3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJzAlBgNVBAsT # Hm5TaGllbGQgVFNTIEVTTjo1MjFBLTA1RTAtRDk0NzElMCMGA1UEAxMcTWljcm9z # b2Z0IFRpbWUtU3RhbXAgU2VydmljZaCCEfswggcoMIIFEKADAgECAhMzAAACF3H7 # LqWvAR3qAAEAAAIXMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w # IFBDQSAyMDEwMB4XDTI1MDgxNDE4NDgyM1oXDTI2MTExMzE4NDgyM1owgdMxCzAJ # BgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25k # MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xLTArBgNVBAsTJE1pY3Jv # c29mdCBJcmVsYW5kIE9wZXJhdGlvbnMgTGltaXRlZDEnMCUGA1UECxMeblNoaWVs # ZCBUU1MgRVNOOjUyMUEtMDVFMC1EOTQ3MSUwIwYDVQQDExxNaWNyb3NvZnQgVGlt # ZS1TdGFtcCBTZXJ2aWNlMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA # wM82sEw+39vYR7iGCIFDnYNhRM+BzF2AYiq5dUpZpJFPRjCcipQ6RUbI+RAYNRAp # Exx5ygrXbaWtuwvqsqAVSWbU/W6fecujjILkPqn9pngtWRkfQgbYgvaXALl6PY2y # OH9f72MD+6AyxQenSpAMdUzY/Qk/jtjsHdFXVBe+tshlIkSJ3GZw8VVKqTg3GZEl # ztwbJWNtrhBEvhf6anxMegQMJP7tO8/BJ7ITs4/AV3D2bv8eHk81Y+fOmQ8mQ61W # Lq2wItvlzIT5bzelK9LvEycf5x1lXxAwEw5a7dpS+CKTanhtv+Q2mwebAybjf9io # 4k48stTaq1rtcrOiDwddqVm1S9e8h1TszXFzjLLvE9EmjnNfIewsY+RChUaHnY4F # FwwJEnEv/JS76oHT0oGdy7+J60fGOl7A1UoUyAkhpb2Bja+SwSIiHbQ4FDyJiLlZ # 6drZZ84MoJ852JSxM0hBjGO6FZlPO8iuNyk680Di8VnbSNpIdJN+DhlepeTUMBDH # qCmd0mVWRWZPm1pvgty93asNt/Ng6o4m2dnooWOdM3yKsJaWjyHqic9gfTrZBM+P # CXqeTaO1oEiaQ+h4w0nHVdV+XSvI2m1yN4iibqjm5HPaAO3OJ+OmNLftNVmr4Z6U # 2T6pIcLBysoKcDUvCqycXj4C/+n1KFBpDGdDMw9gmu8CAwEAAaOCAUkwggFFMB0G # A1UdDgQWBBRQrN9jlwNOoeE5ZQqnF5x8S1bJQzAfBgNVHSMEGDAWgBSfpxVdAF5i # XYP05dJlpxtTNRnpcjBfBgNVHR8EWDBWMFSgUqBQhk5odHRwOi8vd3d3Lm1pY3Jv # c29mdC5jb20vcGtpb3BzL2NybC9NaWNyb3NvZnQlMjBUaW1lLVN0YW1wJTIwUENB # JTIwMjAxMCgxKS5jcmwwbAYIKwYBBQUHAQEEYDBeMFwGCCsGAQUFBzAChlBodHRw # Oi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY3Jvc29mdCUyMFRp # bWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNydDAMBgNVHRMBAf8EAjAAMBYGA1Ud # JQEB/wQMMAoGCCsGAQUFBwMIMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQsF # AAOCAgEARmgFdhB7xIAIHEEg5I/5S+gx67aR6RiW8ZAwtE3mz8o0dyn+pIP+lidN # R1IKQQ0r+RjYgI9cZ6mbvAyvh3e2q/BV8rjHE3ud9PyYyq32euFgdZ3vX4b5QXeP # WlpBAYrdziR27rHz6WwpH5dZsSypbXDBbQkWkNl6g82yTy3AbBbKDXBdzxZsEaua # OplatK7Er4dhglKBex8JQ2dMSkSZweCNDXqd9r/9W2VdRZsDJKP/Xc4UyQlVsboB # otKtYESXFkjwR1HVsH+Q0C69/N5CP/Tq3YgI1ub4b9+3MJFKWhJXCcJGFZkcLwUm # YwoFg1XLo7DLJdGjrIH1jsI2NFXJFQHef6AdRe1ERvYQeqtyrBvxIvR+P/83FNYy # zx04inUT9TF2AwTOuqCC6Z67oNwR4pEEJyAIEREvkdhjjfWcgsk/nGTlfahvNY/S # OHrNRKo49KDlccNzRCJQyQ+D59r7/qebNSyQPTfwI9++jEY0Q/UWKVNLhio55GYB # seJ99s7NzkdxOr9Uftp597HEovbA69qGlZ3OpUE3H1RBGDVp/FvM2uXTum8LrMkP # Xx5Ap/kbPASsC9ju9oMCe2IEXO2SeD1aD3IqvAOdHFKHg1vpbPUQSWb6g2xfBV30 # wFcqaPYgzcbxPWPyZqK+S8l7zw64aO5hmJ7eQwoMfTu0Vay6r48wggdxMIIFWaAD # AgECAhMzAAAAFcXna54Cm0mZAAAAAAAVMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYD # VQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEe # MBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3Nv # ZnQgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAxMDAeFw0yMTA5MzAxODIy # MjVaFw0zMDA5MzAxODMyMjVaMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo # aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y # cG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEw # MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA5OGmTOe0ciELeaLL1yR5 # vQ7VgtP97pwHB9KpbE51yMo1V/YBf2xK4OK9uT4XYDP/XE/HZveVU3Fa4n5KWv64 # NmeFRiMMtY0Tz3cywBAY6GB9alKDRLemjkZrBxTzxXb1hlDcwUTIcVxRMTegCjhu # je3XD9gmU3w5YQJ6xKr9cmmvHaus9ja+NSZk2pg7uhp7M62AW36MEBydUv626GIl # 3GoPz130/o5Tz9bshVZN7928jaTjkY+yOSxRnOlwaQ3KNi1wjjHINSi947SHJMPg # yY9+tVSP3PoFVZhtaDuaRr3tpK56KTesy+uDRedGbsoy1cCGMFxPLOJiss254o2I # 5JasAUq7vnGpF1tnYN74kpEeHT39IM9zfUGaRnXNxF803RKJ1v2lIH1+/NmeRd+2 # ci/bfV+AutuqfjbsNkz2K26oElHovwUDo9Fzpk03dJQcNIIP8BDyt0cY7afomXw/ # TNuvXsLz1dhzPUNOwTM5TI4CvEJoLhDqhFFG4tG9ahhaYQFzymeiXtcodgLiMxhy # 16cg8ML6EgrXY28MyTZki1ugpoMhXV8wdJGUlNi5UPkLiWHzNgY1GIRH29wb0f2y # 1BzFa/ZcUlFdEtsluq9QBXpsxREdcu+N+VLEhReTwDwV2xo3xwgVGD94q0W29R6H # XtqPnhZyacaue7e3PmriLq0CAwEAAaOCAd0wggHZMBIGCSsGAQQBgjcVAQQFAgMB # AAEwIwYJKwYBBAGCNxUCBBYEFCqnUv5kxJq+gpE8RjUpzxD/LwTuMB0GA1UdDgQW # BBSfpxVdAF5iXYP05dJlpxtTNRnpcjBcBgNVHSAEVTBTMFEGDCsGAQQBgjdMg30B # ATBBMD8GCCsGAQUFBwIBFjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3Bz # L0RvY3MvUmVwb3NpdG9yeS5odG0wEwYDVR0lBAwwCgYIKwYBBQUHAwgwGQYJKwYB # BAGCNxQCBAweCgBTAHUAYgBDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQFMAMB # Af8wHwYDVR0jBBgwFoAU1fZWy4/oolxiaNE9lJBb186aGMQwVgYDVR0fBE8wTTBL # oEmgR4ZFaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMv # TWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3JsMFoGCCsGAQUFBwEBBE4wTDBKBggr # BgEFBQcwAoY+aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNS # b29DZXJBdXRfMjAxMC0wNi0yMy5jcnQwDQYJKoZIhvcNAQELBQADggIBAJ1Vffwq # reEsH2cBMSRb4Z5yS/ypb+pcFLY+TkdkeLEGk5c9MTO1OdfCcTY/2mRsfNB1OW27 # DzHkwo/7bNGhlBgi7ulmZzpTTd2YurYeeNg2LpypglYAA7AFvonoaeC6Ce5732pv # vinLbtg/SHUB2RjebYIM9W0jVOR4U3UkV7ndn/OOPcbzaN9l9qRWqveVtihVJ9Ak # vUCgvxm2EhIRXT0n4ECWOKz3+SmJw7wXsFSFQrP8DJ6LGYnn8AtqgcKBGUIZUnWK # NsIdw2FzLixre24/LAl4FOmRsqlb30mjdAy87JGA0j3mSj5mO0+7hvoyGtmW9I/2 # kQH2zsZ0/fZMcm8Qq3UwxTSwethQ/gpY3UA8x1RtnWN0SCyxTkctwRQEcb9k+SS+ # c23Kjgm9swFXSVRk2XPXfx5bRAGOWhmRaw2fpCjcZxkoJLo4S5pu+yFUa2pFEUep # 8beuyOiJXk+d0tBMdrVXVAmxaQFEfnyhYWxz/gq77EFmPWn9y8FBSX5+k77L+Dvk # txW/tM4+pTFRhLy/AsGConsXHRWJjXD+57XQKBqJC4822rpM+Zv/Cuk0+CQ1Zyvg # DbjmjJnW4SLq8CdCPSWU5nR0W2rRnj7tfqAxM328y+l7vzhwRNGQ8cirOoo6CGJ/ # 2XBjU02N7oJtpQUQwXEGahC0HVUzWLOhcGbyoYIDVjCCAj4CAQEwggEBoYHZpIHW # MIHTMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH # UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQL # EyRNaWNyb3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJzAlBgNVBAsT # Hm5TaGllbGQgVFNTIEVTTjo1MjFBLTA1RTAtRDk0NzElMCMGA1UEAxMcTWljcm9z # b2Z0IFRpbWUtU3RhbXAgU2VydmljZaIjCgEBMAcGBSsOAwIaAxUAabKAFaKt2haU # dqkHfFYzAzfgSMuggYMwgYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz # aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENv # cnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAx # MDANBgkqhkiG9w0BAQsFAAIFAO1IFKswIhgPMjAyNjAyMjQxMjE0MzVaGA8yMDI2 # MDIyNTEyMTQzNVowdDA6BgorBgEEAYRZCgQBMSwwKjAKAgUA7UgUqwIBADAHAgEA # AgILNzAHAgEAAgISxzAKAgUA7UlmKwIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgor # BgEEAYRZCgMCoAowCAIBAAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBCwUA # A4IBAQBdzeQtFABdeaWUdcHGG8+HHrwf9oY2AV7r7YsYdGZKHua1idoHdYvdm/eE # GUYVpzfLMEnYC2bc6AOqTAmTKC891bH501x0lz6Vhbcw/CBG/2TKKwWhSIdiMkCS # ZACDPoanqX8XuSFxrSeDgwaEZaXATlxHe5Q2eeB7go0PAOWOdUb2WOmD8LiXe3Pd # a4zMUmfB6lpMPK2lYrZvsxQzBSq592HjOYZaYNyzDT09nqpFdyzqNjLxjJU9vabV # TLau5mL2BWxSd5Rua6vuYNk3gXNYmNB9cyvuJwG14QYXdWECPCKU647mFFVKLYgR # L91UJzZSzatynPSw+Zb0SIM5RHVhMYIEDTCCBAkCAQEwgZMwfDELMAkGA1UEBhMC # VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV # BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp # bWUtU3RhbXAgUENBIDIwMTACEzMAAAIXcfsupa8BHeoAAQAAAhcwDQYJYIZIAWUD # BAIBBQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0B # CQQxIgQgQNEC6XSVgBYW/SpBhtFR8KyWfxcbM53PIFQszDuMmIEwgfoGCyqGSIb3 # DQEJEAIvMYHqMIHnMIHkMIG9BCDQ8lBgPl23yZ0SzUSt5phOIegHPywrkNwevxe2 # k+RaWzCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u # MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp # b24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAC # F3H7LqWvAR3qAAEAAAIXMCIEII9qQfG7FHT1KTi9mXYImsvSwE7gAoIakBtuqzQn # TwyoMA0GCSqGSIb3DQEBCwUABIICAEB37TxEs4Fk1ncmlwijIoaOqzzWwhzJLOi7 # W/A7dKfo5h6IcpAY44wITr+GqWwDtkeMPnQRQddDTHQMXUxW2J0CzLB4pamMr27e # vJpGuJmn1ByMhP3xVaB/7oJqpklT6A440Owr1RxAChVikiAtVedChRFzun+wizwx # G3CkhseDhZlbRGkx9yVax+r30gtw5SIGFUnOZmhieXo9A+S7MlXhcJ3oQ3m1Lt2Z # inx35fX/QJuBKxCkrRU6qOYUq5LJxoJD75jzFUz5UBEfJZ3ulrYZ7CZP+Rl2wbAp # qveArR5h0Y4yYHvJIdGWKZAOyhDZhNj0rQZRReE8qqGZY/XucV1lV96U09JqnCxw # p50BsbOYUlO2Tj1aerU+R+kNjZY5QlwafOSIwpd9MKFhxMlvJpLK0FgeK3+TnaY3 # VcWFk2C5UsbJ8oL9VcpKvrypDqlqdYGHbiK4ecP5yWz3/uNB3dniDSvUEjq0OM4y # KXdXvAnQsTle16eZTPc5H1b7yXYdZHpVR045oJceVEjSLoqgzK0NFTs8webztLuL # LlzpPedSo5HtmiWkNj1ih5G4C3UtrfJD86zjkIHQ/gCCgPImYEm1n07/+hiz6eNh # CbUYSw/JPv6X4o3qVrAqgDYdmhTx15JANwjL84+P6WFSOi/W+zkcZP3ZGY0z/iDv # P2hj87hw # SIG # End signature block |