AzStackHciSBEHealth/AzStackHci.SBEHealth.Helpers.psm1
Import-LocalizedData -BindingVariable locSbeTxt -FileName AzStackHci.SBEHealth.Strings.psd1 function Copy-SBEContentLocalToNode { [CmdletBinding()] param ( [Parameter(Mandatory=$true)] [string]$PackagePath, [Parameter(Mandatory=$true)] [string]$TargetNodeName, [Parameter(Mandatory=$false)] [string[]]$ExcludeDirs, [Parameter(Mandatory=$false)] [string[]]$ExcludeFiles, [PSCredential]$Credential ) $copyItems = @() $sbeConfig = Get-ASArtifactPath -NugetName "Microsoft.AzureStack.SBEConfiguration" $sbeConfigDest = "" if ($env:ComputerName -eq $TargetNodeName) { $dest = Join-Path -Path $env:SystemDrive -ChildPath "SBE\Extracted" } else { $root = '\\' + $TargetNodeName + '\' + ($env:SystemDrive ).Replace(':','$') $dest = $root + '\SBE\Extracted' $sbeConfigDest = Join-Path -Path $root -ChildPath ($sbeConfig.Replace($env:SystemDrive,'')) $drv = New-PSDrive -Credential $Credential -Name SBE -PSProvider FileSystem -Root $root } $copyItems += @{Source=$PackagePath;Destination=$dest} if (-not([string]::IsNullOrWhitespace($sbeConfigDest))) { $copyItems += @{Source=$sbeConfig;Destination=$sbeConfigDest} } [string]$exclude = "" if ($ExcludeFiles.Count -ne 0) { $exclude += " /XF $ExcludeFiles" } if ($ExcludeDirs.Count -ne 0) { $exclude += " /XD $ExcludeDirs" } foreach ($item in $copyItems) { Log-Info -Message ($locSbeTxt.CopySBEToNode -f $item.Source, $item.Destination) -Type Info $copyCmd = "robocopy.exe $($item.Source) $($item.Destination) *.* /MIR /NP /R:2 /W:10$exclude" $output = Invoke-Command -ScriptBlock { cmd.exe /c $copyCmd } # Check for exit code. If exit code is greater than 7, an error occurred while peforming the copy operation. if ($LASTEXITCODE -ge 8) { Log-Info -Message ($locSbeTxt.RobocopyFailed -f $LASTEXITCODE) -ConsoleOut -Type Error Log-Info -Message ($output | Out-String).Trim() -ConsoleOut -Type Info if ($drv) { $drv | Remove-PSDrive -ErrorAction SilentlyContinue } return $false } } if ($drv) { $drv | Remove-PSDrive -ErrorAction SilentlyContinue } return $true } function Get-SBEHealthCheckParams { # TODO : Implement calling these SBESolutionExtensionHelper functions once they are implemented to supply these param values # $credList = Get-SBECredentialList -SecretStoreConfig [PSObject]$storeConfig # $hostData = Get-AllNodesData -BareMetalConfig [PSObject]$BareMetalConfig # $partnerProp = Get-SBEPartnerProperties -SBERoleConfig [PSObject]$sbeRoleConfig # For $Tag we may need logic to convert EnvChkr Tag to partner Tag values $credList = $null $hostData = $null $partnerProp = $null $params = @{ CredentialList = $credList HostData = $hostData PartnerProperties = $partnerProp Tag = $Tag } return $params } function Test-SolutionExtensionModule { [CmdletBinding()] param ( [Parameter(Mandatory=$true)] [string] $PackagePath, [Parameter()] [System.Management.Automation.Runspaces.PSSession] $PsSession ) $ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop $solExtModule = $null Log-Info -Message ($locSbeTxt.SBEPackagePath -f $PackagePath) -Type Info if ($PSSession) { $computername = $PsSession.ComputerName } else { $computername = $env:ComputerName } # Validate the SolutionExtension module using a function from the SBE Role Helper module $sbValidate = { try { if ($null -eq $PackagePath) { $PackagePath = $using:PackagePath } $sbeRoleNuget = Get-ASArtifactPath -NugetName "Microsoft.AzureStack.Role.SBE" Import-Module "$($sbeRoleNuget)\content\Helpers\SBESolutionExtensionHelper.psm1" -Force -ErrorAction Stop -Verbose:$false -DisableNameChecking -Global | Out-Null $solExtModulePath = Join-Path -Path $PackagePath -ChildPath "Configuration\SolutionExtension" $solExtModule = Initialize-SolutionExtensionModule -SolExtFilePath $solExtModulePath -RequireTag "HealthServiceIntegration" -AssertCertificate return $solExtModule } catch { Write-Output "An exception occurred while validating the SolutionExtension module: " + ($PSItem | Format-List * | Out-String).Trim() } } $solExtModule = if ($PsSession) { Invoke-Command -Session $PsSession -ScriptBlock $sbValidate } else { Invoke-Command -ScriptBlock $sbValidate } if ($null -eq $solExtModule) { Log-Info -Message ($locSbeTxt.NoHeatlhChecks) -Type Info return $false } elseif ($solExtModule -match "An exception occurred") { throw $solExtModule } return $true } function Invoke-TestSBEContentIntegrity { [CmdletBinding()] param ( [Parameter(Mandatory=$true)] [string] $SBEMetadataPath, [Parameter(Mandatory=$true)] [string] $SBEContentPath, [Parameter()] [System.Management.Automation.Runspaces.PSSession] $PsSession ) $sbIntegrity = { if ($null -eq $SBEMetadataPath) { $SBEMetadataPath = $using:SBEMetadataPath } if ($null -eq $SBEContentPath) { $SBEContentPath = $using:SBEContentPath } try { if (-not(Get-Command -Name Test-SBEContentIntegrity -ErrorAction SilentlyContinue)) { $sbeRoleNuget = Get-ASArtifactPath -NugetName "Microsoft.AzureStack.Role.SBE" Import-Module "$($sbeRoleNuget)\content\Helpers\SBESolutionExtensionHelper.psm1" -Force -ErrorAction Stop -Verbose:$false -DisableNameChecking -Global | Out-Null } $skipDir = @("IntegratedContent") Test-SBEContentIntegrity -SBEMetadataDirPath $SBEMetadataPath -SBEContentPath $SBEContentPath -IgnoreTopLevelFolder $skipDir } catch { throw $PSItem } } $result = if ($PsSession) { Invoke-Command -Session $PsSession -ScriptBlock $sbIntegrity } else { Invoke-Command -ScriptBlock $sbIntegrity } return $result } function Import-SolutionExtensionModule { [CmdletBinding()] param ( [Parameter(Mandatory=$true)] [string] $PackagePath, [Parameter()] [System.Management.Automation.Runspaces.PSSession] $PsSession ) $ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop # Import the SolutionExtension module $solExtModule = (Join-Path -Path $PackagePath -ChildPath "Configuration\SolutionExtension\SolutionExtension.psd1") Log-Info -Message ($locSbeTxt.ModuleToImport -f $solExtModule) -Type Info $sbImport = { try { if ($null -eq $solExtModule) { $solExtModule = $using:solExtModule } Import-Module $solExtModule -Force -ErrorAction Stop -Verbose:$false -DisableNameChecking -Global | Out-Null } catch { Write-Output "An error occurred while importing the SolutionExtension module: " + ($PSItem | Format-List * | Out-String).Trim() } } $result = if ($PsSession) { Invoke-Command -Session $PsSession -ScriptBlock $sbImport } else { Invoke-Command -ScriptBlock $sbImport } if ($result -match "An exception occurred") { throw $solExtModule } return $true } function New-SBEHealthResultObject { param ( [Parameter(Mandatory=$true)] [string]$TargetName, [Parameter()] [string]$TestName, [Parameter()] [ValidateSet('CRITICAL','WARNING','INFORMATIONAL')] [string]$Severity = 'INFORMATIONAL', [Parameter()] [ValidateSet('SUCCESS', 'FAILURE', 'ERROR')] [string]$Status, [Parameter()] [string]$Description, [Parameter()] [string]$Detail, [Parameter()] [bool]$PopulateAdditionalData = $true ) $name = 'AzStackHci_SBEHealth' $title = 'SBE' if (-not([string]::IsNullOrWhiteSpace($TestName))) { $name += "_$TestName" $title += " $TestName" } $name += "_$TargetName" if (-not($title.EndsWith(" Health Check"))) { $title += " Health Check" } $params = @{ Name = $name Title = $title DisplayName = $title Severity = $Severity Description = $Description Tags = @{} Remediation = '' TargetResourceID = $TargetName TargetResourceName = $TargetName TargetResourceType = 'SBEHealth' Timestamp = [datetime]::UtcNow Status = $Status AdditionalData = @{ Source = $TargetName Resource = 'SBEHealth' Detail = $Detail Status = $Status TimeStamp = $resultObj.Timestamp } HealthCheckSource = $ENV:EnvChkrId } $resultObj = New-AzStackHciResultObject @params return $resultObj } function Get-ResultObject { $resultObject = @{ "Name" = "" "DisplayName" = "" "Title"= "" "Description" = "" "Status" = "" "Severity" = "" "Timestamp" = "" "TargetResourceID" = "" "TargetResourceName" = "" "TargetResourceType" = "" "Tags" = @{} "AdditionalData" = @{} "HealthCheckSource" = "" "Remediation" = "" } return $resultObject } function Assert-ResponseSchemaValid { [CmdletBinding()] param ( [PSObject[]]$ResultObject ) $expectedSchema = Get-ResultObject foreach ($item in $ResultObject) { # Assert Name or Title must contain information if ([string]::IsNullOrWhiteSpace($item.Name) -and [string]::IsNullOrWhiteSpace($item.Title)) { $msg = "Both Name and Title properties of this result object are empty" Log-Info -Message $msg -Type Error $item.AdditionalData.NameTitleEmpty = $msg $item.Severity = 'CRITICAL' $item.Status = "Error" } elseif ([string]::IsNullOrWhiteSpace($item.Name)) { $item.Name = $item.Title } elseif ([string]::IsNullOrWhiteSpace($item.Title)) { $item.Title = $item.Name } # Assert response contains expected schema properties foreach ($expectedKey in $expectedSchema.Keys) { if (-not($item.ContainsKey($expectedKey))) { # TODO : Temporary special case to add DisplayName if missing due to this being added after partner communication if ($key -eq "DisplayName") { $item.DisplayName = $item.Title } else { Log-Info -Message "Expected result property '$($expectedKey)' was not found" -Type Warning $item.$expectedKey = "" # TODO : In the future, we should decide how to better handle these cases of missing properties } } } # Assert Status values if ($item.Status -notin @("Success", "Failure", "Error")) { $msg = "Unexpected Status: '$($item.Status)'" Log-Info -Message $msg $item.AdditionalData.StatusDiscrepancy = $msg $item.Status = "Error" } # Assert Severity values if ($item.Severity -notin @('CRITICAL', 'WARNING', 'INFORMATIONAL')) { $msg = "Unexpected Severity: '$($item.Severity)'" $item.AdditionalData.SeverityDiscrepancy = $msg if ($item.Status -eq "Success") { $item.Severity = 'WARNING' Log-Info -Message $msg -Type Warning } else { $item.Severity = 'CRITICAL' Log-Info -Message $msg -Type Error } } } return $ResultObject } Export-ModuleMember -Function Test-* Export-ModuleMember -Function New-SBEHealthResultObject Export-ModuleMember -Function Get-SBEHealthCheckParams Export-ModuleMember -Function Copy-SBEContentLocalToNode Export-ModuleMember -Function Import-SolutionExtensionModule Export-ModuleMember -Function Assert-ResponseSchemaValid Export-ModuleMember -Function Invoke-TestSBEContentIntegrity # SIG # Begin signature block # MIIoLQYJKoZIhvcNAQcCoIIoHjCCKBoCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCo2q71+hNAxiPA # zhwld2i0bfi91SEcRPk5rKrOlrQveqCCDXYwggX0MIID3KADAgECAhMzAAADrzBA # DkyjTQVBAAAAAAOvMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjMxMTE2MTkwOTAwWhcNMjQxMTE0MTkwOTAwWjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQDOS8s1ra6f0YGtg0OhEaQa/t3Q+q1MEHhWJhqQVuO5amYXQpy8MDPNoJYk+FWA # hePP5LxwcSge5aen+f5Q6WNPd6EDxGzotvVpNi5ve0H97S3F7C/axDfKxyNh21MG # 0W8Sb0vxi/vorcLHOL9i+t2D6yvvDzLlEefUCbQV/zGCBjXGlYJcUj6RAzXyeNAN # xSpKXAGd7Fh+ocGHPPphcD9LQTOJgG7Y7aYztHqBLJiQQ4eAgZNU4ac6+8LnEGAL # go1ydC5BJEuJQjYKbNTy959HrKSu7LO3Ws0w8jw6pYdC1IMpdTkk2puTgY2PDNzB # tLM4evG7FYer3WX+8t1UMYNTAgMBAAGjggFzMIIBbzAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQURxxxNPIEPGSO8kqz+bgCAQWGXsEw # RQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEW # MBQGA1UEBRMNMjMwMDEyKzUwMTgyNjAfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzci # tW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEG # CCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQu # Y29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0 # MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBAISxFt/zR2frTFPB45Yd # mhZpB2nNJoOoi+qlgcTlnO4QwlYN1w/vYwbDy/oFJolD5r6FMJd0RGcgEM8q9TgQ # 2OC7gQEmhweVJ7yuKJlQBH7P7Pg5RiqgV3cSonJ+OM4kFHbP3gPLiyzssSQdRuPY # 1mIWoGg9i7Y4ZC8ST7WhpSyc0pns2XsUe1XsIjaUcGu7zd7gg97eCUiLRdVklPmp # XobH9CEAWakRUGNICYN2AgjhRTC4j3KJfqMkU04R6Toyh4/Toswm1uoDcGr5laYn # TfcX3u5WnJqJLhuPe8Uj9kGAOcyo0O1mNwDa+LhFEzB6CB32+wfJMumfr6degvLT # e8x55urQLeTjimBQgS49BSUkhFN7ois3cZyNpnrMca5AZaC7pLI72vuqSsSlLalG # OcZmPHZGYJqZ0BacN274OZ80Q8B11iNokns9Od348bMb5Z4fihxaBWebl8kWEi2O # PvQImOAeq3nt7UWJBzJYLAGEpfasaA3ZQgIcEXdD+uwo6ymMzDY6UamFOfYqYWXk # ntxDGu7ngD2ugKUuccYKJJRiiz+LAUcj90BVcSHRLQop9N8zoALr/1sJuwPrVAtx # HNEgSW+AKBqIxYWM4Ev32l6agSUAezLMbq5f3d8x9qzT031jMDT+sUAoCw0M5wVt # CUQcqINPuYjbS1WgJyZIiEkBMIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkq # 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 # Z25pbmcgUENBIDIwMTECEzMAAAOvMEAOTKNNBUEAAAAAA68wDQYJYIZIAWUDBAIB # BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO # MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIOoHRI//OKT0U5s3s7t5PxvY # f+opR1fdoxIq411C0FY1MEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A # cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB # BQAEggEAaq+7aAfUhSSNUMPj6DVchv6OKNpmub09uHDUfxs9BOIyW/hh9qK8zjEZ # bf2H9GgIuPZzPUldqW0Z2a2dJJODcfMV/2CPK8t9FV+PNq1uA8Po5z9wr/QAy5BO # nVLepWNJpSh7Wmt0sywBI93gdL5b3Hs8Z6YRs6FqdwQM0EkfIWVYJ0XRnCqpbSAP # x/HSbgrWtfTC4oCoTDLn440MUrUIyPiPvSvn6H7ST4dS9DCw0rG6S7n/GqWPXh1W # XkHxxBmMOMni/kT7zGH49HtblhzQfkclCuPqNZohB8xs7dnRkLTzmo3VNrtDqpZE # qzuNXmL4V9D7VwmRxsHpNOsmmMmho6GCF5cwgheTBgorBgEEAYI3AwMBMYIXgzCC # F38GCSqGSIb3DQEHAqCCF3AwghdsAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFSBgsq # hkiG9w0BCRABBKCCAUEEggE9MIIBOQIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl # AwQCAQUABCCLy4ton2vMG18MfWxEJuiTP8xoqcMVhCys8SE1nqokaQIGZbwS6brO # GBMyMDI0MDIxMjE0MDcwOC43NTFaMASAAgH0oIHRpIHOMIHLMQswCQYDVQQGEwJV # UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE # ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1l # cmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046MzcwMy0w # NUUwLUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2Wg # ghHtMIIHIDCCBQigAwIBAgITMwAAAeqaJHLVWT9hYwABAAAB6jANBgkqhkiG9w0B # AQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE # BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYD # VQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAeFw0yMzEyMDYxODQ1 # MzBaFw0yNTAzMDUxODQ1MzBaMIHLMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz # aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENv # cnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25z # MScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046MzcwMy0wNUUwLUQ5NDcxJTAjBgNV # BAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggIiMA0GCSqGSIb3DQEB # AQUAA4ICDwAwggIKAoICAQC1C1/xSD8gB9X7Ludoo2rWb2ksqaF65QtJkbQpmsc6 # G4bg5MOv6WP/uJ4XOJvKX/c1t0ej4oWBqdGD6VbjXX4T0KfylTulrzKtgxnxZh7q # 1uD0Dy/w5G0DJDPb6oxQrz6vMV2Z3y9ZxjfZqBnDfqGon/4VDHnZhdas22svSC5G # HywsQ2J90MM7L4ecY8TnLI85kXXTVESb09txL2tHMYrB+KHCy08ds36an7IcOGfR # mhHbFoPa5om9YGpVKS8xeT7EAwW7WbXL/lo5p9KRRIjAlsBBHD1TdGBucrGC3TQX # STp9s7DjkvvNFuUa0BKsz6UiCLxJGQSZhd2iOJTEfJ1fxYk2nY6SCKsV+VmtV5ai # PzY/sWoFY542+zzrAPr4elrvr9uB6ci/Kci//EOERZEUTBPXME/ia+t8jrT2y3ug # 15MSCVuhOsNrmuZFwaRCrRED0yz4V9wlMTGHIJW55iNM3HPVJJ19vOSvrCP9lsEc # EwWZIQ1FCyPOnkM1fs7880dahAa5UmPqMk5WEKxzDPVp081X5RQ6HGVUz6ZdgQ0j # cT59EG+CKDPRD6mx8ovzIpS/r/wEHPKt5kOhYrjyQHXc9KHKTWfXpAVj1Syqt5X4 # nr+Mpeubv+N/PjQEPr0iYJDjSzJrqILhBs5pytb6vyR8HUVMp+mAA4rXjOw42vkH # fQIDAQABo4IBSTCCAUUwHQYDVR0OBBYEFCuBRSWiUebpF0BU1MTIcosFblleMB8G # A1UdIwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8GA1UdHwRYMFYwVKBSoFCG # Tmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUy # MFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBsBggrBgEFBQcBAQRgMF4w # XAYIKwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2Vy # dHMvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3J0MAwG # A1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwDgYDVR0PAQH/BAQD # AgeAMA0GCSqGSIb3DQEBCwUAA4ICAQAog61WXj9+/nxVbX3G37KgvyoNAnuu2w3H # oWZj3H0YCeQ3b9KSZThVThW4iFcHrKnhFMBbXJX4uQI53kOWSaWCaV3xCznpRt3c # 4/gSn3dvO/1GP3MJkpJfgo56CgS9zLOiP31kfmpUdPqekZb4ivMR6LoPb5HNlq0W # bBpzFbtsTjNrTyfqqcqAwc6r99Df2UQTqDa0vzwpA8CxiAg2KlbPyMwBOPcr9hJT # 8sGpX/ZhLDh11dZcbUAzXHo1RJorSSftVa9hLWnzxGzEGafPUwLmoETihOGLqIQl # Cpvr94Hiak0Gq0wY6lduUQjk/lxZ4EzAw/cGMek8J3QdiNS8u9ujYh1B7NLr6t3I # glfScDV3bdVWet1itTUoKVRLIivRDwAT7dRH13Cq32j2JG5BYu/XitRE8cdzaJmD # VBzYhlPl9QXvC+6qR8I6NIN/9914bTq/S4g6FF4f1dixUxE4qlfUPMixGr0Ft4/S # 0P4fwmhs+WHRn62PB4j3zCHixKJCsRn9IR3ExBQKQdMi5auiqB6xQBADUf+F7hSK # ZfbA8sFSFreLSqhvj+qUQF84NcxuaxpbJWVpsO18IL4Qbt45Cz/QMa7EmMGNn7a8 # MM3uTQOlQy0u6c/jq111i1JqMjayTceQZNMBMM5EMc5Dr5m3T4bDj9WTNLgP8SFe # 3EqTaWVMOTCCB3EwggVZoAMCAQICEzMAAAAVxedrngKbSZkAAAAAABUwDQYJKoZI # 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 # MCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOjM3MDMtMDVFMC1EOTQ3MSUwIwYDVQQD # ExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiMKAQEwBwYFKw4DAhoDFQCJ # 2x7cQfjpRskJ8UGIctOCkmEkj6CBgzCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w # IFBDQSAyMDEwMA0GCSqGSIb3DQEBCwUAAgUA6XRolzAiGA8yMDI0MDIxMjA5NTEx # OVoYDzIwMjQwMjEzMDk1MTE5WjB3MD0GCisGAQQBhFkKBAExLzAtMAoCBQDpdGiX # AgEAMAoCAQACAiY/AgH/MAcCAQACAhOAMAoCBQDpdboXAgEAMDYGCisGAQQBhFkK # BAIxKDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAIAgEAAgMBhqAwDQYJ # KoZIhvcNAQELBQADggEBAIeJj1Hw+sb1l/Ps+mMe19bwjz4JymTkkcg4/ZlM8LxH # 9fkRHefrXaZ6DCazKS3epw5b58/BWoFMgcbQ45EggsR0O80tcVQxZZu9i38A//4H # Y6IhXO45bl8BajblrQ+4B0VEH+qxyk2qQKxeyiNaahLczgvfq86urkunZ5LaXGVW # 6Q5NVzIhu1X5Dbx/AwhCqG6BN1PcIYVrcrBJK3ot2XMbL3kSojn7/ahLaswmAmNX # a052rk01sRRF04kuuS1fBFnuwFqwaNQ7nQ1u9z7fl5nPnMrUG+tIJHW4sjYbsuYG # kWVpYpukdWCg/0bLr6S5/5ISkM3NHeSXPoU3LydiMWUxggQNMIIECQIBATCBkzB8 # MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVk # bW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1N # aWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAeqaJHLVWT9hYwABAAAB # 6jANBglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEE # MC8GCSqGSIb3DQEJBDEiBCDlyVnFO+S15NPVjqHtr8PujF6WHKUlHSLO9eoCG36D # OzCB+gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EICmPodXjZDR4iwg0ltLANXBh # 5G1uKqKIvq8sjKekuGZ4MIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgT # Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29m # dCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENB # IDIwMTACEzMAAAHqmiRy1Vk/YWMAAQAAAeowIgQgZ/tX+K4cIAQr7/jYomvwBCtf # vDto7JlyjY5tCOO7B40wDQYJKoZIhvcNAQELBQAEggIAsJVfoywXhNKaEQP8UcPG # j/cAm5glIuDO3/EYSwupv0HXkHQa1Xn+9P46aIvCviA65qvknuzxpJmxIdqfXyDV # mCfgeYoo2STIaSU8sQ3CzN5pH2qt9pcqcw30mv1beUc/qxrrxJfqWVE9APJtlK+h # tJYI4ASicnglSRICQvRNOSgO9CdegML+CQpjTZWTcw+ZAJlzb1fJrZV9mVqsbzyT # WSKtcjBbK8CpjLVIFYG3AcR3YqVdmSbnWmKHb+h552KqTXee77gM1PNPQy/my+bE # JqIE4ffC/LUNSXzN30ihxRELru5tgdQ00wCyiVH14Yhn6r5PJ2JETxfsIezTpxPw # vvcoVo8kWq8bRYu8fasStk2I1Yn1FMaalVk+n72f7ubb0+gifaoL8wFzRUToL6B2 # d7hEB+jsJUVPoeMEq0f41cGEZfZr69YMYdrF22dwnJTFzdPMG58Srl5E8sna9w7c # 0G8Kh8QkjdKCyjp9+OLVbIiyFDH47l9Uml4yjjN23y4S+FGV2Zv8SCT935UOuojs # DW2FjpHz4fie7LfbvgR2GedBumIUw99C6cUs38yBpQEKTmRMjdvSekjruD3Ylm52 # eZxqfL0ER+LPs541X9UyJXwQ8Tv1YBhtKraflY7TuRfGZ2/SEQsGKjMy1lo09BZQ # 0DEJnTnx75fLVDpj0k1YHXU= # SIG # End signature block |