AzStackHCIARBStack/AzStackHci.ARBStack.Helpers.psm1
|
Import-LocalizedData -BindingVariable lARBSTxt -FileName AzStackHci.ARBStack.Strings.psd1 function Test-ARBStackBandwidth { <# .SYNOPSIS Verify that the bandwidth is enough to download ARB Canary Image. .DESCRIPTION Verify that the bandwidth is enough to download ARB Canary Image. .PARAMETER PsSession Specify the PsSession(s) used to validation from. .PARAMETER OperationType Specify the Operation Type to target for ARBStack validation. e.g. Deployment, Update, etc #> [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [string[]] $OperationType ) try { Log-Info -Message ($lARBSTxt.BandwidthInfo) -Type Info # Import DownloadSdk module for bandwidth test Log-Info -Message ($lARBSTxt.ImportInfo -f "DownloadSdk") -Type Info Import-Module DownloadSdk -Verbose:$false -DisableNameChecking $severity = 'WARNING' # or 'CRITICAL' or 'INFORMATIONAL' $status = "SUCCESS" $nodeName = $ENV:COMPUTERNAME # DownloadSDK test params $timeout = 320 $downloadSDKTimeout = "$($timeout)s" $canaryImageVersion = "0.1.37.10128" $elapsedTime = [System.Diagnostics.Stopwatch]::StartNew() try { Get-DownloadSdkRelease -CatalogName arc-appliance-stable-catalogs-ext -Name mariner-canary-vhdx-rpm-stable -Version $canaryImageVersion -Audience stable -Destination . -Parts 10 -Timeout $downloadSDKTimeout -HTTPS: $true -Verbose | Out-Null $detail = ($lARBSTxt.BandwidthSuccess -f $nodeName, $elapsedTime.Elapsed.Seconds, $timeout) } catch { $status = "FAILURE" $detail = ($lARBSTxt.BandwidthFail -f $nodeName, $elapsedTime.Elapsed.Seconds, $timeout) Log-Info -Message ($_.Exception.Message) -Type Error Log-Info -Message $detail -Type Error } finally { $elapsedTime.Stop() } $additionalData = @{ Status = $status Source = $nodeName Resource = $nodeName Detail = $detail } $params = @{ Name = 'AzStackHci_ARBStack_ARBStackBandwidth' Title = 'ARBStack Cluster Bandwidth' DisplayName = 'ARBStack Cluster Bandwidth' Severity = $severity Description = "Test to check if ARB canary image can be downloaded in $timeout seconds" Tags = @{ OperationType = $operationType } Remediation = 'Confirm that the network bandwidth is sufficient and performing optimally' TargetResourceID = $nodeName TargetResourceName = $nodeName TargetResourceType = 'Node' Timestamp = [datetime]::UtcNow Status = $status AdditionalData = $additionalData HealthCheckSource = $ENV:EnvChkrId } $instanceResults = New-AzStackHciResultObject @params return $instanceResults } catch { throw $_ } } function Test-AzCLIExtensionsOutsideVR { <# .SYNOPSIS Verify that known incompatible Azure CLI extensions currently installed are detected. .DESCRIPTION Verify that known incompatible Azure CLI extensions currently installed are detected. .PARAMETER PsSession Specify the PsSession(s) used to validation from. .PARAMETER OperationType Specify the Operation Type to target for ARBStack validation. e.g. Deployment, Update, etc #> [CmdletBinding()] param ( [Parameter()] [System.Management.Automation.Runspaces.PSSession[]] $PsSession, [Parameter()] [string] $scenario, [Parameter(Mandatory = $false)] [string[]] $OperationType ) try { $severity = 'INFORMATIONAL' # or 'CRITICAL' or 'WARNING' # Import helper modules $azureStackNugetPath = Get-ASArtifactPath -NugetName "Microsoft.AzureStack.Solution.Deploy.CloudDeployment" $roleHelpersPath = Join-Path $azureStackNugetPath "content\Roles\Common\RoleHelpers.psm1" Log-Info -Message ($lARBSTxt.ImportInfo -f $roleHelpersPath) -Type Info Import-Module $roleHelpersPath -Force -DisableNameChecking -Verbose:$false | Out-Null $mocArblifeCycleNugetPath = Get-ASArtifactPath -NugetName "Microsoft.AzureStack.MocArb.LifeCycle" $mocArblifeCycleHelperPath = Join-Path $mocArblifeCycleNugetPath "Content\Scripts\MocArbHelper.psm1" Log-Info -Message ($lARBSTxt.ImportInfo -f $mocArblifeCycleHelperPath) -Type Info Import-Module $mocArblifeCycleHelperPath -Force -DisableNameChecking -Verbose:$false | Out-Null # Get scenario and validated recipe json path $validatedRecipeJson = Get-ValidatedRecipeJsonPath -Scenario $scenario Log-Info -Message ($lARBSTxt.ImportInfo -f $validatedRecipeJson) -Type Info # Get supported extensions from validated recipe json $modulesInVR = Get-Content -Path $validatedRecipeJson -Raw | ConvertFrom-Json $supportedAzCliExtensions = $modulesInVR | Where-Object { $_.Type -eq 'AzCliExtension' } $supportedAzCliExtensionsList = $supportedAzCliExtensions.Name | ForEach-Object { $_.Split('.')[-1].ToLower() } $supportedAzCliExtensionsNames = $supportedAzCliExtensionsList -join ", " Log-Info -Message ($lARBSTxt.AzCLIExtensionsSupported -f $supportedAzCliExtensionsNames) -Type Info # Scriptblock to collect extension data from nodes $scriptblock = { $nodeName = $ENV:COMPUTERNAME try{ $currentlyInstalledExtensions = az extension list --output json --only-show-errors 2>$null | ConvertFrom-Json -ErrorAction SilentlyContinue if ($null -eq $currentlyInstalledExtensions) { $currentlyInstalledExtensions = @() } return @{ NodeName = $nodeName Extensions = $currentlyInstalledExtensions } } catch{ return @{ NodeName = $nodeName Extensions = @() } } } # Collect data from nodes if ($PsSession) { $nodeData = Invoke-Command -Session $PsSession -ScriptBlock $scriptblock } else { $nodeData = @(& $scriptblock) } $results = @() # Process data for each node locally foreach ($node in $nodeData) { $status = "SUCCESS" $nodeName = $node.NodeName $currentlyInstalledExtensions = $node.Extensions $remediation = 'https://learn.microsoft.com/en-us/cli/azure/azure-cli-extensions-overview' Log-Info -Message ($lARBSTxt.AzCLIExtensionsInfo -f $nodeName) -Type Info $extensionNames = if ($currentlyInstalledExtensions) { ($currentlyInstalledExtensions.Name -join ", ") } else { "None" } Log-Info -Message ($lARBSTxt.AzCLIExtensionsCurrent -f $extensionNames) -Type Info # Compare lists and report any unsupported extensions found $unsupportedExtensionsInstalled = @() if ($null -ne $currentlyInstalledExtensions -and $currentlyInstalledExtensions.Count -gt 0) { foreach ($extension in $currentlyInstalledExtensions) { if (-not ($supportedAzCliExtensionsList -contains $extension.Name.ToLower())) { $unsupportedExtensionsInstalled += $extension.Name } } } if ($unsupportedExtensionsInstalled.Count -gt 0) { $unsupportedExtensionsInstalledNames = $unsupportedExtensionsInstalled -join ", " $status = "FAILURE" $detail = ($lARBSTxt.AzCLIExtensionsFail -f $unsupportedExtensionsInstalledNames, $supportedAzCliExtensionsNames, $extensionNames) $remediation = "Please remove unsupported Azure CLI extensions to ensure compatibility ($unsupportedExtensionsInstalledNames)." Log-Info -Message $detail -Type Error } else { $detail = ($lARBSTxt.AzCLIExtensionsSuccess -f $extensionNames) Log-Info -Message $detail -Type Info } $additionalData = @{ Status = $status Source = $nodeName Resource = $nodeName Detail = $detail } $params = @{ Name = 'AzStackHci_ARBStack_AzCLIKnownIncompatibleExtensions' Title = "ARBStack Azure CLI Known Incompatible Extensions for $nodeName" DisplayName = "ARBStack Azure CLI Known Incompatible Extensions for $nodeName" Severity = $severity Description = "Detect known incompatible Azure CLI extensions that should be removed." Tags = @{ OperationType = $OperationType } Remediation = $remediation TargetResourceID = $nodeName TargetResourceName = $nodeName TargetResourceType = 'Node' Timestamp = [datetime]::UtcNow Status = $status AdditionalData = $additionalData HealthCheckSource = $ENV:EnvChkrId } $instanceResults = New-AzStackHciResultObject @params $results += $instanceResults } return $results } catch { throw $_ } } function Test-ARBIsNotLocked { <# .SYNOPSIS Verify that the ARB is not locked. .DESCRIPTION Verify that the ARB is not locked. .PARAMETER PsSession Specify the PsSession(s) used to validation from. .PARAMETER OperationType Specify the Operation Type to target for ARBStack validation. e.g. Deployment, Update, etc #> [CmdletBinding()] param ( [Parameter(Mandatory = $true, HelpMessage = "Azure Environment used for Azure Login")] [string] $EnvironmentName, [Parameter(Mandatory = $true, HelpMessage = "Azure Tenant used for Azure Login")] [string] $TenantId, [Parameter(Mandatory = $true, HelpMessage = "Azure Subscription used for Azure Login")] [string] $SubscriptionId, [Parameter(Mandatory = $false)] [string[]] $OperationType ) try { $severity = "INFORMATIONAL" # or "WARNING" or "CRITICAL" $status = "SUCCESS" $nodeName = $ENV:COMPUTERNAME $commandName = $MyInvocation.MyCommand $global:actionplanid = New-Guid $remediation = "https://github.com/Azure/AzureLocal-Supportability/blob/main/TSG/Update/UpgradeArbAndExtensions_fails_ApplianceResourceScopeLocked.md" # Login to Azure try { Log-Info -Message ($lARBSTxt.LoginInfo) -Type Info $mocArblifeCycleNugetPath = Get-ASArtifactPath -NugetName "Microsoft.AzureStack.MocArb.LifeCycle" $mocArblifeCycleHelperPath = Join-Path $mocArblifeCycleNugetPath "Content\Scripts\MocArbHelper.psm1" Log-Info -Message ($lARBSTxt.ImportInfo -f $mocArblifeCycleHelperPath) -Type Info Import-Module $mocArblifeCycleHelperPath -Force -DisableNameChecking -Verbose:$false | Out-Null Login-AzCliSessionWithMSI -subcriptionId $SubscriptionId -ActionPlanprefix $commandName -CloudName $EnvironmentName | Out-Null Log-Info -Message ($lARBSTxt.LoginSuccess) -Type Info } catch { $status = "FAILURE" $detail = ($lARBSTxt.LoginFail -f $_.Exception.Message) Log-Info -Message $detail -Type Error $remediation = "https://github.com/Azure/AzureLocal-Supportability/blob/main/TSG/EnvironmentValidator/Troubleshooting-MSI-Does-Not-Have-Access-To-Subscription.md" } # Check if ARB is locked only if login was successful try { if ($status -eq "SUCCESS") { Log-Info -Message ($lARBSTxt.ARBLockStatusInfo) -Type Info # Import archci module $archcimodule = Get-InstalledModule archci $archciPsm1Path = Join-Path $archcimodule.InstalledLocation -ChildPath "archci.psm1" Log-Info -Message ($lARBSTxt.ImportInfo -f $archciPsm1Path) -Type Info Import-Module $archciPsm1Path -Force -DisableNameChecking -Verbose:$false | Out-Null # Get ARB name and RG $arc = Get-ArcHciMgmtWithTelemetry -ActionPlanStep $commandName $arbId = $arc.ResourceBridge.Id $arbRG = $arc.ResourceBridge.resourceGroup $arbName = $arc.ResourceBridge.Name $listLocksArgs = "lock list --resource `"$arbName`" --resource-type `"Microsoft.ResourceConnector/appliances`" --resource-group `"$arbRG`" --only-show-errors" $locks = Invoke-ArcHciAzCommand -arguments $listLocksArgs | ConvertFrom-Json if($locks -eq $null -or $locks.Count -eq 0) { $detail = $lARBSTxt.ARBLockStatusSuccess } else { $detail = ($lARBSTxt.ARBLockStatusFail -f $locks.Count, $arbName) throw $detail } } } catch { $status = "FAILURE" Log-Info -Message ($_.Exception.Message) -Type Error $detail = $_.Exception.Message } finally { Logout-AzCliSession } $additionalData = @{ Status = $status Source = $nodeName Resource = "Azure Resource Bridge" Detail = $detail } $params = @{ Name = "AzStackHci_ARBStack_ARBIsLocked" Title = "ARB should not be Locked" DisplayName = "ARB should not be Locked" Severity = $severity Description = "Test to confirm that Azure Resource Bridge is not locked" Tags = @{ OperationType = $operationType } Remediation = $remediation TargetResourceID = $arbId TargetResourceName = $arbName TargetResourceType = "Azure Resource Bridge" Timestamp = [datetime]::UtcNow Status = $status AdditionalData = $additionalData HealthCheckSource = $ENV:EnvChkrId } $instanceResults = New-AzStackHciResultObject @params return $instanceResults } catch { throw $_ } } # SIG # Begin signature block # MIInSQYJKoZIhvcNAQcCoIInOjCCJzYCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBH/g5RoH42tVKb # W43E77tBNZuylp9BmI2DHgpNmJZSRKCCDLowggX1MIID3aADAgECAhMzAAACHU0Z # yE7XD1dIAAAAAAIdMA0GCSqGSIb3DQEBCwUAMFcxCzAJBgNVBAYTAlVTMR4wHAYD # VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBD # b2RlIFNpZ25pbmcgUENBIDIwMjQwHhcNMjYwNDE2MTg1OTQzWhcNMjcwNDE1MTg1 # OTQzWjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE # BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYD # VQQDExVNaWNyb3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IB # DwAwggEKAoIBAQDQvewXxx9gZZFC6Ys1WBay8BJ8kGA4JQnH5CMafqOASlTpK9H8 # o5ZXTXt0caVQTNMUPt445wXYD+dFtaKWTwDn1I52oUSrC9vJin1Gsqt+zyKJL5Dg # 3eQXbQNR61DmMy20GLTIO3SFed9Rfi/ophgCLGFLDR3r0KvHjwMb/jYWS0celV/4 # Lz27LfAekm8v9E5IXaeiXbAUYZKK090n4CVl3JBtbN+9DtI9SNu/yjvozW52/u7R # X/Ttpa/KDlpuokZ+Zcbvmtd9ur9gFLvZzh41o9MsE/clQtdaFWGvuo6Jua/ntpgk # ey3E5/vBFe+MJPG6phdnuo6r57ZudCudiI1bAgMBAAGjggGbMIIBlzAOBgNVHQ8B # Af8EBAMCB4AwHwYDVR0lBBgwFgYKKwYBBAGCN0wIAQYIKwYBBQUHAwMwHQYDVR0O # BBYEFH6QuMwqcPG0hQlQ6c5jCtTTLrVeMEUGA1UdEQQ+MDykOjA4MR4wHAYDVQQL # ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xFjAUBgNVBAUTDTIzMDAxMis1MDc1NTkw # HwYDVR0jBBgwFoAUf1k/VCHarU/vBeXmo9ctBpQSCDEwYAYDVR0fBFkwVzBVoFOg # UYZPaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jcmwvTWljcm9zb2Z0 # JTIwQ29kZSUyMFNpZ25pbmclMjBQQ0ElMjAyMDI0LmNybDBtBggrBgEFBQcBAQRh # MF8wXQYIKwYBBQUHMAKGUWh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMv # Y2VydHMvTWljcm9zb2Z0JTIwQ29kZSUyMFNpZ25pbmclMjBQQ0ElMjAyMDI0LmNy # dDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4ICAQBKTbYOjzwTG/DXGaz9 # s6+fQeaTtDcFmMY+5UyVFCyj7Pv+5i37qfX8lSL/tBIfYQfWsMuBQlfZurJD6r4H # VJ2CeH+1fgiq8dcHdVKoZ3Sa2qXoX3cq9iS8cVb06B7+5/XJ7I0OxHH9fDsvJ3T3 # w5V/ZtAIFmLrl+P0CtG+92uzRsn0nTbdFjOkLMLWPLAU3THohKRlSEMgFJpPkm5n # 5UAZ35xX6FWCrDLsSKb555bTifwa8mJBwdlof0bmfYidH+dxZ1FdDxvLnNl9zeKs # A4kejaaIqqIPguhwAti5Ql7BlTNoJNwxCvBmqW2MQLnCkYN/VVUsR3V2x/rcTNzo # Bf/Z/SpROvdaA2ZOOd1uioXJt3tdLQ7vHpqpib0KfWr/FWXW10q38VxfCnRQBqzb # SuztR7nEMuzX7Ck+B/XaPDXd1qh72+QYyB0Z2VzWmO9zsnb9Uq/dwu8LGeQqnyu6 # 7SDGACvnXii2fb9+US492VTnXSnFKyqwgzUyFMtZK1/sHYTv6bG4TtQUygQxTN+Z # V+aJIlKO2MqZ7bKrAnOzS9m6NgoTdWOq11bTOZwKlIEV/EhV9SWkDmdpR/hPPT2v # 6TEj4F8PT/zHjRezIU5c/DGlt/VhY/pK0XkJtEyMmmS1BMtjU/rqBZVMIm3dnxQs # /TBByr+Cf8Z1r7aifQVQ+WSqzjCCBr0wggSloAMCAQICEzMAAAA5O7Y3Gb8GHWcA # AAAAADkwDQYJKoZIhvcNAQEMBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpX # YXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQg # Q29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRl # IEF1dGhvcml0eSAyMDExMB4XDTI0MDgwODIwNTQxOFoXDTM2MDMyMjIyMTMwNFow # VzELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEo # MCYGA1UEAxMfTWljcm9zb2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAyNDCCAiIwDQYJ # KoZIhvcNAQEBBQADggIPADCCAgoCggIBANgBnB7jOMeqlRYHNa265v4IY9fH8TKh # emHfPINe1gpLaV3dhg324WwH06LcHbpnsBukCDNitryo0dtS/EW6I/yEL/bLSY8h # KpbfQuWusBPr9qazYcDxCW/qnjb5JsI1s8bNOg3bVATvQVL4tcf03aTycsz8QeCd # M0l/yHRObJ9QqazM1r6VPEOJ7LL+uEEb73w6QCuhs89a1uv1zerOYMnsneRRwCbp # yW11IcggU0cRKDDq1pjVJzIbIF6+oiXXbReOsgeI8zu1FyQfK0fVkaya8SmVHQ/t # Of23mZ4W9k0Ri22QW9p3UgSC5OUDktKxxcCmGL6tXLfOGSWHIIV4YrTJTT6PNty5 # REojHJuZHArkF9VnHTERWoTjAzfI3kP+5b4alUdhgAZ7ttOu1bVnXfHaqPYl2rPs # 20ji03LOVWsh/radgE17es5hL+t6lV0eVHrVhsssROWJuz2MXMCt7iw7lFPG9LXK # Gjsmonn2gotGdHIuEg5JnJMJVmixd5LRlkmgYRZKzhxSCwyoGIq0PhaA7Y+VPct5 # pCHkijcIIDm0nlkK+0KyepolcqGm0T/GYQRMhHJlGOOmVQop36wUVUYklUy++vDW # eEgEo4s7hxN6mIbf2MSIQ/iIfMZgJxC69oukMUXCrOC3SkE/xIkgpfl22MM1itkZ # 35nNXkMolU1lAgMBAAGjggFOMIIBSjAOBgNVHQ8BAf8EBAMCAYYwEAYJKwYBBAGC # NxUBBAMCAQAwHQYDVR0OBBYEFH9ZP1Qh2q1P7wXl5qPXLQaUEggxMBkGCSsGAQQB # gjcUAgQMHgoAUwB1AGIAQwBBMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU # ci06AjGQQ7kUBU7h6qfHMdEjiTQwWgYDVR0fBFMwUTBPoE2gS4ZJaHR0cDovL2Ny # bC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMvTWljUm9vQ2VyQXV0MjAx # MV8yMDExXzAzXzIyLmNybDBeBggrBgEFBQcBAQRSMFAwTgYIKwYBBQUHMAKGQmh0 # dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljUm9vQ2VyQXV0MjAx # MV8yMDExXzAzXzIyLmNydDANBgkqhkiG9w0BAQwFAAOCAgEAFJQfOChP7onn6fLI # MKrSlN1WYKwDFgAddymOUO3FrM8d7B/W/iQ6DxXsDn7D5W4wMwYeLystcEqfkjz4 # NURRgazyMu5yRzQh4LqjA4tStTcJh1opExo7nn5PuPBYnbu0+THSuVHTe0VTTPVh # ily/piFrDo3axQ9P4C+Ol5yet+2gTfekICS5xS+cYfSIvgn0JksVBVMYVI5QFu/q # hnLhsEFEUzG8fvv0hjgkO+lkpV9ty6GkN4vdnd7ya6Q6aR9y34aiM1qmxaxBi6OU # nyNl6fkuun/diTFnYDLTppOkr/mg5WSfCiDVMNCxtj4wPKC5OmHm1DQIt/MNokbb # H3UGsFP1QbzsLocuSqLCvH09Io3fDPTmscR9Y75G4qX7RTX8AdBPo0I6OEojf39z # uFZt0qOHm65YWQE69cZM2ueE1MB05dNNgHK9gTE7zKvK/fg8B2qjW88MT/WF5V5u # vZGtqa9FSL2RazArA+rDPuf6JGYz4HpgMZHB4S6szWSKYBv0VisCzfxgeU+dquXW # 9bd0auYlOB58DPcOYKdc3Se94g+xL4pcEhbB54JOgAkwYTu/9dLeH2pDqeJZAABV # DWRQCaXfO5LgyKwKCLYXpigrZYCjUSBcr+Ve8PFWMhVTQl0v4q8J/AUmQN5W4n10 # 1cY2L4A7GTQG1h32HHAvfQESWP0xghnlMIIZ4QIBATBuMFcxCzAJBgNVBAYTAlVT # MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jv # c29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMjQCEzMAAAIdTRnITtcPV0gAAAAAAh0w # DQYJYIZIAWUDBAIBBQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYK # KwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIL6cu9UI # aHt3S8LUkdBXQx7+UosfLCOJQ7o5Q7fQR6HpMEIGCisGAQQBgjcCAQwxNDAyoBSA # EgBNAGkAYwByAG8AcwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20w # DQYJKoZIhvcNAQEBBQAEggEAZAE4dsgXG5yZ1Y79ofm+CJDi5PIuso/eIK3vuFkC # S8g3K8uXafbbtTyPn9ZwAZj6DU5ltER3TonzSci3EHfWhGV5n0hcVMVi+vs0NkSp # Ampb3wfse8SGWt+tt2vUui/ED0ZXYEYV32wPNHl1w0V0kvv2e+CafDAfaOjR7u4l # vwA5kCTcrT//Y2mX8POh8qQkLU7/I0vzb7fCUaRIH03VQaIIeg/rFM9JeyiYBJ3i # gbDgVszfiJxf3Yfga7iXIEpJVsatL53NszIwSOTg0Hu7y+h8Tmqo7l50e3YgRS/H # l5HvH9vK9Rjx2brgmW7VB7r5Oh154GYXtA2hpYyHxMt5MqGCF5cwgheTBgorBgEE # AYI3AwMBMYIXgzCCF38GCSqGSIb3DQEHAqCCF3AwghdsAgEDMQ8wDQYJYIZIAWUD # BAIBBQAwggFSBgsqhkiG9w0BCRABBKCCAUEEggE9MIIBOQIBAQYKKwYBBAGEWQoD # ATAxMA0GCWCGSAFlAwQCAQUABCCs5GhhbLL0Pt+YlaCbBp551Y/C8LTHYN6nbBj1 # jJY62QIGadfEjs1DGBMyMDI2MDUwMzE0MzExMC4xMTZaMASAAgH0oIHRpIHOMIHL # MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVk # bW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxN # aWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRT # UyBFU046OTIwMC0wNUUwLUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0 # YW1wIFNlcnZpY2WgghHtMIIHIDCCBQigAwIBAgITMwAAAiNP2WAkU8/+KwABAAAC # IzANBgkqhkiG9w0BAQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu # Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv # cmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAe # Fw0yNjAyMTkxOTM5NTdaFw0yNzA1MTcxOTM5NTdaMIHLMQswCQYDVQQGEwJVUzET # MBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMV # TWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmlj # YSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046OTIwMC0wNUUw # LUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggIi # MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCK6Q2nk5WUdKzSCSafp+UjUARs # WxHKS63rJhFC/zSabFumTBuaJ0QNrmqevub5Db7fSj5qtwwKnjIO92+HXF67192f # ujL7DFot5WEj/AtEZ/XrzFHimKlN1h6gEQwP5I67wizaPW5ZzSBNpaLBg5oHvASP # OZtwdNUoZ+DQKF3hJl1KZuoIlVK+qi7cLjgak6s5oOZcRCMrKnuC3aoVa6wRDbYv # KUuj7rkFx9KO0PsHJ/k+LnZMggRheh4AVdawyh+oOzKPjlQGUNfSeWUgym2U9CLa # 8tt0mQX4DxDz6+ram50gj1oAfyQ6TQ7r96PADFOKBgaU7+cpHnaZG89dTegQ6ydB # RGIycOw1dRX2eKDRRzziK3cn0WaIm/7OeGsyQKjIzEQuUTDv0Jj/9zQ7truLOOpJ # D98BJVOK7je84Sz2hb3HvUST7j1j2N8peD6olkpFHR/1Z8Jz4F+mkrUF7MmPAirY # HRzunbIg3HrDMNwFYN7yBkDA4/VMo9CY0y9oGUoq2yjbCwTibz9VYl93nB3QQiTC # T9nW3M+TOWB+PMrZpExq1BSHmKPzIqehKqrUDoM33PK+dEKwpYLET6uXq4HuQRMX # WT//sPubUnQAaaUMfQhAZSy23HtxwtN3eK9+T4wCav2wQFt57eUOwUW5/DCzMF9t # ua5He1hNvgcAXaiG1wIDAQABo4IBSTCCAUUwHQYDVR0OBBYEFNbAh89v29nPY9bw # Qb1QYCzxVgeXMB8GA1UdIwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8GA1Ud # HwRYMFYwVKBSoFCGTmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3Js # L01pY3Jvc29mdCUyMFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBsBggr # BgEFBQcBAQRgMF4wXAYIKwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0LmNv # bS9wa2lvcHMvY2VydHMvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIw # MTAoMSkuY3J0MAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgw # DgYDVR0PAQH/BAQDAgeAMA0GCSqGSIb3DQEBCwUAA4ICAQCHQwe7z5tp4NZwAf1c # B+4c9J4svw3P6WqGBMxtqznS6DdzUzStXHCaPZhM41g1iKHNnmcnLjwLujOEaNjh # SnUDiAZqQjW5ZapOBxgc7Egghh9k+r78qWAe3rJ4QohBbhSGdZtKivTRaeRqmnhy # 8+ThrKhzCeEwaarXJimZwSpdQQUDbheWHeyAxASqultd5KO0m/UFvO03tfepqGXA # 4tCg/WGECwKqOjJzpRAfPIB6y1HyVrk+vmL5rpEbTwwLOtX7WxFGG8+cYLk9HjaD # kxraA/HYlKQRx1sdza+w/gulLwgOnByRJKF2rr8M7FNIlwoi6ywFpaNc8A7HewaG # jgw/tfcE260I1XekGluANI9HnONOYWlI7BKBQbWE2teo6vsQ1Vg8B8rTZSePVdmX # L1PPqqs3KVdFKM5kYocPCDM+6VL32IV96sESf2T7DjxanpCg2D2UYj4Z1i7cy8U1 # LLDGg55KWs4af2RRBjH2MulHgAmW5obKxiZCDQjRaroJ2XElXUhigE9BzvhCFbT/ # HDY2vpVpl5HnSpcCSxmL5i5lIT/xbAQMI7Luh75Xrm+IslfFWOGOGMlCp+24qEJE # glXEP7xwsolNdBNndXihhyIefVGlI1DR7xGELiJrk8ifVWYo9XEbEXv/lbvp6F2R # 2UsnweWckvq0y1HWnLHDqH6dPjCCB3EwggVZoAMCAQICEzMAAAAVxedrngKbSZkA # AAAAABUwDQYJKoZIhvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpX # YXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQg # Q29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRl # IEF1dGhvcml0eSAyMDEwMB4XDTIxMDkzMDE4MjIyNVoXDTMwMDkzMDE4MzIyNVow # fDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1Jl # ZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMd # TWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwggIiMA0GCSqGSIb3DQEBAQUA # A4ICDwAwggIKAoICAQDk4aZM57RyIQt5osvXJHm9DtWC0/3unAcH0qlsTnXIyjVX # 9gF/bErg4r25PhdgM/9cT8dm95VTcVrifkpa/rg2Z4VGIwy1jRPPdzLAEBjoYH1q # UoNEt6aORmsHFPPFdvWGUNzBRMhxXFExN6AKOG6N7dcP2CZTfDlhAnrEqv1yaa8d # q6z2Nr41JmTamDu6GnszrYBbfowQHJ1S/rboYiXcag/PXfT+jlPP1uyFVk3v3byN # pOORj7I5LFGc6XBpDco2LXCOMcg1KL3jtIckw+DJj361VI/c+gVVmG1oO5pGve2k # rnopN6zL64NF50ZuyjLVwIYwXE8s4mKyzbnijYjklqwBSru+cakXW2dg3viSkR4d # Pf0gz3N9QZpGdc3EXzTdEonW/aUgfX782Z5F37ZyL9t9X4C626p+Nuw2TPYrbqgS # Uei/BQOj0XOmTTd0lBw0gg/wEPK3Rxjtp+iZfD9M269ewvPV2HM9Q07BMzlMjgK8 # QmguEOqEUUbi0b1qGFphAXPKZ6Je1yh2AuIzGHLXpyDwwvoSCtdjbwzJNmSLW6Cm # gyFdXzB0kZSU2LlQ+QuJYfM2BjUYhEfb3BvR/bLUHMVr9lxSUV0S2yW6r1AFemzF # ER1y7435UsSFF5PAPBXbGjfHCBUYP3irRbb1Hode2o+eFnJpxq57t7c+auIurQID # AQABo4IB3TCCAdkwEgYJKwYBBAGCNxUBBAUCAwEAATAjBgkrBgEEAYI3FQIEFgQU # KqdS/mTEmr6CkTxGNSnPEP8vBO4wHQYDVR0OBBYEFJ+nFV0AXmJdg/Tl0mWnG1M1 # GelyMFwGA1UdIARVMFMwUQYMKwYBBAGCN0yDfQEBMEEwPwYIKwYBBQUHAgEWM2h0 # dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5Lmh0 # bTATBgNVHSUEDDAKBggrBgEFBQcDCDAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMA # QTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbL # j+iiXGJo0T2UkFvXzpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1p # Y3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0w # Ni0yMy5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3 # Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIz # LmNydDANBgkqhkiG9w0BAQsFAAOCAgEAnVV9/Cqt4SwfZwExJFvhnnJL/Klv6lwU # tj5OR2R4sQaTlz0xM7U518JxNj/aZGx80HU5bbsPMeTCj/ts0aGUGCLu6WZnOlNN # 3Zi6th542DYunKmCVgADsAW+iehp4LoJ7nvfam++Kctu2D9IdQHZGN5tggz1bSNU # 5HhTdSRXud2f8449xvNo32X2pFaq95W2KFUn0CS9QKC/GbYSEhFdPSfgQJY4rPf5 # KYnDvBewVIVCs/wMnosZiefwC2qBwoEZQhlSdYo2wh3DYXMuLGt7bj8sCXgU6ZGy # qVvfSaN0DLzskYDSPeZKPmY7T7uG+jIa2Zb0j/aRAfbOxnT99kxybxCrdTDFNLB6 # 2FD+CljdQDzHVG2dY3RILLFORy3BFARxv2T5JL5zbcqOCb2zAVdJVGTZc9d/HltE # AY5aGZFrDZ+kKNxnGSgkujhLmm77IVRrakURR6nxt67I6IleT53S0Ex2tVdUCbFp # AUR+fKFhbHP+CrvsQWY9af3LwUFJfn6Tvsv4O+S3Fb+0zj6lMVGEvL8CwYKiexcd # FYmNcP7ntdAoGokLjzbaukz5m/8K6TT4JDVnK+ANuOaMmdbhIurwJ0I9JZTmdHRb # atGePu1+oDEzfbzL6Xu/OHBE0ZDxyKs6ijoIYn/ZcGNTTY3ugm2lBRDBcQZqELQd # VTNYs6FwZvKhggNQMIICOAIBATCB+aGB0aSBzjCByzELMAkGA1UEBhMCVVMxEzAR # BgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1p # Y3Jvc29mdCBDb3Jwb3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJpY2Eg # T3BlcmF0aW9uczEnMCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOjkyMDAtMDVFMC1E # OTQ3MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiMKAQEw # BwYFKw4DAhoDFQA4RWFs+kTiZnoZiAj1BtYj8zCNaqCBgzCBgKR+MHwxCzAJBgNV # BAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4w # HAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29m # dCBUaW1lLVN0YW1wIFBDQSAyMDEwMA0GCSqGSIb3DQEBCwUAAgUA7aE6szAiGA8y # MDI2MDUwMzAzMDgzNVoYDzIwMjYwNTA0MDMwODM1WjB3MD0GCisGAQQBhFkKBAEx # LzAtMAoCBQDtoTqzAgEAMAoCAQACAgL2AgH/MAcCAQACAhJCMAoCBQDtoowzAgEA # MDYGCisGAQQBhFkKBAIxKDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAI # AgEAAgMBhqAwDQYJKoZIhvcNAQELBQADggEBAK62FgK5K5XFuLoLHYR6IUW+ogJf # KCdyOFrPfdTzuENPYK4pNJRhdSYBwZLnmbGbVkZDDTDbfoHKrru9aeR7Yz01YO+s # KuJlnqXWr1KaCtN+3JD3UPXJVScMbMTniu6dkoWFw2h3TD8IvlxYAub419x2i42f # Xee7sCLaYJYCyEYPuadY7XVpoveMOQeaaUy4CnFaXuxFE6H7kDC6MrIWN1G2mmkJ # WNRsoKoA9Srs2ZfnRZuyX2F0j13KNLZ8Fy+lTnUATgAhEDJHsaKT+A2NJQumzsqy # zINUNUVOO6KLYVQbO6Akb5xWoRsBzRw9dTPYNpAhbhqQtaoC3KGR899zWNsxggQN # MIIECQIBATCBkzB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQ # MA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9u # MSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAiNP # 2WAkU8/+KwABAAACIzANBglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0G # CyqGSIb3DQEJEAEEMC8GCSqGSIb3DQEJBDEiBCAfPqQDXKxAG08exNFEd8o+nCnp # PiEC8aKd2g14A+KlxzCB+gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EIJbwMywR # bvcGiynjnwjAqcaD47yYvebKZRAvtEAR5u6zMIGYMIGApH4wfDELMAkGA1UEBhMC # VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV # BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp # bWUtU3RhbXAgUENBIDIwMTACEzMAAAIjT9lgJFPP/isAAQAAAiMwIgQgit078tPa # fcQnnQptmE1wsN7QTb6Exag5U1V1RqiSX9owDQYJKoZIhvcNAQELBQAEggIAXs0f # IdhgYOHmK3+R42F+p2WN6exYR0YxF23/7a9a9cQW4CmNOB6jXI3klS+GQLCTH8XA # v7iRW/xRZK3WUecZPowU6CjBPiFwb9Qx/ffBGbrHREGCuPh1c3uRF5fph3r8uO27 # PIFtaGccDiMYPOHrEt9L6W84qCoa+dRoD9pL8p2ii1AIDvn2SA+bpnZOba+P3Xkh # jm8cE8o9KABv8bY6pjwj1fm8n5HiiNdd0kS/9rX3cFaXEgc59BTD8nM4/A+dcGQC # BakbHLrgtxPwU1v0vI2EBPkmwQTfCCBOo9wVDm8aKeunWkBDq19Czzs+NYzTbqeE # 60xv/5Ydqh6rXk3rLwjitfvIhSN+5FGmpgXUq0ne8i20mZDHsq2AqIjxzdTEVw24 # r6NTkWPrk1RAjGLJwjCIAHsYXPaB0yYROi7e3e3CYPb3ybb9JfbmoxX59AA+C8zK # ZpKv4OjCTSVSHxbFfPCqrWQD6GyYov3x8dz91Ve6nAhdnqpW1FUZKHjLqWzk3Sxz # Mtl+VRNkq0DeujK98C8LMXm1U3LJGpZxY+04gPftpfjsO88UDddoTPuNgXHMINI1 # jNdCnG22XDUoH2693cwOldCzOqZEdASYG5Jdyb3lw4ZrwOuWfS8PEZqF6pGRLCK6 # kATe48FKKn0wmTwShV7MazcjRElw0IuhvrRopzI= # SIG # End signature block |