Deploy/Classes/EnvironmentValidator/EnvironmentValidator.psm1
<###################################################
# # # Copyright (c) Microsoft. All rights reserved. # # # ##################################################> #using module ..\Common\Role.psm1 $ErrorActionPreference = "Stop" class Role { } class EnvironmentValidator : Role { static EnvironmentValidatorLite([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { <# .SYNOPSIS This function is used to run environment validator during bootstrap on seed node. .EXAMPLE [EnvironmentValidator]::EnvironmentValidatorLite($Parameters) .PARAMETER Parameters The ECE role parameters for this interface. #> $OperationType = 'Bootstrap' [EnvironmentValidator]::RunValidators($Parameters, $OperationType) } static EnvironmentValidatorFull([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { <# .SYNOPSIS This function is used to run environment validator and run pre-deployment when full ECE parameters are available .EXAMPLE [EnvironmentValidator]::EnvironmentValidatorFull($Parameters) .PARAMETER Parameters The ECE role parameters for this interface. #> $OperationType = 'Deployment' [EnvironmentValidator]::RunValidators($Parameters, $OperationType) } static EnvironmentValidatorUpgrade([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { <# .SYNOPSIS This function is used to run environment validator and run pre-upgrade when full ECE parameters are available .EXAMPLE [EnvironmentValidator]::EnvironmentValidatorUpgrade($Parameters) .PARAMETER Parameters The ECE role parameters for this interface. #> $OperationType = 'Upgrade' [EnvironmentValidator]::RunValidators($Parameters, $OperationType) } static EnvironmentValidatorPreUpdate([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { <# .SYNOPSIS This function is used to run environment validator and run pre-update when full ECE parameters are available .EXAMPLE [EnvironmentValidator]::EnvironmentValidatorPreUpdate($Parameters) .PARAMETER Parameters The ECE role parameters for this interface. #> $OperationType = 'PreUpdate' [EnvironmentValidator]::RunValidators($Parameters, $OperationType) } static EnvironmentValidatorPostUpdate([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { <# .SYNOPSIS This function is used to run environment validator and run post-update when full ECE parameters are available .EXAMPLE [EnvironmentValidator]::EnvironmentValidatorPostUpdate($Parameters) .PARAMETER Parameters The ECE role parameters for this interface. #> $OperationType = 'PostUpdate' [EnvironmentValidator]::RunValidators($Parameters, $OperationType) } static EnvironmentValidatorAddNode([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { <# .SYNOPSIS This function is used to run environment validator and run during AddNode when full ECE parameters are available .EXAMPLE [EnvironmentValidator]::EnvironmentValidatorAddNode($Parameters) .PARAMETER Parameters The ECE role parameters for this interface. #> $OperationType = 'AddNode' [EnvironmentValidator]::RunValidators($Parameters, $OperationType) } # TO DO: Remove PreAddNode static EnvironmentValidatorPreAddNode([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { <# .SYNOPSIS This function is used to run environment validator and run before AddNode when full ECE parameters are available, but new node has not be updated in ECE. .EXAMPLE [EnvironmentValidator]::EnvironmentValidatorPreAddNode($Parameters) .PARAMETER Parameters The ECE role parameters for this interface. #> $OperationType = 'PreAddNode' [EnvironmentValidator]::RunValidators($Parameters, $OperationType) } static EnvironmentValidatorRecovery([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { <# .SYNOPSIS This function is used to run environment validator and run during Recovery when full ECE parameters are available .EXAMPLE [EnvironmentValidator]::EnvironmentValidatorRecovery($Parameters) .PARAMETER Parameters The ECE role parameters for this interface. #> $OperationType = 'Recovery' [EnvironmentValidator]::RunValidators($Parameters, $OperationType) } static EnvironmentValidatorReplayResult([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { <# .SYNOPSIS This function is used to replay environment validator results from any node after telemetry pipeline is up .EXAMPLE [EnvironmentValidator]::EnvironmentValidatorReplayResult() #> try { Trace-Execution "Searching for environment validator telemetry events to replay." $PsSession = [EnvironmentValidator]::NewPsSessionAllHosts($Parameters) $sb = { Write-Host ("{0} Importing Module" -f $ENV:COMPUTERNAME) Import-Module AzStackHci.EnvironmentChecker Write-Host ("{0} Reading Events" -f $ENV:COMPUTERNAME) $Events = Get-AzStackHciEnvironmentCheckerEvents -Source Telemetry Write-Host ("{0} Finished reading events" -f $ENV:COMPUTERNAME) if ($Events) { Write-Host ("{0} Environment Validator Telemetry Events found on {1}. Replaying." -f $Events.Count, $ENV:COMPUTERNAME) $Events | Foreach-Object { $params = @{ Source = 'AzStackHciEnvironmentChecker/Telemetry' LogName = 'AzStackHciEnvironmentChecker' Message = $PSITEM.Message EventId = $PSITEM.EventId EventType = $PSITEM.EntryType } Write-ETWLog @params } Write-Host "Replay complete." } else { Write-Host ("No Environment Validator Telemetry Events found on {0}. No Action." -f $ENV:COMPUTERNAME) } } Trace-Execution ("Calling script block") Invoke-Command -Session $PsSession -ScriptBlock $sb Trace-Execution ("Finished calling script block") } catch { Trace-Execution ("Error occurred trying to replay environment validator telemetry events. Error: {0}" -f $_) } } static ValidatePreAddNode([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { <# .SYNOPSIS This function is used to run environment validator and run before AddNode when full ECE parameters are available, but new node has not be updated in ECE. .EXAMPLE [EnvironmentValidator]::ValidatePreAddNode($Parameters) .PARAMETER Parameters The ECE role parameters for this interface. #> $OperationType = 'AddNode' $ValidatorName = 'AzStackHciAddNode' [EnvironmentValidator]::RunSingleValidator($Parameters, $OperationType, $ValidatorName) } static ValidateBitlocker([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { <# .SYNOPSIS This function is used to run environment validator before deployment when full ECE parameters are available. .EXAMPLE [EnvironmentValidator]::ValidateBitlocker($Parameters) .PARAMETER Parameters The ECE role parameters for this interface. #> $OperationType = 'Deployment' $ValidatorName = 'AzStackHciBitlocker' [EnvironmentValidator]::RunSingleValidator($Parameters, $OperationType, $ValidatorName) } static ValidateConnectivity([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { <# .SYNOPSIS This function is used to run environment validator before deployment when full ECE parameters are available. .EXAMPLE [EnvironmentValidator]::ValidateConnectivity($Parameters) .PARAMETER Parameters The ECE role parameters for this interface. #> $OperationType = 'Deployment' $ValidatorName = 'AzStackHciConnectivity' [EnvironmentValidator]::RunSingleValidator($Parameters, $OperationType, $ValidatorName) } static ValidateScaleOutConnectivity([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { <# .SYNOPSIS This function is used to run environment validator before AddNode when full ECE parameters are available. .EXAMPLE [EnvironmentValidator]::ValidateScaleOutConnectivity($Parameters) .PARAMETER Parameters The ECE role parameters for this interface. #> $OperationType = 'AddNode' $ValidatorName = 'AzStackHciConnectivity' [EnvironmentValidator]::RunSingleValidator($Parameters, $OperationType, $ValidatorName) } static ValidateExternalAD([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { <# .SYNOPSIS This function is used to run environment validator before deployment. .EXAMPLE [EnvironmentValidator]::ValidateExternalAD($Parameters) .PARAMETER Parameters The ECE role parameters for this interface. #> $OperationType = 'Deployment' $ValidatorName = 'AzStackHciExternalActiveDirectory' [EnvironmentValidator]::RunSingleValidator($Parameters, $OperationType, $ValidatorName) } static ValidateHardware([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { <# .SYNOPSIS This function is used to run environment validator before deployment. .EXAMPLE [EnvironmentValidator]::ValidateHardware($Parameters) .PARAMETER Parameters The ECE role parameters for this interface. #> $OperationType = 'Deployment' $ValidatorName = 'AzStackHciHardware' [EnvironmentValidator]::RunSingleValidator($Parameters, $OperationType, $ValidatorName) } static ValidateScaleOutHardware([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { <# .SYNOPSIS This function is used to run environment validator before AddNode. .EXAMPLE [EnvironmentValidator]::ValidateScaleOutHardware($Parameters) .PARAMETER Parameters The ECE role parameters for this interface. #> $OperationType = 'AddNode' $ValidatorName = 'AzStackHciHardware' [EnvironmentValidator]::RunSingleValidator($Parameters, $OperationType, $ValidatorName) } static ValidateNetwork([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { <# .SYNOPSIS This function is used to run environment validator before deployment. .EXAMPLE [EnvironmentValidator]::ValidateNetwork($Parameters) .PARAMETER Parameters The ECE role parameters for this interface. #> $OperationType = 'Deployment' $ValidatorName = 'AzStackHciNetwork' [EnvironmentValidator]::RunSingleValidator($Parameters, $OperationType, $ValidatorName) } static ValidateScaleOutNetwork([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { <# .SYNOPSIS This function is used to run environment validator before AddNode. .EXAMPLE [EnvironmentValidator]::ValidateScaleOutNetwork($Parameters) .PARAMETER Parameters The ECE role parameters for this interface. #> $OperationType = 'AddNode' $ValidatorName = 'AzStackHciNetwork' [EnvironmentValidator]::RunSingleValidator($Parameters, $OperationType, $ValidatorName) } static ValidateObservability([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { <# .SYNOPSIS This function is used to run environment validator before deployment. .EXAMPLE [EnvironmentValidator]::ValidateObservability($Parameters) .PARAMETER Parameters The ECE role parameters for this interface. #> $OperationType = 'Deployment' $ValidatorName = 'AzStackHciObservability' [EnvironmentValidator]::RunSingleValidator($Parameters, $OperationType, $ValidatorName) } static ValidateScaleOutObservability([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { <# .SYNOPSIS This function is used to run environment validator before AddNode. .EXAMPLE [EnvironmentValidator]::ValidateScaleOutObservability($Parameters) .PARAMETER Parameters The ECE role parameters for this interface. #> $OperationType = 'AddNode' $ValidatorName = 'AzStackHciObservability' [EnvironmentValidator]::RunSingleValidator($Parameters, $OperationType, $ValidatorName) } static ValidateMOCStack([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { <# .SYNOPSIS This function is used to run environment validator before deployment. .EXAMPLE [EnvironmentValidator]::ValidateMOCStack($Parameters) .PARAMETER Parameters The ECE role parameters for this interface. #> $OperationType = 'Deployment' $ValidatorName = 'AzStackHciMOCStack' [EnvironmentValidator]::RunSingleValidator($Parameters, $OperationType, $ValidatorName) } static ValidateScaleOutMOCStack([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { <# .SYNOPSIS This function is used to run environment validator before AddNode. .EXAMPLE [EnvironmentValidator]::ValidateScaleOutMOCStack($Parameters) .PARAMETER Parameters The ECE role parameters for this interface. #> $OperationType = 'AddNode' $ValidatorName = 'AzStackHciMOCStack' [EnvironmentValidator]::RunSingleValidator($Parameters, $OperationType, $ValidatorName) } static ValidateArcIntegration([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { <# .SYNOPSIS This function is used to run environment validator before deployment. .EXAMPLE [EnvironmentValidator]::ValidateArcIntegration($Parameters) .PARAMETER Parameters The ECE role parameters for this interface. #> $OperationType = 'Deployment' $ValidatorName = 'AzStackHciArcIntegration' [EnvironmentValidator]::RunSingleValidator($Parameters, $OperationType, $ValidatorName) } static [CloudEngine.Actions.ConditionalActionDescription] EvaluateValidateArcIntegrationAction([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { $ErrorActionPreference = "Stop" return [EnvironmentValidator]::EvaluateValidatorAction($Parameters, "ValidateArcIntegration") } static [CloudEngine.Actions.ConditionalActionDescription] EvaluateValidatorAction([CloudEngine.Configurations.EceInterfaceParameters] $Parameters, [string] $actionName) { $ErrorActionPreference = "Stop" function Get-ArcAEndpoint { [CmdletBinding()] Param ( [Parameter(Mandatory = $true)] [CloudEngine.Configurations.EceInterfaceParameters] $Parameters ) $cloudRole = $Parameters.Roles["Cloud"].PublicConfiguration $registrationArcAEndpoint = $cloudRole.PublicInfo.RegistrationArcAEndpoint return $registrationArcAEndpoint } function Test-IsArcADeployment { [CmdletBinding()] Param ( [Parameter(Mandatory = $true)] [CloudEngine.Configurations.EceInterfaceParameters] $Parameters ) $registrationArcAEndpoint = Get-ArcAEndpoint -Parameters $Parameters if ($null -eq $registrationArcAEndpoint -or $registrationArcAEndpoint.Length -eq 0) { return $false } return $true } # For ArcA-Managed ASZ deployment, tenantId is retrieved from ArcA, skip validation... if (-not (Test-IsArcADeployment -Parameters $Parameters)) { Trace-Execution "EvaluateValidatorActionForArcA: Execute $actionName" return [CloudEngine.Actions.ConditionalActionDescription]::CreateWithDefinedAction("Cloud\Infrastructure\EnvironmentValidator", $actionName) } Trace-Execution "EvaluateValidatorActionForArcA: Skip executing $actionName for ArcA deployment" -Verbose return [CloudEngine.Actions.ConditionalActionDescription]::CreateNegative() } static ValidateSBEHealth([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { <# .SYNOPSIS This function is used to run environment validator before deployment. .EXAMPLE [EnvironmentValidator]::ValidateSBEHealth($Parameters) .PARAMETER Parameters The ECE role parameters for this interface. #> $OperationType = 'Deployment' $ValidatorName = 'AzStackHciSBEHealth' [EnvironmentValidator]::RunSingleValidator($Parameters, $OperationType, $ValidatorName) } static ValidateSoftware([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { <# .SYNOPSIS This function is used to run environment validator before deployment. .EXAMPLE [EnvironmentValidator]::ValidateSoftware($Parameters) .PARAMETER Parameters The ECE role parameters for this interface. #> $OperationType = 'Deployment' $ValidatorName = 'AzStackHciSoftware' [EnvironmentValidator]::RunSingleValidator($Parameters, $OperationType, $ValidatorName) } static ValidateScaleOutSoftware([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { <# .SYNOPSIS This function is used to run environment validator before AddNode. .EXAMPLE [EnvironmentValidator]::ValidateScaleOutSoftware($Parameters) .PARAMETER Parameters The ECE role parameters for this interface. #> $OperationType = 'AddNode' $ValidatorName = 'AzStackHciSoftware' [EnvironmentValidator]::RunSingleValidator($Parameters, $OperationType, $ValidatorName) } static ValidateClusterWitness([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { <# .SYNOPSIS This function is used to run environment validator before deployment. .EXAMPLE [EnvironmentValidator]::ValidateClusterWitness($Parameters) .PARAMETER Parameters The ECE role parameters for this interface. #> $OperationType = 'Deployment' $ValidatorName = 'AzStackHciClusterWitness' [EnvironmentValidator]::RunSingleValidator($Parameters, $OperationType, $ValidatorName) } static hidden RunSingleValidator([CloudEngine.Configurations.EceInterfaceParameters] $Parameters, [string] $OperationType, [string] $ValidatorName) { $ErrorActionPreference = "Stop" [EnvironmentValidator]::ImportEnvironmentValidator($Parameters) if (Get-Module -Name AzStackHci.EnvironmentChecker) { $EnvCheckerModuleBase = Get-Module -Name AzStackHci.EnvironmentChecker | Sort-Object Version -Descending | Select-Object -First 1 | Select-Object -ExpandProperty ModuleBase } else { $EnvCheckerModuleBase = Get-Module AzStackHci.EnvironmentChecker -ListAvailable | Where-Object {$_.Path -notlike "*\$($_.Version)*"} | Sort-Object Version -Descending | Select-Object -First 1 | Select-Object -ExpandProperty ModuleBase } Trace-Execution "EnvCheckerModuleBase: $EnvCheckerModuleBase" Import-Module (Join-Path "$EnvCheckerModuleBase\$ValidatorName" "$ValidatorName.psm1") -Force $cmdLetname = "Test-$ValidatorName" $splat = @{ Parameters = $Parameters OperationType = $OperationType FailFast = $true } $ENV:EnvChkrOp = $OperationType Invoke-Expression "$cmdletName @splat" } static hidden [Array] BuildJobExecution([CloudEngine.Configurations.EceInterfaceParameters] $Parameters, [string] $OperationType) { $ErrorActionPreference = "Stop" [EnvironmentValidator]::ImportEnvironmentValidator($Parameters) if (Get-Module -Name AzStackHci.EnvironmentChecker) { $EnvCheckerModuleBase = Get-Module -Name AzStackHci.EnvironmentChecker | Sort-Object Version -Descending | Select-Object -First 1 | Select-Object -ExpandProperty ModuleBase } else { $EnvCheckerModuleBase = Get-Module AzStackHci.EnvironmentChecker -ListAvailable | Where-Object {$_.Path -notlike "*\$($_.Version)*"} | Sort-Object Version -Descending | Select-Object -First 1 | Select-Object -ExpandProperty ModuleBase } Trace-Execution "EnvCheckerModuleBase: $EnvCheckerModuleBase" $validators = Get-ChildItem -Path $EnvCheckerModuleBase -Filter AzStackHci* -Directory Trace-Execution "Validators found: $($validators.Name -join ',')" # Gather validator list to run $executionJobs = @() class ExecutionJob { [string]$ModuleName [hashTable]$Params [string]$Command [string]$Name [string]$Description [string]$Status [datetime]$StartTime [datetime]$EndTime [int]$DurationSeconds [psobject[]]$FailedResult [psobject[]]$AllResults [string]$ExecutionDetail } foreach ($validator in $validators) { $validatorModulePath = Get-ChildItem -Path $validator.FullName -Filter "$($Validator.Name)*.psm1" if ($validatorModulePath) { Trace-Execution "Importing Validator $($validatorModulePath.Directory.Name)" Import-Module -Name $validatorModulePath.FullName -Force $module = Get-Module $validatorModulePath.Directory.Name $metaData = $module.ExportedVariables.MetaData.value if ($metaData) { Trace-Execution "Checking if $OperationType is applicable to $($Module.Name)" if ($metaData.OperationType -contains $OperationType) { $validatorCommand = Get-Command -Module $module.Name if ($validatorCommand) { Trace-Execution "Found command $validatorCommand in module $($module.Name) for operation type $OperationType" $executionJobs += New-Object ExecutionJob -Property @{ ModuleName = $module.Name Params = @{ Parameters = $Parameters OperationType = $OperationType FailFast = $false } Command = $validatorCommand.Name Status = 'Queued' Name = if ([string]::IsNullOrEmpty($metaData.UIName)) { 'Azure Stack HCI {0}' -f ($validatorCommand.Name -replace 'Test-AzStackHci','') } else { $metaData.UIName } Description = if ([string]::IsNullOrEmpty($metaData.UIDescription)) { "Test {0} requirements" -f ($validatorCommand.Name -replace 'Test-AzStackHci','') } else { $metaData.UIDescription } } } else { Trace-Execution "No command found for $($validator.Name)" } } else { Trace-Execution "$($Module.Name) not applicable for $OperationType. Continuing." } } else { Trace-Execution "No metadata found for $($module.Name)" } } else { Trace-Execution "No PS module found for $($validator.Name)" } } return $executionJobs } static hidden RunValidators([CloudEngine.Configurations.EceInterfaceParameters] $Parameters, [string] $OperationType) { # Archive files 'AzStackHciEnvironmentChecker.log', 'AzStackHciEnvironmentProgress.json', 'AzStackHciEnvironmentReport.json', 'AzStackHciEnvironmentReport.xml' | ForEach-Object { Get-ChildItem -Path "$env:LocalRootFolderPath\MASLogs" -Filter $PSITEM -ErrorAction SilentlyContinue | ForEach-Object { Rename-Item -Path $PSITEM.FullName -NewName ($PSITEM.fullname -replace '(\.)', ('_{0}.' -f (Get-Date -Format yyyyMMdd-HHmmss))) } } # Determine which jobs need to run. $executionJobs = [EnvironmentValidator]::BuildJobExecution($Parameters,$OperationType) # Run validator list foreach ($work in $executionJobs) { Trace-Execution ("Running & {0} {1}" -f $work.Command,(($work.params.GetEnumerator() | ForEach-Object { "$($_.Key)=$($_.Value)" }) -join ';')) $work.StartTime = [System.DateTime]::UtcNow $work.Status = 'In Progress' [EnvironmentValidator]::WriteProgressFile($executionJobs) $splat = $work.Params $command = $work.Command # Execute validator try { $ENV:EnvChkrOp = $splat.OperationType $result = Invoke-Expression "$command @splat" $work.Status = $result.Status $work.FailedResult = $result.FailedResult | Sort-Object Severity $work.ExecutionDetail = $result.ExecutionDetail $work.AllResults = $result.AllResults } catch { $work.Status = 'Error' $work.ExecutionDetail = "Exception occurred ($($work.Command)): $($_.exception.message)" } finally { $work.EndTime = [System.DateTime]::UtcNow $work.DurationSeconds = [System.Math]::Round(([system.datetime]$work.EndTime - [system.datetime]$work.StartTime).TotalSeconds,0) [EnvironmentValidator]::WriteProgressFile($executionJobs) } } Trace-Execution (Get-AzStackHciEnvironmentCheckerProgress -Path $env:LocalRootFolderPath\MASLogs\AzStackHciEnvironmentProgress.json | Out-String) # First throw any exceptions $exceptions = $executionJobs | Where-Object { $_.ExecutionDetail -match 'Exception occurred' } if ($exceptions) { throw ("Failing action plan. {0}:`n{1}`nLog file: {2}`nProgress file: {3}" -f ` ($exceptions.ModuleName | Foreach-Object { "{0} validator threw and exception`r`n" -f $_ -replace 'AzStackHci',''}), ($exceptions.ExecutionDetail -join "`r`n"),"$env:LocalRootFolderPath\MASLogs\AzStackHciEnvironmentChecker.log","$env:LocalRootFolderPath\MASLogs\AzStackHciEnvironmentProgress.json") } # Second throw if there are failed tests $terminateResult = @('Error','FAILURE','CRITICAL') $terminatingValidators = $executionJobs | Where-Object {$_.Status -in $terminateResult } # if this is update, there's a runtimeparamter to put the file try { $runtimeParameters = $Parameters.RunInformation["RuntimeParameter"] $resultPath = $null if ($runtimeParameters -ne $null -and -not ([string]::IsNullOrEmpty($runtimeParameters["EnvironmentCheckerResultPath"]))) { $resultPath = $runtimeParameters["EnvironmentCheckerResultPath"] if (-not (Test-Path $resultPath -IsValid)) { throw "Invalid result path: $resultPath" } $executionJobs.Allresults | ConvertTo-Json -Depth 5 | Out-File -FilePath $resultPath } else { Trace-Execution "HealthCheckResultPath runtime parameter was not specified, so result will not be written to file." } } catch { Trace-Warning "Error occurred trying to write the environment checker result file. Error: $_" } # Throw on any terminating validators if ($terminatingValidators) { throw ("Failing action plan. {0}:`n{1}`nLog file: {2}`nReport file: {3}" -f ` ($terminatingValidators.ModuleName | Foreach-Object { "{0} requirements not met`r`n" -f $_ -replace 'AzStackHci',''}), ([EnvironmentValidator]::FormatResultToString($terminatingValidators.FailedResult)), ` "$env:LocalRootFolderPath\MASLogs\AzStackHciEnvironmentChecker.log", ` "$env:LocalRootFolderPath\MASLogs\AzStackHciEnvironmentReport.json") } } static hidden WriteProgressFile([array] $ExecutionJobs) { # Write a progress file $progressPath = Join-Path -Path "$($env:LocalRootFolderPath)\MASLogs" -ChildPath AzStackHciEnvironmentProgress.json if (-not (Test-Path -Path (Split-Path $progressPath -Parent))) { New-Item -Path (Split-Path $progressPath -Parent) -ItemType Directory -Force | Out-Null } $ExecutionJobs | Select-Object Name, Description, Status, Command, ModuleName, StartTime, EndTime, DurationSeconds, FailedResult, ExecutionDetail | ConvertTo-Json -Depth 5 | Out-File -FilePath $progressPath } static hidden ImportEnvironmentValidator([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { [EnvironmentValidator]::CleanEnvironmentValidator() $fromUpdateIsTrue = [EnvironmentValidator]::ImportEnvironmentValidatorFromUpdatePackage($Parameters) if (!$fromUpdateIsTrue -and -not (Get-Module AzStackHci.EnvironmentChecker -ListAvailable | Where-Object {$_.Path -notlike "*\$($_.Version)*"})) { $nugetname = [EnvironmentValidator]::InstallEnvironmentValidator() Trace-Execution "Importing $nugetname" $EnvCheckerNugetPath = Get-ASArtifactPath -NugetName $nugetname $EnvCheckerModulePath = Join-Path $EnvCheckerNugetPath "$nugetname.psd1" Import-Module $EnvCheckerModulePath -ErrorAction Stop -Verbose:$false -Force -Global | Out-Null if (-not (Get-Module $nugetname)) { throw "Unable to import AzStackHci.EnvironmentChecker" } } } static hidden [bool] ImportEnvironmentValidatorFromUpdatePackage([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { try { # Gets the file share directory containing expanded contents of a currently in-progress update package. function Get-InProgressUpdatePackagePath { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [CloudEngine.Configurations.EceInterfaceParameters] $Parameters ) $runtimeParameters = $Parameters.RunInformation['RuntimeParameter'] $packagePath = $runtimeParameters["UpdatePackagePath"] if (-not $packagePath) { Trace-Execution "The UpdatePackagePath runtime parameter is not present." return $null } Trace-Execution "Upgrade package @: [$packagePath]" # The package path is expected to be a full path to an update package file (e.g. a self- # extracting .exe, or a metadata.xml file). The path where the contents are extracted is # the directory containing this file. return (Split-Path $packagePath) } # Gets the location of the folder containing nugets in the currently in-progress update package. function Get-InProgressUpdateNugetStore { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [CloudEngine.Configurations.EceInterfaceParameters] $Parameters ) $updatePackagePath = Get-InProgressUpdatePackagePath -Parameters $Parameters if ($updatePackagePath) { $urpRole = $Parameters.Roles["URP"].PublicConfiguration $nugetSourceFolderName = ($urpRole.PublicInfo.UpdatePackagePaths.ContentMappings.ContentMapping | Where-Object { $_.Name -eq "Nugets" }).SourceFolder $nugetStorePath = Join-Path $updatePackagePath $nugetSourceFolderName if (Test-Path $nugetStorePath) { Trace-Execution "Found update nuget store at $nugetStorePath." return $nugetStorePath } # Fallback: check for legacy nuget store path. $nugetSourceFolderName = ($urpRole.PublicInfo.UpdatePackagePaths.ContentMappings.ContentMapping | Where-Object { $_.Name -eq "NugetsLegacy" }).SourceFolder $nugetStorePath = Join-Path $updatePackagePath $nugetSourceFolderName if (Test-Path $nugetStorePath) { Trace-Execution "Found legacy update nuget store at $nugetStorePath." return $nugetStorePath } Trace-Execution "No nuget store was found at update package path: $updatePackagePath." } else { Trace-Execution "No update is currently in progress." } return $null } $nugetAndModuleName = "AzStackHci.EnvironmentChecker" Trace-Execution "Trying Get-InprogressUpdateNugetStore" $updatePackageNugetStore = Get-InProgressUpdateNugetStore -Parameters $Parameters Trace-Execution "InprogressUpdateNugetStorePath: $updatePackageNugetStore" if (-not $updatePackageNugetStore) { throw "Update package nuget store was not specified in runtime parameters" } Trace-Execution "Checking for new $nugetAndModuleName nuget in the update package at $updatePackageNugetStore" $nuget = Find-Package -Source $updatePackageNugetStore -Name $nugetAndModuleName -ErrorAction Ignore if (-not $nuget) { throw "The $nugetAndModuleName nuget was not found." } $tempFolder = Join-Path (Get-Item $env:TEMP | % FullName) ([IO.Path]::GetRandomFileName()) if (-not (Test-Path $tempFolder -PathType Container)) { $null = New-Item -Path $tempFolder -Type Directory } Trace-Execution "Installing $($nuget.Name) to $tempFolder." $nuget | Install-Package -Destination $tempFolder | Out-Null $installedPath = Get-Package -Destination $tempFolder -Name $nuget.Name | % Source | Split-Path $validatorModulePath = Join-Path $installedPath "$nugetAndModuleName.psd1" Trace-Execution "$nugetAndModuleName module installed to $validatorModulePath." if (-not (Test-Path $validatorModulePath)) { throw "Unable to find the validator module from the installed location: $validatorModulePath." } Trace-Execution "Importing $validatorModulePath." Import-Module $validatorModulePath -ErrorAction Stop -Verbose:$false -Force -Global | Out-Null return $true } catch { Trace-Execution "Get-InprogressUpdateNugetStore failed. $_." return $false } } static hidden [string] InstallEnvironmentValidator() { [EnvironmentValidator]::CleanEnvironmentValidator() $nugetname = "AzStackHci.EnvironmentChecker" $nugetSourceFolderPath = "$env:LocalRootFolderPath\CloudDeployment\NuGetStore" $nugetDestination = "$env:LocalRootFolderPath:\Nugetstore" if (-not (Test-Path "$nugetDestination\$nugetname.1*")) { $nugetExePath = "$env:LocalRootFolderPath\tools\nuget.exe" Trace-Execution "Unpacking $nugetname" Invoke-Expression "$nugetExePath install $nugetName -Source $nugetSourceFolderPath -OutputDirectory $nugetDestination -PackageSaveMode 'nuspec' -Prerelease" } return $nugetname } static hidden CleanEnvironmentValidator() { $nugetname = "AzStackHci.EnvironmentChecker" # Unload any previously loaded modules $loadedModule = Get-Module -Name $nugetname | Where-Object {$_.Path -like "*$($_.Version)*"} if ($loadedModule) { Trace-Execution "Loaded module(s) found for $($loadedModule.Name):" Trace-Execution ($loadedModule | Format-List Path, Name, Version | Out-String) Trace-Execution "Unloading" $loadedModule | Remove-Module -Force } # Uninstall any modules installed from external $installedModule = Get-Module -Name $nugetname -ListAvailable | Where-Object {$_.Path -like "*$($_.Version)*"} if ($installedModule) { Trace-Execution "Externally installed module(s) found for $($installedModule.Name):" Trace-Execution ($installedModule | Format-List Path, Name, Version | Out-String) Trace-Execution "Uninstalling" $installedModule | Uninstall-Module -Force } } static hidden [PsObject] ParseResult([array] $Result, [string]$Title, [switch]$FailFast) { <# If no result, set Result to skipped and detail to 'no result' If skipped results exist, set Result to skipped and include If warning results exist, set Result to warning and include If critical results exist, set Result to failed #> $statusResult = '' $detail = @() $executionDetail = '' if ($null -eq $Result) { Trace-Warning ("Warning: checking {0} requirements. Review {1}. Returning." -f $Title, "$($env:LocalRootFolderPath)\MasLogs\AzStackHciEnvironmentChecker.log") $executionDetail = 'Validator generated no results.' $statusResult = 'INFORMATIONAL' } # Check for skips and write to log $skipped = @() $skipped = $Result | Where-Object { $_.Status -eq 'Skipped' } if ($skipped) { Trace-Warning ("{0} validation has skipped results: {1}" -f $Title, ($skipped | Format-List | Out-String)) # if there are no more results then the overall Result is skipped if (-not ($Result | Where-Object { $_.Status -ne 'Skipped' })) { $statusResult = 'INFORMATIONAL' $detail += $skipped $executionDetail = 'All tests were skipped' } } # Check for warnings $warningFailures = @() $warningFailures = $Result | Where-Object { $_.Status -eq 'FAILURE' -and $_.Severity -eq 'WARNING' } if ($warningFailures) { $warningResultString = [EnvironmentValidator]::FormatResultToString($warningFailures) Trace-Warning ("Warning (non-blocking) {0} failures found in validation. {1} Rules failed: {2}" -f $Title,$warningFailures.count,($warningResultString -join "")) $statusResult = 'WARNING' $detail += $warningFailures } # Check for critical failures $criticalFailures = @() $criticalFailures = $Result | Where-Object { $_.Status -eq 'FAILURE' -and $_.Severity -eq 'CRITICAL' } if ($criticalFailures) { Trace-Warning ("Critical (blocking) {0} failures found in validation. {1} Rules failed: " -f $Title, $criticalFailures.Count) $criticalResultString = [EnvironmentValidator]::FormatResultToString($criticalFailures) $terminalFailureMsg = ("{0} requirements not met. Review output and remediate: {1}" -f $Title, ($criticalResultString -join "")) if ($FailFast) { throw $terminalFailureMsg } Trace-Warning $terminalFailureMsg $statusResult = 'CRITICAL' $detail += $criticalFailures } if (-not [string]::IsNullOrEmpty($Result) -and -not $criticalFailures -and -not $warningFailures) { Trace-Execution -Verbose "$Title prerequisites met." $statusResult = 'SUCCESS' } return (New-Object -TypeName PSObject -Property @{ Status = $statusResult FailedResult = if ($detail.count -gt 0) { $detail } else { $null } ExecutionDetail = $executionDetail AllResults = $Result }) } static hidden [string] FormatResultToString([PSObject] $FailedResult) { $failureDetail = @() $failureDetail = $FailedResult | Foreach-Object { Write-Output "`nRule:" $PSITEM | Select-Object -Property * -ExcludeProperty AdditionalData | Format-List | Out-String Write-Output "AdditionalData:" $PSITEM.AdditionalData | Where-Object Status -eq 'FAILURE' | Format-List | Out-String } return $failureDetail } static hidden [PSCredential] GetLocalAdminCredential([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { $cloudRole = $Parameters.Roles["Cloud"].PublicConfiguration # Account info $securityInfo = $cloudRole.PublicInfo.SecurityInfo $localAdmin = $securityInfo.LocalUsers.User | ? Role -EQ $Parameters.Configuration.Role.PrivateInfo.Accounts.BuiltInAdminAccountID $localAdminCredential = $Parameters.GetCredential($localAdmin.Credential) Trace-Execution ("Found credential {0}" -f $localAdminCredential.Username ) return $localAdminCredential } static hidden [PSCredential] GetDomainAdminCredential([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { $cloudRole = $Parameters.Roles["Cloud"].PublicConfiguration # Account info $securityInfo = $cloudRole.PublicInfo.SecurityInfo $domainFQDN = $Parameters.Roles["Domain"].PublicConfiguration.PublicInfo.DomainConfiguration.FQDN $domainName = $Parameters.Roles["Domain"].PublicConfiguration.PublicInfo.DomainConfiguration.DomainName $domainAdminUser = $securityInfo.DomainUsers.User | ? Role -EQ $Parameters.Configuration.Role.PrivateInfo.Accounts.DomainAdminAccountID $Credential = $Parameters.GetCredential($domainAdminUser.Credential) $domainAdminCredential = New-Object pscredential("$domainFQDN\$($Credential.UserName)", $Credential.Password) return $domainAdminCredential } static hidden [string[]] GetAnswerFile() { # Return the deployment data $deploymentData = Get-Content "$($env:LocalRootFolderPath)\CloudDeployment\Unattended.json" | ConvertFrom-Json return $deploymentData } static hidden [string[]] GetAllHostNicIps([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { # Find the IP for the contect node for PSSession [System.Array]$NodesIps = Get-NetworkMgmtIPv4FromECEForAllHosts -Parameters $Parameters Trace-Execution ("Found host IPs {0}" -f ($NodesIps -join ',')) return $NodesIps.Values } static hidden [string[]] GetHostNicIpByName([CloudEngine.Configurations.EceInterfaceParameters] $Parameters, [string]$NodeName) { # Find the IP for the contect node for PSSession [System.Array]$NodesIps = Get-NetworkMgmtIPv4FromECEForHost -Parameters $Parameters -HostName $NodeName Trace-Execution ("Found host IP {0} for host {1}" -f ($NodesIps -join ','),$NodeName) return $NodesIps } static hidden [System.Management.Automation.Runspaces.PSSession[]] NewPsSessionAllHosts([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { $Nodes = [EnvironmentValidator]::GetAllHostNicIps($Parameters) if ([string]::IsNullOrEmpty(($nodes | Select-Object -first 1))) { # As a back up try the local cluster e.g. during upgrade Trace-Execution "Unable to find nodes in ECE parameters, querying local cluster." $Nodes = Get-ClusterNode | Select-Object -ExpandProperty Name } [EnvironmentValidator]::SetTrustedHosts($Nodes) Trace-Execution ("Making PsSession to {0}" -f ($Nodes -join ',')) [System.Array]$PsSession = $Nodes | ForEach-Object { [EnvironmentValidator]::NewPsSessionWithRetries($PSITEM,$Parameters) } return $PsSession } static hidden [System.Management.Automation.Runspaces.PSSession[]] NewPsSessionByHost([CloudEngine.Configurations.EceInterfaceParameters] $Parameters, [string[]]$Node, [switch]$IncludeLocalHost) { $Ips = @() $Node | ForEach-Object { $Ips += [EnvironmentValidator]::GetHostNicIpByName($Parameters,$PSITEM) } if ($IncludeLocalHost) { $Ips += 'localhost' } [EnvironmentValidator]::SetTrustedHosts($Ips) Trace-Execution ("Making PsSession to {0}" -f ($Ips -join ',')) [System.Array]$PsSession = $Ips | ForEach-Object { [EnvironmentValidator]::NewPsSessionWithRetries($PSITEM,$Parameters) } return $PsSession } static hidden [System.Management.Automation.Runspaces.PSSession] NewPsSessionWithRetries([string]$Node, [CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { function New-PsSessionWithRetries { param ( [string]$node, [pscredential]$Credential, [int]$retries = 3, [int]$waitSeconds = 30 ) For ($i=1; $i -le $retries; $i++) { try { Trace-Execution "Creating PsSession ($i/$retries) to $Node as $($Credential.UserName)..." $PsSession = Microsoft.PowerShell.Core\New-PSSession -ComputerName $Node -Credential $Credential -ErrorAction Stop $computerName = Microsoft.PowerShell.Core\Invoke-Command -Session $PsSession -ScriptBlock { $ENV:COMPUTERNAME } -ErrorAction Stop $IsAdmin = Microsoft.PowerShell.Core\Invoke-Command -Session $PsSession -ScriptBlock { ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] 'Administrator') } -ErrorAction Stop if (-not $IsAdmin) { throw ("PsSession was successful but user: {0} is not an administrator on computer {1} " -f $PsSession.Runspace.ConnectionInfo.Credential.Username, $computerName) } break } catch { Trace-Execution "Creating PsSession ($i/$retries) to $Node failed: $($_.exception.message)" $errMsg = $_.tostring() Start-Sleep -Seconds $waitSeconds } } if ($PsSession -and $ComputerName -and $IsAdmin) { Trace-Execution ("PsSession to {0} created after {1} retries. (Remote machine name: {2})" -f $Node, ("$i/$retries"), $computerName) return $PsSession } else { throw "Unable to create a valid session to $Node`: $errMsg" } } # Check if we can connect with domain credentials # Otherwise connect with local credentials $domainCredential = [EnvironmentValidator]::GetDomainAdminCredential($Parameters) if (Test-WSMan -ComputerName $Node -Credential $domainCredential -Authentication Default -ErrorAction SilentlyContinue) { Trace-Execution "Attempting PsSession $Node with domain credentials" $PsSession = New-PsSessionWithRetries -Node $Node -Credential $domainCredential } else { Trace-Execution "Attempting PsSession $Node with local credentials" $localCredential = [EnvironmentValidator]::GetLocalAdminCredential($Parameters) if ($null -eq $localCredential) { throw "Unable to create a valid session to $Node. No local admin credential found." } $PsSession = New-PsSessionWithRetries -Node $Node -Credential $localCredential } if ($PsSession) { return $PsSession } else { throw "Unable to create a valid session to $Node" } } static hidden [string[]] GetNodeContext([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { $nodeName = @() $executionRoleName = $Parameters.Context.ExecutionContext.Roles.Role.RoleName $nodeName = $Parameters.Context.ExecutionContext.Roles.Role.Nodes.Node.Name if ($null -eq $nodeName) { # Try runtimeParameters $runtimeParameters = $Parameters.RunInformation['RuntimeParameter'] if ($runtimeParameters.ContainsKey('NodeName')) { $nodeName = $runtimeParameters['NodeName'] Trace-Execution "Retrieved node(s): $($nodeName -join ', ') from runtimeParameters." } else { # If node names are not provided, get all nodes associated to the $executionRoleName Trace-Execution "Retrieving ExecutionContext with role name $executionRoleName and node(s): $($nodeName -join ', ')" $nodeName = ($Parameters.Roles[$executionRoleName].PublicConfiguration.Nodes.Node.Name) | Select-Object -Unique } } else { Trace-Execution "Retrieved node(s): $($nodeName -join ', ') from ExecutionContext." } return $nodeName } static hidden SetTrustedHosts([string[]]$Nodes) { $trustedHosts = (Get-Item -Path WSMan:\localhost\Client\TrustedHosts).Value foreach ($node in $nodes) { if ('*' -notin $TrustedHosts -and ($node -notin $TrustedHosts.Split(','))) { Trace-Execution "Adding $node to TrustedHosts" Set-Item WSMan:\localhost\Client\TrustedHosts -Value $node -Concatenate -Force } else { Trace-Execution "TrustedHosts already matches $node. Continuing." } } } } # SIG # Begin signature block # MIInwgYJKoZIhvcNAQcCoIInszCCJ68CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCw0fx0mos7XWxw # OaXIFdRkSPI7551sad9nR5ZQ6zmWdKCCDXYwggX0MIID3KADAgECAhMzAAADrzBA # 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 # /Xmfwb1tbWrJUnMTDXpQzTGCGaIwghmeAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw # EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN # aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp # Z25pbmcgUENBIDIwMTECEzMAAAOvMEAOTKNNBUEAAAAAA68wDQYJYIZIAWUDBAIB # BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO # MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIKyIzQEp3nSEaa3kdEXFxppD # 9DNR32CwcBl1Qbq8D7BTMEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A # cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB # BQAEggEAZahThdliRaIDtQcCqJGOrJ9iCYwebDw24oniAHHDhRSSVZZcXnmcDSHw # UQuVdF1CIo3Bzd8X90sYOKJmJPFi54mBp9A1TKLvY3ew0sCf+jYs5aE+Dspg7AF1 # 2ew1zpbcG0TCioFb5mXcaNpFwfSU7xtS9VZoXr41Aa71bWKhgToLS+tlbUuW/Mq0 # Ef9P4Zbq8klwmgUYbQWcJkRWFtH7feI/xI+pu3t2lAX/BfUAmtkx7PsYZEnqfADu # ey1QJdddcGcvxCmBQygjEXttAgHde97PnkaoL70e1BW+q8Hjw91A/6yY+ieFAg0d # JaWKJC4jVNShHyqYva5UZbQrorFdxKGCFywwghcoBgorBgEEAYI3AwMBMYIXGDCC # FxQGCSqGSIb3DQEHAqCCFwUwghcBAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFZBgsq # hkiG9w0BCRABBKCCAUgEggFEMIIBQAIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl # AwQCAQUABCAue56J3zqPC1hF4u3/1KCfXK5OHUVhz8+39qHyuYA67gIGZpfHenkf # GBMyMDI0MDcyMzExMDA1Ny42MTRaMASAAgH0oIHYpIHVMIHSMQswCQYDVQQGEwJV # UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE # ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJl # bGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNO # OjNCRDQtNEI4MC02OUMzMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBT # ZXJ2aWNloIIRezCCBycwggUPoAMCAQICEzMAAAHlj2rA8z20C6MAAQAAAeUwDQYJ # KoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x # EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv # bjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwHhcNMjMx # MDEyMTkwNzM1WhcNMjUwMTEwMTkwNzM1WjCB0jELMAkGA1UEBhMCVVMxEzARBgNV # BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv # c29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMkTWljcm9zb2Z0IElyZWxhbmQgT3Bl # cmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjozQkQ0LTRC # ODAtNjlDMzElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZTCC # AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKl74Drau2O6LLrJO3HyTvO9 # aXai//eNyP5MLWZrmUGNOJMPwMI08V9zBfRPNcucreIYSyJHjkMIUGmuh0rPV5/2 # +UCLGrN1P77n9fq/mdzXMN1FzqaPHdKElKneJQ8R6cP4dru2Gymmt1rrGcNe800C # cD6d/Ndoommkd196VqOtjZFA1XWu+GsFBeWHiez/PllqcM/eWntkQMs0lK0zmCfH # +Bu7i1h+FDRR8F7WzUr/7M3jhVdPpAfq2zYCA8ZVLNgEizY+vFmgx+zDuuU/GChD # K7klDcCw+/gVoEuSOl5clQsydWQjJJX7Z2yV+1KC6G1JVqpP3dpKPAP/4udNqpR5 # HIeb8Ta1JfjRUzSv3qSje5y9RYT/AjWNYQ7gsezuDWM/8cZ11kco1JvUyOQ8x/JD # kMFqSRwj1v+mc6LKKlj//dWCG/Hw9ppdlWJX6psDesQuQR7FV7eCqV/lfajoLpPN # x/9zF1dv8yXBdzmWJPeCie2XaQnrAKDqlG3zXux9tNQmz2L96TdxnIO2OGmYxBAA # ZAWoKbmtYI+Ciz4CYyO0Fm5Z3T40a5d7KJuftF6CToccc/Up/jpFfQitLfjd71cS # +cLCeoQ+q0n0IALvV+acbENouSOrjv/QtY4FIjHlI5zdJzJnGskVJ5ozhji0YRsc # v1WwJFAuyyCMQvLdmPddAgMBAAGjggFJMIIBRTAdBgNVHQ4EFgQU3/+fh7tNczEi # fEXlCQgFOXgMh6owHwYDVR0jBBgwFoAUn6cVXQBeYl2D9OXSZacbUzUZ6XIwXwYD # VR0fBFgwVjBUoFKgUIZOaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9j # cmwvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3JsMGwG # CCsGAQUFBwEBBGAwXjBcBggrBgEFBQcwAoZQaHR0cDovL3d3dy5taWNyb3NvZnQu # Y29tL3BraW9wcy9jZXJ0cy9NaWNyb3NvZnQlMjBUaW1lLVN0YW1wJTIwUENBJTIw # MjAxMCgxKS5jcnQwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcD # CDAOBgNVHQ8BAf8EBAMCB4AwDQYJKoZIhvcNAQELBQADggIBADP6whOFjD1ad8Gk # EJ9oLBuvfjndMyGQ9R4HgBKSlPt3pa0XVLcimrJlDnKGgFBiWwI6XOgw82hdolDi # MDBLLWRMTJHWVeUY1gU4XB8OOIxBc9/Q83zb1c0RWEupgC48I+b+2x2VNgGJUsQI # yPR2PiXQhT5PyerMgag9OSodQjFwpNdGirna2rpV23EUwFeO5+3oSX4JeCNZvgyU # OzKpyMvqVaubo+Glf/psfW5tIcMjZVt0elswfq0qJNQgoYipbaTvv7xmixUJGTbi # xYifTwAivPcKNdeisZmtts7OHbAM795ZvKLSEqXiRUjDYZyeHyAysMEALbIhdXgH # Eh60KoZyzlBXz3VxEirE7nhucNwM2tViOlwI7EkeU5hudctnXCG55JuMw/wb7c71 # RKimZA/KXlWpmBvkJkB0BZES8OCGDd+zY/T9BnTp8si36Tql84VfpYe9iHmy7Pqq # xqMF2Cn4q2a0mEMnpBruDGE/gR9c8SVJ2ntkARy5SfluuJ/MB61yRvT1mUx3lypp # O22ePjBjnwoEvVxbDjT1jhdMNdevOuDeJGzRLK9HNmTDC+TdZQlj+VMgIm8ZeEIR # NF0oaviF+QZcUZLWzWbYq6yDok8EZKFiRR5otBoGLvaYFpxBZUE8mnLKuDlYobjr # xh7lnwrxV/fMy0F9fSo2JxFmtLgtMIIHcTCCBVmgAwIBAgITMwAAABXF52ueAptJ # mQAAAAAAFTANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgT # Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29m # dCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNh # dGUgQXV0aG9yaXR5IDIwMTAwHhcNMjEwOTMwMTgyMjI1WhcNMzAwOTMwMTgzMjI1 # WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH # UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD # Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCAiIwDQYJKoZIhvcNAQEB # BQADggIPADCCAgoCggIBAOThpkzntHIhC3miy9ckeb0O1YLT/e6cBwfSqWxOdcjK # NVf2AX9sSuDivbk+F2Az/1xPx2b3lVNxWuJ+Slr+uDZnhUYjDLWNE893MsAQGOhg # fWpSg0S3po5GawcU88V29YZQ3MFEyHFcUTE3oAo4bo3t1w/YJlN8OWECesSq/XJp # rx2rrPY2vjUmZNqYO7oaezOtgFt+jBAcnVL+tuhiJdxqD89d9P6OU8/W7IVWTe/d # vI2k45GPsjksUZzpcGkNyjYtcI4xyDUoveO0hyTD4MmPfrVUj9z6BVWYbWg7mka9 # 7aSueik3rMvrg0XnRm7KMtXAhjBcTyziYrLNueKNiOSWrAFKu75xqRdbZ2De+JKR # Hh09/SDPc31BmkZ1zcRfNN0Sidb9pSB9fvzZnkXftnIv231fgLrbqn427DZM9itu # qBJR6L8FA6PRc6ZNN3SUHDSCD/AQ8rdHGO2n6Jl8P0zbr17C89XYcz1DTsEzOUyO # ArxCaC4Q6oRRRuLRvWoYWmEBc8pnol7XKHYC4jMYctenIPDC+hIK12NvDMk2ZItb # oKaDIV1fMHSRlJTYuVD5C4lh8zYGNRiER9vcG9H9stQcxWv2XFJRXRLbJbqvUAV6 # bMURHXLvjflSxIUXk8A8FdsaN8cIFRg/eKtFtvUeh17aj54WcmnGrnu3tz5q4i6t # AgMBAAGjggHdMIIB2TASBgkrBgEEAYI3FQEEBQIDAQABMCMGCSsGAQQBgjcVAgQW # BBQqp1L+ZMSavoKRPEY1Kc8Q/y8E7jAdBgNVHQ4EFgQUn6cVXQBeYl2D9OXSZacb # UzUZ6XIwXAYDVR0gBFUwUzBRBgwrBgEEAYI3TIN9AQEwQTA/BggrBgEFBQcCARYz # aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9Eb2NzL1JlcG9zaXRvcnku # aHRtMBMGA1UdJQQMMAoGCCsGAQUFBwMIMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIA # QwBBMAsGA1UdDwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNX2 # VsuP6KJcYmjRPZSQW9fOmhjEMFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwu # bWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dF8yMDEw # LTA2LTIzLmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYBBQUHMAKGPmh0dHA6Ly93 # d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYt # MjMuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQCdVX38Kq3hLB9nATEkW+Geckv8qW/q # XBS2Pk5HZHixBpOXPTEztTnXwnE2P9pkbHzQdTltuw8x5MKP+2zRoZQYIu7pZmc6 # U03dmLq2HnjYNi6cqYJWAAOwBb6J6Gngugnue99qb74py27YP0h1AdkY3m2CDPVt # I1TkeFN1JFe53Z/zjj3G82jfZfakVqr3lbYoVSfQJL1AoL8ZthISEV09J+BAljis # 9/kpicO8F7BUhUKz/AyeixmJ5/ALaoHCgRlCGVJ1ijbCHcNhcy4sa3tuPywJeBTp # kbKpW99Jo3QMvOyRgNI95ko+ZjtPu4b6MhrZlvSP9pEB9s7GdP32THJvEKt1MMU0 # sHrYUP4KWN1APMdUbZ1jdEgssU5HLcEUBHG/ZPkkvnNtyo4JvbMBV0lUZNlz138e # W0QBjloZkWsNn6Qo3GcZKCS6OEuabvshVGtqRRFHqfG3rsjoiV5PndLQTHa1V1QJ # sWkBRH58oWFsc/4Ku+xBZj1p/cvBQUl+fpO+y/g75LcVv7TOPqUxUYS8vwLBgqJ7 # Fx0ViY1w/ue10CgaiQuPNtq6TPmb/wrpNPgkNWcr4A245oyZ1uEi6vAnQj0llOZ0 # dFtq0Z4+7X6gMTN9vMvpe784cETRkPHIqzqKOghif9lwY1NNje6CbaUFEMFxBmoQ # tB1VM1izoXBm8qGCAtcwggJAAgEBMIIBAKGB2KSB1TCB0jELMAkGA1UEBhMCVVMx # EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT # FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMkTWljcm9zb2Z0IElyZWxh # bmQgT3BlcmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjoz # QkQ0LTRCODAtNjlDMzElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vy # dmljZaIjCgEBMAcGBSsOAwIaAxUA942iGuYFrsE4wzWDd85EpM6RiwqggYMwgYCk # fjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH # UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD # Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQUFAAIF # AOpJhYcwIhgPMjAyNDA3MjMwOTI3MzVaGA8yMDI0MDcyNDA5MjczNVowdzA9Bgor # BgEEAYRZCgQBMS8wLTAKAgUA6kmFhwIBADAKAgEAAgIE9gIB/zAHAgEAAgISXDAK # AgUA6krXBwIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIB # AAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUAA4GBAKXq6tSuyrlwxkg/ # J9kYj751iJXUpCRlXIEIvlV/6kJEWJGuEOZGbIIbq8p2E6Ey7ixup6RoCvhC/KB4 # 8YfDionhUDmuCr6M3+T+RXeyRSN4q3ZN+slYdzprDL9FrvpqXoQd+2NNeSpB2rI+ # vrR5WoiNnCFIgVK4IDHKXQgst/kgMYIEDTCCBAkCAQEwgZMwfDELMAkGA1UEBhMC # VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV # BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp # bWUtU3RhbXAgUENBIDIwMTACEzMAAAHlj2rA8z20C6MAAQAAAeUwDQYJYIZIAWUD # BAIBBQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0B # CQQxIgQgEEbYmNq/ia6r6NsLPCKYuexDQeUbmqIsvjBMZDV8ijEwgfoGCyqGSIb3 # DQEJEAIvMYHqMIHnMIHkMIG9BCAVqdP//qjxGFhe2YboEXeb8I/pAof01CwhbxUH # 9U697TCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u # MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp # b24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAB # 5Y9qwPM9tAujAAEAAAHlMCIEIN2DXzqqPnfDPq8kn3VhILvJ/1JGplHAaWk/ZRf8 # 66epMA0GCSqGSIb3DQEBCwUABIICAA8OtQAwWvYQ4fna841MdsAgwEKrKPrhjvD/ # 81tanuWcRVVOCS8tD4uIPwL2OjEiC62fmzOw6yxh2Qqe2esPZfv77pkaHTL6t1DL # Llid2HydC2cPmHS59i1QE9RRERrYx1xlnRO7R3Q2SQ66NhMsIGdWOaZqbeirLR6k # VODkX88vYgDiUaoDbZQ3is69f8RcTII0hJ4Pgd3BGYSPNRp5yXLlVm7f+4orj9jh # E1A/CR+q8Joe5GIWHLrXmP7RlOlXaKU7Uv5kF27KVlkm0WB2KFmcVT3xH206Eg2I # 9b0GHMFWFVjQO5B4wbtQxnB0sKJbA3Y++jFCP9oc1TyktEt9+v2MfHZJQORvLNna # /Fb0Zpp+zxIN0gceHGDChWnsTFIJ82JVj0PnE3IwVL/bnp4P5Ex8Z3fm+eRDMN69 # ChFQo7/0BukBtq+VoLef34Rb+HHaquTO8QfC4Jw8bL3oYffWtF4wO6X+QYXqPCa1 # /wgtTYilUSJOAzp7duZcKHWARGqSfI9rNTxT75MDYowYAc74O8ag940tglkGe/jJ # pEDEn4K4FapVp3Abg1eiHIuN4dmF4iS0Wpghqtdyjk9h5nXCOgMOcETiSaEbfjXG # YvswF21cbHCFAs9rzQItX3WlM0AAp3MmxZ3pchrzcH3hHrVAnmrTEB3UnKCTH0tK # ooSKWiNd # SIG # End signature block |