RestoreReplay.ps1
<#PSScriptInfo
.COMPANYNAME Microsoft .VERSION 1.0.1 .GUID 5163768d-5359-48f8-8bc5-02b93f251115 .AUTHOR Microsoft-Sdn-DevTeam .SYNOPSIS Restore NetworkController by replaying the data from a copy of SDNAPI IMOS database or a NetworkController backup. .DESCRIPTION Extract the resource JSONs from SDNAPI IMOS or a NetworkController backup and reply it. If the replay is from an IMOS DB(BackupType Manual) and AesKey used is not available, all the credential resources should be manually re-added - which includes credentials resources and virtual gateway networkconnection credentials. If the BackupType is NetworkController, then the AesKey is extracted from the backup. Input directory structure should not be modified. Following is an example of the IMOS DB directory structure. In this case BackupDirectoryOrZipFile will be "C:\R_131705518767194909" Directory: C:\R_131705518767194909 Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---- 6/1/2018 10:47 AM 8192 edb.chk -a---- 6/1/2018 10:44 AM 10485760 edb.log -a---- 6/1/2018 10:43 AM 10485760 edb00000006.log -a---- 6/1/2018 10:44 AM 10485760 edb00000007.log -a---- 6/1/2018 10:35 AM 10485760 edbres00001.jrs -a---- 6/1/2018 10:35 AM 10485760 edbres00002.jrs -a---- 6/1/2018 10:43 AM 10485760 edbtmp.log -a---- 6/1/2018 10:44 AM 16793600 ImosStore -a---- 6/1/2018 10:36 AM 0 ImosStore.pat Following is an example of NetworkController backup directory structure. In this case BackupDirectoryOrZipFile will be "C:\NetworkControllerGeneratedBackup" Directory: C:\NetworkControllerGeneratedBackup Mode LastWriteTime Length Name ---- ------------- ------ ---- d----- 5/31/2018 4:41 PM ApiService d----- 5/31/2018 4:41 PM NamedPropertyStore -a---- 5/8/2018 11:40 AM 11 VersionId #> <# .PARAMETER NCRestEndPoint A DNS name or IP address sepcifying the NetworkController rest endpoint. .PARAMETER BackupDirectoryOrZipFile Directory where a copy of the SDNAPI IMOS database or a zip file containing the backup is present. User credentials used to run the script should have write permissions to this directory. .PARAMETER AesKey AES key that was used with the NetworkController where the backup was taken. If the OperationType is NetworkController, this parameter is not needed and the AES key is extracted from the NamedProperty backup. .PARAMETER SkipResetReplica Skip the reset of existing IMOS db. Use the option to retry the replay if some of the resources failed to provision. .PARAMETER IgnoreFailure Ignore the failure to a put a resource and continue with other resources. Ignoring a failure may result in cascading failures for other resources. .PARAMETER Credential Specifies a user credential that has permission to perform this action. .PARAMETER EseUtilPath Path where the eseutil.exe is present. Default is script_path\ReplicatedStoreViewer .PARAMETER SDNImosStoreExplorerPath Path where the SDNImosStoreExplorer.exe is present. Default is script_path .PARAMETER OperationType Type of operation to perform [Default ExtractAndReplay]. ExtractOnly - Extract the resources to BackupDirectory\Jsons. ReplayOnly - Replay the resources from BackupDirectory\Jsons. ExtractAndReplay - Extract the resources and replay it. Put - A plain put of all the resources under <BackupDirectoryOrZipFile>\Jsons. .PARAMETER BackupType Type of backup. Manual - A manually copied SDNAPI IMOS dump. The directory should have all the files copied from the service fabric SDNAPI replica IMOS folder. NetworkController - A backup created by PutNetworkControllerBackup operation or zip file created by Autobackup. .PARAMETER GetAesKey Fetch the current AESKey from networkcontroller. .PARAMETER MultisiteRestoreOperation Type of multisite restore operation to perform. ReplayPrimarySite - Used to indicate that the replay should happen on primary site. Restores all resources. ReplaySecondaryPrerequisiteLocalResourcesOnly - Replays only local resources that global resources are depdendent on. May be used when NC site needs to sync global resources using multisite. Resets references in local resources to global resources. ReplaySecondaryLocalResourcesOnly - Replays only local resources. May be used after global resources have been synced using multisite. Skips reset of replicas when this switch is turned on. .PARAMETER Force Force run the operation. Specifiying the force parameter has the following implications. 1. If the BackupDirectoryOrZipFile parameter is a zip file, and a directory is already present with the extracted contents it is overwritten. 2. Do not wait for NetworkController to become healthy before starting the restore. .EXAMPLE ./RestoreReplay.ps1 -NCRestEndPoint NC.contoso.com -BackupDirectory C:\R_131705518767194909 -BackupType Manual -AesKey "<AesKeyPlaceholder>" Perform restore from a SDNAPI Imos dump present under c:\R_131705518767194909 .EXAMPLE ./RestoreReplay.ps1 -NCRestEndPoint NC.contoso.com -BackupDirectory C:\AutoBackup_2018-05-30T14-35-52.zip -BackupType NetworkController -OperationType ExtractOnly -Force Extract the resources from an autoback zip file generated by networkcontroller. Because -Force parameter is specified if "C:\AutoBackup_2018-05-30T14-35-52" is present, it will be overwritten with contents of "AutoBackup_2018-05-30T14-35-52.zip" .EXAMPLE ./RestoreReplay.ps1 -NCRestEndPoint NC.contoso.com -BackupDirectory C:\AutoBackup_2018-05-30T14-35-52 -BackupType NetworkController -OperationType ReplayOnly -Verbose Replay the resource from C:\AutoBackup_2018-05-30T14-35-52. A detailed log is also written to the console. .EXAMPLE # On site 1: ./RestoreReplay.ps1 -NCRestEndPoint RestServer-S3 -BackupDirectory C:\rest\bcdr3 -BackupType NetworkController -MultisiteRestoreOperation ReplayPrimarySite # On site 2: ./RestoreReplay.ps1 -NCRestEndPoint RestServer-S4 -BackupDirectory C:\rest\bcdr3 -BackupType NetworkController -OperationType ExtractOnly ./RestoreReplay.ps1 -NCRestEndPoint RestServer-S4 -BackupDirectory C:\rest\bcdr3 -BackupType NetworkController -OperationType ReplayOnly -MultisiteRestoreOperation ReplaySecondaryPrerequisiteLocalResourcesOnly # (sync sites using multisite) # On site 2: ./RestoreReplay.ps1 -NCRestEndPoint RestServer-S4 -BackupDirectory C:\rest\bcdr3 -BackupType NetworkController -OperationType ReplayOnly -MultisiteRestoreOperation ReplaySecondaryLocalResourcesOnly Replay resources in multisite scenario. .NOTES .LINK #> [CmdletBinding(DefaultParameterSetName="Default",SupportsShouldProcess=$True, ConfirmImpact="High")] param( [Parameter(position=0,mandatory=$false,ParameterSetName="Default")] [ValidateNotNullOrEmpty()] [string]$NCRestEndPoint, [Parameter(position=1,mandatory=$true,ParameterSetName="Default")] [ValidateNotNullOrEmpty()] [string]$BackupDirectoryOrZipFile, [Parameter(position=2,mandatory=$false,ParameterSetName="Default")] [ValidateNotNullOrEmpty()] [string]$AesKey, [Parameter(position=3,mandatory=$false,ParameterSetName="Default")] [switch]$SkipResetReplica, [Parameter(position=4,mandatory=$false,ParameterSetName="Default")] [switch]$IgnoreFailure, [ValidateNotNull()] [Parameter(position=5,mandatory=$false,ParameterSetName="Default")] [PSCredential] $Credential = [System.Management.Automation.PSCredential]::Empty, [Parameter(position=6,mandatory=$false,ParameterSetName="Default")] [ValidateNotNullOrEmpty()] [string] $EseUtilPath, [Parameter(position=7,mandatory=$false,ParameterSetName="Default")] [ValidateNotNullOrEmpty()] [string] $SDNImosStoreExplorerPath, [Parameter(position=8,mandatory=$false,ParameterSetName="Default")] [ValidateSet("ExtractOnly","ReplayOnly","ExtractAndReplay","Put")] [string] $OperationType = "ExtractAndReplay", [Parameter(position=9,mandatory=$false,ParameterSetName="Default")] [ValidateSet("Manual","NetworkController")] [string] $BackupType, [Parameter(position=10,mandatory=$false,ParameterSetName="Default")] [ValidateSet("ReplayPrimarySite","ReplaySecondaryLocalResourcesOnly","ReplaySecondaryPrerequisiteLocalResourcesOnly")] [string] $MultisiteRestoreOperation, [Parameter(position=0,mandatory=$true,ParameterSetName="GetAesKey")] [switch] $GetAesKey, [Parameter(position=11,mandatory=$false,ParameterSetName="Default")] [switch] $Force ) #Globals $script:E_PropertyNotFound = -2147017783 $script:NCApplicationUri = "fabric:/NetworkController" $script:SDNApiServiceUri = "fabric:/NetworkController/ApiService" $script:ncNameSpace = "root\Microsoft\Windows\NetworkController\Server" $script:succeededProvState = "Succeeded" $script:updatingProvState = "Updating" $script:failedProvState = "Failed" $script:NCHealthWaitTimeoutInSec = 20 * 60 $script:provisioningTimeoutInSec = 15 * 60 $script:ForceOperation = $false $script:ResultMessages = @() $script:WarningMessages = @() $script:ErrorMessages = @() $script:LogLevels = @{ Verbose = "VERBOSE" Info = "INFO" Warn = "WARN" Error = "ERROR" Fatal = "FATAL" } $script:BackupTypes = @{ Manual = "Manual" NetworkController = "NetworkController" } $script:OperationTypes = @{ ExtractOnly = "ExtractOnly" ReplayOnly = "ReplayOnly" ExtractAndReplay = "ExtractAndReplay" Put = "Put" } $script:LogLevel = $script:LogLevels["Warn"] $script:GlobalBaseTypes = @( 'virtualnetworkmanager', 'virtualswitchmanager', 'iDnsServer', 'securityTags', 'accessControlLists', 'routetables', 'serviceInsertions', 'virtualnetworks', 'networkinterfaces') # Local base types that global base types are dependent on $script:PrerequisiteLocalBaseTypes = @( 'logicalnetworks', 'macPools') function FixPwdPath { param( [string]$Path ) if([String]::IsNullOrEmpty($Path)) { return $Path } $newPath = $Path.ToLower() if($newPath.StartsWith(".\")) { $newPath = $((Get-Location).Path) + $newPath.TrimStart(".") } return $newPath } function Initialize { $script:Initialzed = $false $script:scriptDir = Split-Path $script:MyInvocation.MyCommand.Path $script:LogFile = Join-Path -Path $script:scriptDir -ChildPath "DebugLog.txt" Write-Log "Initializing..." $script:BackupDirectory = FixPwdPath -Path $BackupDirectoryOrZipFile $script:AesCryptoKey = $AesKey if($IgnoreFailure.IsPresent) { $script:IgnorePutFailure = $true } if($script:BackupDirectory.EndsWith(".zip")) { $backupZipFile = $script:BackupDirectory.Split("\") | Select-Object -Last 1 $backupPath = $script:BackupDirectory $destination = $script:BackupDirectory.TrimEnd($backupZipFile) $script:BackupDirectory = $script:BackupDirectory.TrimEnd(".zip") if(-not ($($OperationType -eq $script:OperationTypes["ReplayOnly"]) -or $($OperationType -eq $script:OperationTypes["Put"]))) { Add-Type -assembly "system.io.compression.filesystem" if($(Test-Path $(Join-Path -Path $script:BackupDirectory -ChildPath "*"))) { if($script:ForceOperation) { Remove-Item $script:BackupDirectory -Force -ErrorAction Stop -Recurse } else { throw "Zip file provided, but the destination for extracting the zip $($script:BackupDirectory) is not empty. Delete the directory or rerun with -Force, aborting restore" } } [io.compression.zipfile]::ExtractToDirectory($BackUpPath, $destination) } } if($BackupType -eq $($script:BackupTypes["NetworkController"])) { if($OperationType -ne "ExtractOnly" -and [String]::IsNullOrEmpty($script:AesCryptoKey)) { #If backup type is NetworkController, try to extract AESKey $NamedPropertyXml = Join-Path -Path $script:BackupDirectory -ChildPath "NamedPropertyStore" $NamedPropertyXml = Join-Path -Path $NamedPropertyXml -ChildPath "NamedPropertyStore.xml" if($(Test-Path $NamedPropertyXml) -eq $true) { [xml]$contentXml = Get-Content $NamedPropertyXml $script:AesCryptoKey = $contentXml.NamedPropertyStore.AESKey Write-VerboseLog "Extracted AESKey" } } } if ($OperationType -eq "ExtractOnly" -or $OperationType -eq "ExtractAndReplay") { $tempDir = [System.IO.Path]::GetTempPath() if (!(Test-Path -Path $tempDir)) { mkdir $tempDir } $imosDir = $script:BackupDirectory if ($BackupType -eq $($script:BackupTypes["NetworkController"])) { #Fix up ImosDirectory $imosDir = Join-Path -Path $imosDir -ChildPath "ApiService" $imosDir = Join-Path -Path $imosDir -ChildPath "new" } Copy-item -Path $imosDir -Destination $tempDir -Recurse -Force $script:ImosDirectory = Join-Path -Path $tempDir -ChildPath (Get-Item -Path $imosDir).BaseName $script:ImosFile = Join-Path -Path $script:ImosDirectory -ChildPath "ImosStore" $script:ImosStoreOutputFile = Join-Path -Path $script:BackupDirectory -ChildPath "ImoStoreOutput.txt" } $script:JsonOutputDirectory = Join-Path -Path $script:BackupDirectory -ChildPath "Jsons" if([String]::IsNullOrEmpty($EseUtilPath)) { $script:eseUtilExe = Join-Path -Path $script:scriptDir -ChildPath "ReplicatedStoreViewer" } else { $script:eseUtilExe = FixPwdPath -Path $EseUtilPath } $script:eseUtilExe = Join-Path -Path $script:eseUtilExe -ChildPath "eseutil.exe" if([String]::IsNullOrEmpty($SDNImosStoreExplorerPath)) { $script:ImosExplorerExe = $script:scriptDir } else { $script:ImosExplorerExe = FixPwdPath -Path $SDNImosStoreExplorerPath } $script:ImosExplorerExe = Join-Path -Path $script:ImosExplorerExe -ChildPath "SDNImosStoreExplorer.exe" ValidateParameters #Os Version $script:OsVersion = [System.Environment]::OSVersion.Version.Build $script:RS1Version = 14393 if($Credential -eq [System.Management.Automation.PSCredential]::Empty) { $script:DefaultCredParaSet = $true } else { $script:DefaultCredParaSet = $false } $script:NetworkControllerCred = $Credential if($SkipResetReplica.IsPresent) { $script:RestReplica = $false } else { $script:RestReplica = $true } if($OperationType -ne "ExtractOnly") { $discoveryUri = "https://$NCRestEndPoint/Networking/Discovery" $result = Invoke-WebRequestWithRetries -Uri $discoveryUri -UseBasicParsing -DisableKeepAlive -Method "Get" if([String]::IsNullOrEmpty($result)) { $script:NCRestURI = "https://$NCRestEndPoint/Networking/v1" } else { $json = $result.Content | ConvertFrom-Json $script:NCRestURI = "https://$NCRestEndPoint/Networking/$($json.properties.currentRestVersion)" } } if ($MultisiteRestoreOperation -eq "ReplaySecondaryLocalResourcesOnly" -or $MultisiteRestoreOperation -eq "ReplaySecondaryPrerequisiteLocalResourcesOnly" -or $MultisiteRestoreOperation -eq "ReplayPrimarySite") { $script:MultisiteRestore = $true ValidatePreExtractMultisiteConditions } else { $script:MultisiteRestore = $false } Write-VerboseLog "Using RestURL $script:NCRestURI" $script:Initialzed = $true } function ValidateParameters { if($OperationType -ne $script:OperationTypes["Put"] -and [String]::IsNullOrEmpty($BackupType)) { throw "BackupType is not specified, aborting restore" } if([String]::IsNullOrEmpty($script:BackupDirectory)) { throw "Backup directory is not provided, aborting restore" } if($OperationType -ne $script:OperationTypes["ReplayOnly"] -and $OperationType -ne $script:OperationTypes["Put"]) { if($(Test-Path $script:eseUtilExe) -eq $false) { throw "EseUtil exe not found at $($script:eseUtilExe). Use EseUtilPath to provide an alternate location, aborting restore" } if($(Test-Path $script:ImosExplorerExe) -eq $false) { throw "SDNImosStoreExplorer.exe not found at $($script:ImosExplorerExe). Use SDNImosStoreExplorerPath to provide an alternate loation, aborting restore" } if($(Test-Path -Path $script:ImosFile) -eq $false) { throw "ImosStore file not found at $script:ImosFile, aborting restore" } } if($OperationType -eq $script:OperationTypes["ReplayOnly"] -or $OperationType -eq $script:OperationTypes["Put"]) { if($(Test-Path $script:JsonOutputDirectory) -eq $false) { throw "$script:JsonOutputDirectory is empty, aborting restore" } } if($OperationType -ne $script:OperationTypes["ExtractOnly"]) { if([String]::IsNullOrEmpty($NCRestEndPoint)) { throw "NCRestEndpoint can be empty only if OperationType is $($script:OperationTypes["ExtractOnly"]) , aborting restore" } if(-not $script:ForceOperation) { Write-Log "Checking NC Health" WaitForServiceFabricHealth -WaitTimeoutInSec 60 -DoInitialWait $false $result = Test-NetConnection -ComputerName $NCRestEndPoint -Port 443 if($result.TcpTestSucceeded -eq $false) { throw "Cannot connect to NCRestEndpoint $NCRestEndPoint, aborting $OperationType" } } } if($OperationType -ne $script:OperationTypes["ExtractOnly"] -and ` $OperationType -ne $script:OperationTypes["Put"] -and ` [String]::IsNullOrEmpty($script:AesCryptoKey)) { Write-Log "AES Key not provided. Credentials should be manually readded after the restore" $script:LogLevels["Warn"] } } function PrepareBackup { if(-not $script:OperationTypes["ReplayOnly"]) { Cleanup } $origPath = (Get-Location).Path try { Set-Location $script:ImosDirectory Write-Log "Recovering Database from $($script:ImosDirectory)" $recoveryArgs = @("-r", "edb", "-t", "-i", "-d") $result = & $script:eseUtilExe $recoveryArgs Write-VerboseLog "Recovery Result: $($result)" Write-VerboseLog "Repairing Database $($script:ImosFile)" $repairArgs = @("-p", $script:ImosFile, "-o") $result = & $script:eseUtilExe $repairArgs Write-VerboseLog "Repair Result: $($result)" $stateArgs = @("-m", $script:ImosFile) $result = & $script:eseUtilExe $stateArgs $state = $result | Select-String "State:" Write-VerboseLog "Database state is $state" $cleanShutDown = $state | Select-String "clean shutdown" if($cleanShutDown -eq $null) { throw "Database recovery failed, database state is $state" } } finally { Set-Location $origPath } $dbExtractArgs = @("-dir", $script:ImosDirectory, "-out", $script:ImosStoreOutputFile) Write-VerboseLog "Extracting database to $($script:ImosStoreOutputFile)" $result = & $script:ImosExplorerExe $dbExtractArgs Write-VerboseLog "Extract Result: $($result)" if($(Test-Path $script:ImosStoreOutputFile) -eq $false) { throw "IMOS DB output file not generated, failing prepare backup" } GenerateJsons -OutDir $script:JsonOutputDirectory -ImosStoreDump $script:ImosStoreOutputFile } function PerformPreRestore { if(-not [String]::IsNullOrEmpty($script:AesCryptoKey)) { Write-VerboseLog "Setting AesKey" #Set the AesKey $Arguments = @{ AesKey = $script:AesCryptoKey; }; $result = Invoke-CimMethod -ClassName PS_NetworkController -Namespace $script:ncNameSpace -MethodName SetAesKey -Arguments $Arguments -ErrorAction Stop -Confirm:$false } # Set the SF restore property $result = SetAndValidateSFProperty -Section "fabric:/NetworkController/GlobalConfiguration" -Property "Global.UseValueInRestObject" -Value "True" #If this is a first replay restore on a fresh installation, replica reset can be skipped if($script:RestReplica -eq $true -and -not ($MultisiteRestoreOperation -eq "ReplaySecondaryLocalResourcesOnly")) { ResetReplicas } else { Write-Log "Skipping replica reset" RestartSDNApiPrimaryReplica WaitForServiceFabricHealth -ServiceUri $script:SDNApiServiceUri } } function StartRestore { Write-Log "Starting resource replay" ImportJsonInOrder -BaseType "credentials" ImportJsonInOrder -BaseType "virtualnetworkmanager" ImportJsonInOrder -BaseType "virtualswitchmanager" ImportJsonInOrder -BaseType "iDnsServer" ImportJsonInOrder -BaseType "securityTags" ImportJsonInOrder -BaseType "accessControlLists" ImportJsonInOrder -BaseType "routetables" ImportJsonInOrder -BaseType "serviceInsertions" ImportJsonInOrder -BaseType "logicalnetworks" ImportJsonInOrder -BaseType "macPools" ImportJsonInOrder -BaseType "loadBalancerManager" ImportJsonInOrder -BaseType "publicIpAddresses" ImportJsonInOrder -BaseType "gatewaypools" ImportJsonInOrder -BaseType "servers" ImportJsonInOrder -BaseType "networkinterfaces" -Type "Fabric" ImportJsonInOrder -BaseType "virtualServers" ImportJsonInOrder -BaseType "loadBalancerMuxes" ImportJsonInOrder -BaseType "virtualnetworks" ImportJsonInOrder -BaseType "loadBalancers" -Type "PublicLB" ImportJsonInOrder -BaseType "loadBalancers" -Type "InternalLB" ImportJsonInOrder -BaseType "networkinterfaces" -Type "Tenant" ImportJsonInOrder -BaseType "gateways" ImportJsonInOrder -BaseType "virtualgateways" } function RestartSDNApiPrimaryReplica { Write-Log "Restarting SDNAPI primary replica" $WarningPreference='silentlycontinue' $outNull = Connect-ServiceFabricCluster $WarningPreference='continue' $outNull = Restart-ServiceFabricReplica -ServiceName $script:SDNApiServiceUri -ReplicaKindPrimary } function PerformPostRestore { try { Write-Log "Performing Post restore operations" # Reset the SF restore property $isChanged = SetAndValidateSFProperty -Section "fabric:/NetworkController/GlobalConfiguration" -Property "Global.UseValueInRestObject" -Value "False" if($isChanged -eq $true) { RestartSDNApiPrimaryReplica WaitForServiceFabricHealth -ServiceUri $script:SDNApiServiceUri -WaitTimeoutInSec 120 } } catch { Write-ExceptionLog "Performing Post Restore failed" $_ } } #region helper functions <# Get or set a property in service fabric. If $Value is null, it is treated as a Get operation, otherwise a Put operation #> function GetOrPutProperty() { param( [string]$Section, [string]$Property, [string]$Value, [string]$Operation ) $client = $null try { $client=[System.Fabric.FabricClient]::new() $IsGet = $false if($Operation -eq "Get") { $IsGet = $true } if($IsGet) { $task = $client.PropertyManager.GetPropertyAsync($Section, $Property) } else { $task = $client.PropertyManager.PutPropertyAsync($Section, $Property, $Value) } try { $task.Wait() } catch { } $hresult = 0 if($task.IsFaulted -eq $true) { if($task.Exception.InnerException -ne $null) { $hresult = $task.Exception.InnerException.HResult $exception = $task.Exception.InnerException } else { $hresult = $hresult = $task.Exception.HResult $exception = $task.Exception } } if($IsGet) { if($hresult -eq $script:E_PropertyNotFound) { return $null } if($hresult -eq 0) { $curValue=[System.Fabric.NamedProperty].getmethod("GetValue").MakeGenericMethod([string]).Invoke($task.Result, $null); return $curValue } } if($hresult -ne 0 ) { throw "GetOrPutSF failed with $exception" } } finally { if($client -ne $null) { $client.Dispose() } } } function SetAndValidateSFProperty { param( [string]$Section, [string]$Property, [string]$Value ) $WarningPreference='silentlycontinue' $outNull = Connect-ServiceFabricCluster $WarningPreference='continue' $retryInterval = 30 $retryCount = 3 $count = 1 $changed = $false while($count -le $retryCount) { $client = $null try { $curValue = GetOrPutProperty -Section $Section -Property $Property -Operation "Get" if($curValue -eq $Value) { break } $outNull = GetOrPutProperty -Section $Section -Property $Property -Value $Value -Operation "Set" $curValue = GetOrPutProperty -Section $Section -Property $Property -Operation "Get" if($curValue -eq $Value) { $changed = $true break } } catch { if($count -eq $retryCount) { throw $_ } } $count += 1 } return $changed } function WaitForServiceFabricHealth { param( $WaitTimeoutInSec = $script:NCHealthWaitTimeoutInSec, $DoInitialWait = $true, $ServiceUri = $null ) $WarningPreference='silentlycontinue' $outNull = Connect-ServiceFabricCluster $WarningPreference='continue' $waitST = Date Write-Log "Waiting upto $WaitTimeoutInSec seconds for NC services to become healthy" if($DoInitialWait) { #Its take a bit for the status to change, wait for a minute before checking. Start-Sleep 60 } while($true) { if([String]::IsNullOrEmpty($ServiceUri)) { $health = Get-ServiceFabricApplicationHealth -ApplicationName $script:NCApplicationUri $status = $health.DeployedApplicationHealthStates $aggreStatus = $status[0].AggregatedHealthState } else { $health = Get-ServiceFabricPartition -ServiceName $ServiceUri | Get-ServiceFabricPartitionHealth $aggreStatus = $health.AggregatedHealthState } if($aggreStatus -ne "Ok") { Write-VerboseLog "Waiting for NC service replicas to become healthy..." $healtStr = $health | Out-String #Check for timeout $curTime = Date $timeElapsed = ($curTime - $waitST).TotalSeconds if($timeElapsed -gt $WaitTimeoutInSec) { Write-VerboseLog $healtStr throw "Timed out waiting for NC service replica health, aborting restore" } Sleep 30 } else { Write-VerboseLog "NC service is healthy" break } } } function ResetReplicas { Write-Log "Resetting NC Service replicas" $WarningPreference='silentlycontinue' $outNull = Connect-ServiceFabricCluster $WarningPreference='continue' $NCServices = Get-ServiceFabricService -ApplicationName $script:NCApplicationUri foreach($ncService in $NCServices) { Write-VerboseLog "Resetting replicas of $($ncService.ServiceName), partition $($partition.PartitionId)" $partition = Get-ServiceFabricPartition -ServiceName $ncService.ServiceName $replicas = Get-ServiceFabricReplica -PartitionId $partition.PartitionId foreach($replica in $replicas) { Write-VerboseLog "Resetting replica $($replica.ReplicaOrInstanceId) of $($ncService.ServiceName)" try { if($script:OsVersion -eq $script:RS1Version) { $result = Remove-ServiceFabricReplica -PartitionId $partition.PartitionId -NodeName $replica.NodeName ` -ReplicaOrInstanceId $replica.ReplicaOrInstanceId -ForceRemove -ErrorAction Stop } else { # Try graceful remove first in RS5+ $result = Remove-ServiceFabricReplica -PartitionId $partition.PartitionId -NodeName $replica.NodeName ` -ReplicaOrInstanceId $replica.ReplicaOrInstanceId -ErrorAction Stop } Write-VerboseLog $result } catch { Write-VerboseLog "Reset Replica failed, retrying ForceRemove" $result = Remove-ServiceFabricReplica -PartitionId $partition.PartitionId -NodeName $replica.NodeName ` -ReplicaOrInstanceId $replica.ReplicaOrInstanceId -ForceRemove -ErrorAction Stop Write-VerboseLog $result } } } WaitForServiceFabricHealth } function GenerateJsons { param( [Parameter(Mandatory=$true)] [string] $OutDir, [Parameter(Mandatory=$true)] [string] $ImosStoreDump ) Write-Log "Dumping resources from $ImosStoreDump. This may take a few minutes.." $reader = [System.IO.File]::OpenText($ImosStoreDump) $countRecords = 0 $startTime = Date try { for() { $line = $reader.ReadLine() if ($line -eq $null) { break } $objs = $line.Split(",") $objs[0] = $objs[0].replace("/", "\") $pathToken = $objs[0].Split("\") $dir = $OutDir + "\\" + $pathToken[1] $fileName = $pathToken[2] + ".json" $tokens = $objs[2].Split(" ") $output="" foreach($tok in $tokens) { try { $c= [char]([convert]::toint16($tok,16)) $output += $c } catch { } } New-Item -ItemType Directory -Force -Path $dir -Confirm:$false | Out-Null Set-Content -Path "$dir\$fileName" -Value $output -Force -Confirm:$false #Give a status update every 5 minutes $countRecords += 1 $curTime = Date $diff = ($curTime - $startTime).TotalSeconds if($diff -ge 5) { $startTime = Date Write-Log "$countRecords resources extracted so far" $script:LogLevels["Verbose"] } } } finally { $reader.Close() } Write-Log "$countRecords resources extracted from imos database" $script:LogLevels["Info"] } function ImportJsonInOrder() { param ( [parameter(Mandatory=$true)] [string] $BaseType, [parameter(Mandatory=$false)] [string] $BaseUrl = $script:NCRestURI, [parameter(Mandatory=$false)] [string] $BaseFolder = $script:JsonOutputDirectory, [parameter(Mandatory=$false)] [string] $Type = "Generic" ) if (($MultisiteRestoreOperation -eq "ReplaySecondaryLocalResourcesOnly" -and $BaseType -in $script:GlobalBaseTypes) -or ($MultisiteRestoreOperation -eq "ReplaySecondaryPrerequisiteLocalResourcesOnly" -and $BaseType -notin $script:PrerequisiteLocalBaseTypes)) { return; } #TODO: Optimize multiple passes through the full resource set ImportJson -BaseType $BaseType -BaseUrl $BaseUrl -BaseFolder $BaseFolder -Type $Type -State $script:succeededProvState ImportJson -BaseType $BaseType -BaseUrl $BaseUrl -BaseFolder $BaseFolder -Type $Type -State $script:updatingProvState ImportJson -BaseType $BaseType -BaseUrl $BaseUrl -BaseFolder $BaseFolder -Type $Type -State $script:failedProvState } function RemoveLocalReferencesToGlobalResources() { param ( [Parameter(Mandatory=$true)] [string] $Body, [Parameter(Mandatory=$true)] [string] $BaseType ) $jsonBody = $Body | ConvertFrom-Json -ErrorAction Stop # Remove ACL/service insertion from logical subnets if ($BaseType -eq "logicalnetworks") { foreach ($subnet in $jsonbody.properties.subnets) { if ($null -ne $subnet.properties.accessControlList) { $subnet.properties.AccessControlList = $null } if ($null -ne $subnet.properties.ServiceInsertion) { $subnet.properties.ServiceInsertion = $null } } } return $jsonBody | ConvertTo-Json -Depth 50 } function ImportJson() { param ( [parameter(Mandatory=$true)] [string] $BaseType, [parameter(Mandatory=$true)] [string] $BaseUrl, [parameter(Mandatory=$true)] [string] $BaseFolder, [parameter(Mandatory=$true)] [string] $Type, [parameter(Mandatory=$false)] [string] $State ) $headers = @{"Accept"="application/json"} $content = "application/json; charset=UTF-8" $timeout = 10 $method = "Put" $folderPath = "$BaseFolder\$BaseType"; $pathExists = Test-Path $folderPath; if (!$pathExists) { return; } $fileNames = Get-ChildItem -Name $BaseFolder\$BaseType -File #Count retry if the provisioning state fails $retryCount = 3 $retryIntervalInSeconds = 30 foreach($fileName in $fileNames ) { $name = $fileName.PSChildName $resourceId = $name.TrimEnd(".json") $_baseType = $BaseType; if ($resourceId -ne $null) { $_baseType += "/$resourceId" } $uri = "$BaseUrl/$_baseType" $body = Get-Content $BaseFolder\$BaseType\$name -Raw if ($MultisiteRestoreOperation -eq "ReplaySecondaryPrerequisiteLocalResourcesOnly") { $body = RemoveLocalReferencesToGlobalResources -Body $body -BaseType $BaseType } $jsonBody = ConvertFrom-Json $body -ErrorAction Stop $origInstanceId = $jsonBody.instanceId $origProvState = $jsonBody.properties.provisioningState if ([String]::IsNullOrEmpty($origProvState)) { if ($State -ne "Succeeded") { continue } } elseif($origProvState -ne $State) { continue } # TODO: Optimize if($BaseType -eq "networkInterfaces") { $isFabric = $false if($jsonBody.properties.ipConfigurations -eq $null -or $jsonBody.properties.ipConfigurations.Count -eq 0 -or $jsonBody.properties.ipConfigurations[0].properties.subnet.resourceRef.Contains("logicalnetworks") ) { $isFabric = $true #This is a lnet interface,check if there is any lb config foreach($ipconfig in $jsonBody.properties.ipConfigurations) { if(($ipconfig.properties.loadBalancerBackendAddressPools -ne $null -and $ipconfig.properties.loadBalancerBackendAddressPools.Count -ne 0) -or ($ipconfig.properties.loadBalancerInboundNatRules -ne $null -and $ipconfig.properties.loadBalancerInboundNatRules.Count -ne 0) ) { $isFabric = $false break } } } if($Type -eq "Fabric" -and $isFabric -eq $false) { continue } elseif($Type -eq "Tenant" -and $isFabric -eq $true) { continue } } if($BaseType -eq "loadbalancers") { $isIlb = $false $feipConfigs = $jsonBody.properties.frontendIPConfigurations if($feipConfigs -ne $null) { foreach($feip in $feipConfigs) { if($feip.properties.subnet -ne $null) { $subnet = $feip.properties.subnet.resourceRef if($subnet.StartsWith("/virtualNetworks",'OrdinalIgnoreCase') -eq $true) { $isIlb = $true break } } } } if($Type -eq "PublicLB" -and $isIlb -eq $true) { continue } elseif($Type -eq "InternalLB" -and $isIlb -eq $false) { continue } } Write-Log $uri $script:LogLevels["Verbose"] $currentRetryCount = 1 $isErrorFatal = $false while($currentRetryCount -le $retryCount) { try { $outnull = Invoke-WebRequestWithRetries -Headers $headers -ContentType $content -Method $method -Uri $uri -Body $body -DisableKeepAlive -UseBasicParsing $provisioningST = Date $sleepTime = 1 #Wait for provisioning to succceed while($true) { $result = $null $result = Invoke-WebRequestWithRetries -Uri $uri -DisableKeepAlive -UseBasicParsing -Method "Get" $toplevel = convertfrom-json $result.Content if ($toplevel.value -eq $null) { $jsonOut = $toplevel } else { $jsonOut = $toplevel.value } $curInstanceId = $jsonOut.instanceId if(($curInstanceId -ne $origInstanceId) -and $OperationType -ne $script:OperationTypes["Put"]) { $msg = "$($_baseType) current instance id $curInstanceId does not match with backup instance id $origInstanceId, aborting restore" $isErrorFatal = $true throw $msg } if($jsonOut.properties.provisioningState -eq "Failed") { if($BaseType -eq "iDnsServer") { $msg = "Provisioning state failed for $($_baseType). Please check the iDnsServer credentials and DNS server reachablity and re-add the resource" Write-Log $msg $script:WarningMessages += $msg break } elseif($BaseType -eq "virtualgateways") { $msg = "Provisioning state failed for $($_baseType). Please check the NetworkConnection credentials and re-add the resource" Write-Log $msg $script:WarningMessages += $msg break } elseif($origProvState -eq "Failed") { $msg = "Provisioning state failed for previously failed resource $($_baseType)." Write-Log $msg $script:WarningMessages += $msg break } elseif($script:IgnorePutFailure) { $msg = "Provisioning state failed for resource $($_baseType), ignored" Write-Log $msg $script:WarningMessages += $msg break } else { throw "Provisioning state failed for $($_baseType)" } } if($jsonOut.properties.provisioningState -eq "Succeeded") { Write-Log "Provisioning succeed for $($BaseType) $($resourceId)" $script:LogLevels["Verbose"] break; } $curTime = Date $diff = ($curTime - $provisioningST).TotalSeconds if($diff -ge $script:provisioningTimeoutInSec) { throw "Timed out waiting provisioning to succeed for $($BaseType) $($resourceId)" } Write-Log "Waiting provisioning state to succeed for $($BaseType) $($resourceId), current state $($jsonOut.properties.provisioningState)" $script:LogLevels["Verbose"] Sleep $sleepTime } break } catch { if($isErrorFatal) { throw } if($currentRetryCount -ge $retryCount) { $msg = "Restore Failed for resource $($_baseType)`r`n$_`r`n$($_ | Out-String)`r`n" if($script:IgnorePutFailure) { Write-Log $msg $script:WarningMessages += "Restore Failed for resource $($_baseType), ignored" } else { Write-Log $msg $script:LogLevels["Fatal"] throw $msg } } Write-Log "Retrying Failure `r`n$_`r`n$($_.Exception.Response)`r`n" $script:LogLevels["Debug"] $currentRetryCount += 1 Start-Sleep $retryIntervalInSeconds } } } } function Invoke-WebRequestWithRetries { param( [System.Collections.IDictionary] $Headers, [string] $ContentType, [Microsoft.PowerShell.Commands.WebRequestMethod] $Method, [System.Uri] $Uri, [object] $Body, [Switch] $DisableKeepAlive, [Switch] $UseBasicParsing, [Parameter(mandatory=$false)] [bool] $shouldRetry = $true ) $params = @{ 'Headers'=$headers; 'ContentType'=$content; 'Method'=$method; 'uri'=$uri; 'ErrorAction'='Stop'; } if($Body -ne $null) { $params.Add('Body', $Body) } if($DisableKeepAlive.IsPresent) { $params.Add('DisableKeepAlive', $true) } if($UseBasicParsing.IsPresent) { $params.Add('UseBasicParsing', $true) } if ($script:DefaultCredParaSet -eq $true) { $params.Add('UseDefaultCredentials', $true) } elseif($script:NetworkControllerCred -ne [System.Management.Automation.PSCredential]::Empty) { $params.Add('Credential', $script:NetworkControllerCred) } $retryIntervalInSeconds = 30 $maxRetry = 6 $retryCounter = 0 do { try { $result = $null $result = Invoke-WebRequest @params break } catch { Write-VerboseLog "Invoke-WebRequestWithRetries: $($Method) Exception: $_" Write-VerboseLog "Invoke-WebRequestWithRetries: $($Method) Exception: $($_.Exception.Response)" if($_.Exception.Response.StatusCode.value__ -eq 404) { #Dont retry on Not Found break } $retryCounter++ if($retryCounter -le $maxRetry) { Write-VerboseLog "Invoke-WebRequestWithRetries: retry $($Method) $($Uri) operation in $($retryIntervalInSeconds) seconds. Retry count: $($retryCounter)." sleep -Seconds $retryIntervalInSeconds } else { # last retry still fails, so throw the exception throw $_ } } } while ($shouldRetry -and ($retryCounter -le $maxRetry)) return $result } Function Get-AesKey { $result = Invoke-CimMethod -ClassName PS_NetworkController -Namespace $script:ncNameSpace -MethodName GetAESKey -ErrorAction Stop -Confirm:$false $key = $result.AesKey.Value Write-Host $key } Function Write-Log { Param( [Parameter(Mandatory=$True)] [string] $Message, [Parameter(Mandatory=$False)] [String] $Level = $script:LogLevels["Info"] ) $levels = ($script:LogLevels["Verbose"], $script:LogLevels["Info"], $script:LogLevels["Warn"], $script:LogLevels["Error"], $script:LogLevels["Fatal"]) $enabledLogLevel = [array]::IndexOf($levels, $script:LogLevel) $currentLogLevel = [array]::IndexOf($levels, $Level) $Stamp = (Get-Date).toString("yyyy/MM/dd HH:mm:ss:fff") $Line = "$Stamp $Level $Message" Add-Content $script:LogFile -Value $Line -Confirm:$false if($currentLogLevel -ge $enabledLogLevel) { $writeHostPara = @{ Object = $Line } if($Level -eq $script:LogLevels["Warn"]) { $writeHostPara.Add("ForegroundColor", "Yellow") } elseif($Level -eq $script:LogLevels["Error"] -or $Level -eq $script:LogLevels["Fatal"]) { $writeHostPara.Add("ForegroundColor", "Red") } Write-Host @writeHostPara } elseif($Level -eq $script:LogLevels["Info"]) { Write-Debug $line } elseif($Level -eq $script:LogLevels["Verbose"]) { Write-Verbose $line } } Function Write-ExceptionLog { [CmdletBinding()] Param( [Parameter(Mandatory=$True)] [String] $Message, [Parameter(Mandatory=$True)] [Object] $ExpObj, [Parameter(Mandatory=$False)] [String] $Level = $script:LogLevels["Error"] ) $strMsg = $ExpObj | Out-String Write-Log "$Message `r`n $strMsg" $Level } Function Write-VerboseLog { [CmdletBinding()] Param( [Parameter(Mandatory=$True)] [String] $Message ) Write-Log $Message $script:LogLevels["Verbose"] } function Cleanup { Remove-Item $script:ImosStoreOutputFile -Force -ErrorAction SilentlyContinue -Confirm:$false Remove-Item $script:JsonOutputDirectory -Force -ErrorAction SilentlyContinue -Recurse -Confirm:$false } function ValidatePostExtractMultisiteConditions { $multisiteResourcePath = Join-Path -Path $script:JsonOutputDirectory -ChildPath "multisite" if ($script:MultisiteRestore) { if (!(Test-Path -Path $multisiteResourcePath) -or !((Get-ChildItem -Path $multisiteResourcePath).Count -gt 0)) { throw "Multisite resource not found for a multisite restore." } } else { if ((Test-Path -Path $multisiteResourcePath) -and ((Get-ChildItem -Path $multisiteResourcePath).Count -gt 0)) { $script:WarningMessages += "Multisite resource found, but MultisiteRestoreOperation parameter not given." } } } function ValidatePreExtractMultisiteConditions { if ($MultisiteRestoreOperation -eq "ReplaySecondaryPrerequisiteLocalResourcesOnly") { $multisite = Get-NetworkControllerMultisiteConfiguration -ConnectionUri "https://$NCRestEndPoint" foreach ($site in $multisite.Properties.Sites) { if ($site.Properties.State -eq "Connected") { throw "Multisite sync detected. The ReplaySecondaryPrerequisiteLocalResourcesOnly option should only be used for restoring local resources so that initial sync is successful." } } } elseif ($MultisiteRestoreOperation -eq "ReplaySecondaryLocalResourcesOnly") { $multisite = Get-NetworkControllerMultisiteConfiguration -ConnectionUri "https://$NCRestEndPoint" $multisiteSyncExists = $false foreach ($site in $multisite.Properties.Sites) { if ($site.Properties.State -eq "Connected") { $multisiteSyncExists = $true break } } if (-not $multisiteSyncExists) { throw "Multisite sync not detected. The ReplaySecondaryLocalResourcesOnly option should be used after peering so that local resources with references to global resources resolve correctly." } } } #endregion if ($PSBoundParameters['Debug']) { $DebugPreference = 'Continue' } if ($PSBoundParameters['Verbose']) { $DebugPreference = 'Continue' } if ($PSBoundParameters['Force']) { $script:ForceOperation = $true } if($OperationType -eq "ExtractOnly") { $whatifMsg = "Extract the resources from $BackupDirectoryOrZipFile" } elseif($OperationType -eq $script:OperationTypes["Put"]) { $whatifMsg = "Put all resources from $BackupDirectoryOrZipFile\Jsons" } else { $whatifMsg = "Restore networkcontroller resources from $BackupDirectoryOrZipFile." if(($BackupType -eq $script:BackupTypes["Manual"]) -and ([String]::IsNullOrEmpty($AesKey) -eq $true)) { $whatifMsg += "`r`nAesKey is not provided. Credential resources and the virtual gateway connection credentials should be manually readded after the restore." } $whatifMsg += "`r`nThis operation will reset the existing NetworkController database and all the current data will be erased" } if($GetAesKey.IsPresent) { Get-AesKey } elseif ($PSCmdlet.ShouldProcess($whatifMsg)) { try { Initialize if($OperationType -eq "ExtractOnly" -or $OperationType -eq "ExtractAndReplay") { Cleanup PrepareBackup if($OperationType -eq "ExtractOnly") { $script:ResultMessages += "Extracted JSONs to $script:BackupDirectory" } } if($OperationType -eq "ReplayOnly" -or $OperationType -eq "ExtractAndReplay") { ValidatePostExtractMultisiteConditions PerformPreRestore StartRestore $script:ResultMessages += "Restored NetworkController from $script:BackupDirectory" } if($OperationType -eq $script:OperationTypes["Put"]) { ValidatePostExtractMultisiteConditions StartRestore $script:ResultMessages += "Put all resources from $script:BackupDirectory" } } catch { $script:ErrorMessages += "Restore failed: $_" } finally { if($script:Initialzed -eq $true -and ($OperationType -eq "ReplayOnly" -or $OperationType -eq "ExtractAndReplay")) { PerformPostRestore } if ($script:Initialzed -and ($OperationType -eq "ExtractAndReplay" -or $OperationType -eq "ExtractOnly")) { Write-Log "Cleaning up $script:ImosDirectory" Remove-Item $script:ImosDirectory -ErrorAction Stop -Recurse -Force } Write-Host "=======Result Summary=======" foreach($msg in $script:WarningMessages) { Write-Host -ForegroundColor Yellow $msg } foreach($msg in $script:ErrorMessages) { Write-Host -ForegroundColor Red $msg } foreach($msg in $script:ResultMessages) { Write-Host -ForegroundColor Green $msg } Write-Host "=======Result Summary=======" } } # SIG # Begin signature block # MIIoLQYJKoZIhvcNAQcCoIIoHjCCKBoCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCJg6GC3lSbHN0T # 8cxCKnXFWjtwXcNuGQ65/tMZY59xN6CCDXYwggX0MIID3KADAgECAhMzAAADrzBA # 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 # MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIMavr6NXWOzJEsPwTXzZX07E # /j2Qr0yuBg97B8XJflmGMEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A # cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB # BQAEggEAGKdTCMVcmkw89+iJ4mQVv/tsZEPAy0ssMJEqEL6b0MWPVgoW1CUCG6nm # L6B2DzQFuPYhaTHPR/uSRlHtZbExQYzBlllwjoUx6FPMnoUNm4ao8ld+MH3Kk0Ci # 9CH32UQVy9+nO6vSc9fMYvGOxExUZ3yZWN2s2soMzPnFU3E8gBaK6Qv1R7C9FMT9 # ny8oAwNP14PliFK1OWTo+8RIE3u9vJNvwL4TFfbwpDLQE7kEGl8KDy14GEbuu9Kp # KrVITIieOeI3ztnI+VVEbIKCUi2R7cSJWAeugdux4fQbjZnHW3JO0NiLLw97Q4oh # HgZFQ11nesAEJucPy/bEh2AYdU0BFqGCF5cwgheTBgorBgEEAYI3AwMBMYIXgzCC # F38GCSqGSIb3DQEHAqCCF3AwghdsAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFSBgsq # hkiG9w0BCRABBKCCAUEEggE9MIIBOQIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl # AwQCAQUABCCNFVjZ8KDaxfdZ1qzUOOrnc2X5pImkmI46v1khPogOggIGZmsMoVV7 # GBMyMDI0MDYyNDIyMjMyNS40OTJaMASAAgH0oIHRpIHOMIHLMQswCQYDVQQGEwJV # UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE # ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1l # cmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046N0YwMC0w # NUUwLUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2Wg # ghHtMIIHIDCCBQigAwIBAgITMwAAAfAqfB1ZO+YfrQABAAAB8DANBgkqhkiG9w0B # AQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE # BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYD # VQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAeFw0yMzEyMDYxODQ1 # NTFaFw0yNTAzMDUxODQ1NTFaMIHLMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz # aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENv # cnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25z # MScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046N0YwMC0wNUUwLUQ5NDcxJTAjBgNV # BAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggIiMA0GCSqGSIb3DQEB # AQUAA4ICDwAwggIKAoICAQC1Hi1Tozh3O0czE8xfRnrymlJNCaGWommPy0eINf+4 # EJr7rf8tSzlgE8Il4Zj48T5fTTOAh6nITRf2lK7+upcnZ/xg0AKoDYpBQOWrL9Ob # FShylIHfr/DQ4PsRX8GRtInuJsMkwSg63bfB4Q2UikMEP/CtZHi8xW5XtAKp95cs # 3mvUCMvIAA83Jr/UyADACJXVU4maYisczUz7J111eD1KrG9mQ+ITgnRR/X2xTDMC # z+io8ZZFHGwEZg+c3vmPp87m4OqOKWyhcqMUupPveO/gQC9Rv4szLNGDaoePeK6I # U0JqcGjXqxbcEoS/s1hCgPd7Ux6YWeWrUXaxbb+JosgOazUgUGs1aqpnLjz0YKfU # qn8i5TbmR1dqElR4QA+OZfeVhpTonrM4sE/MlJ1JLpR2FwAIHUeMfotXNQiytYfR # BUOJHFeJYEflZgVk0Xx/4kZBdzgFQPOWfVd2NozXlC2epGtUjaluA2osOvQHZzGO # oKTvWUPX99MssGObO0xJHd0DygP/JAVp+bRGJqa2u7AqLm2+tAT26yI5veccDmNZ # sg3vDh1HcpCJa9QpRW/MD3a+AF2ygV1sRnGVUVG3VODX3BhGT8TMU/GiUy3h7ClX # OxmZ+weCuIOzCkTDbK5OlAS8qSPpgp+XGlOLEPaM31Mgf6YTppAaeP0ophx345oh # twIDAQABo4IBSTCCAUUwHQYDVR0OBBYEFNCCsqdXRy/MmjZGVTAvx7YFWpslMB8G # A1UdIwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8GA1UdHwRYMFYwVKBSoFCG # Tmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUy # MFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBsBggrBgEFBQcBAQRgMF4w # XAYIKwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2Vy # dHMvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3J0MAwG # A1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwDgYDVR0PAQH/BAQD # AgeAMA0GCSqGSIb3DQEBCwUAA4ICAQA4IvSbnr4jEPgo5W4xj3/+0dCGwsz863QG # Z2mB9Z4SwtGGLMvwfsRUs3NIlPD/LsWAxdVYHklAzwLTwQ5M+PRdy92DGftyEOGM # Hfut7Gq8L3RUcvrvr0AL/NNtfEpbAEkCFzseextY5s3hzj3rX2wvoBZm2ythwcLe # ZmMgHQCmjZp/20fHWJgrjPYjse6RDJtUTlvUsjr+878/t+vrQEIqlmebCeEi+VQV # xc7wF0LuMTw/gCWdcqHoqL52JotxKzY8jZSQ7ccNHhC4eHGFRpaKeiSQ0GXtlbGI # bP4kW1O3JzlKjfwG62NCSvfmM1iPD90XYiFm7/8mgR16AmqefDsfjBCWwf3qheIM # fgZzWqeEz8laFmM8DdkXjuOCQE/2L0TxhrjUtdMkATfXdZjYRlscBDyr8zGMlprF # C7LcxqCXlhxhtd2CM+mpcTc8RB2D3Eor0UdoP36Q9r4XWCVV/2Kn0AXtvWxvIfyO # Fm5aLl0eEzkhfv/XmUlBeOCElS7jdddWpBlQjJuHHUHjOVGXlrJT7X4hicF1o23x # 5U+j7qPKBceryP2/1oxfmHc6uBXlXBKukV/QCZBVAiBMYJhnktakWHpo9uIeSnYT # 6Qx7wf2RauYHIER8SLRmblMzPOs+JHQzrvh7xStx310LOp+0DaOXs8xjZvhpn+Wu # Zij5RmZijDCCB3EwggVZoAMCAQICEzMAAAAVxedrngKbSZkAAAAAABUwDQYJKoZI # 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 # MCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOjdGMDAtMDVFMC1EOTQ3MSUwIwYDVQQD # ExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiMKAQEwBwYFKw4DAhoDFQDC # KAZKKv5lsdC2yoMGKYiQy79p/6CBgzCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w # IFBDQSAyMDEwMA0GCSqGSIb3DQEBCwUAAgUA6iQKujAiGA8yMDI0MDYyNDE1MDk0 # NloYDzIwMjQwNjI1MTUwOTQ2WjB3MD0GCisGAQQBhFkKBAExLzAtMAoCBQDqJAq6 # AgEAMAoCAQACAhg5AgH/MAcCAQACAhO/MAoCBQDqJVw6AgEAMDYGCisGAQQBhFkK # BAIxKDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAIAgEAAgMBhqAwDQYJ # KoZIhvcNAQELBQADggEBAAQhaNFST+wXTLA0B2/bOdBZGzv2YtZcWydb3gH+vcuU # mPXRSykU6wR1C/KfeWEaSEv+0i9QQR4wND3Avi/tvaGTRdnrDtILv8dWHK+WqlXL # MvLQ3hpSlEfDxroBTzLAN9B5iu/qSgBfPVi0lRKTr9phAWY0Go2Wvr9a9NwawaQD # IBnHpUP34EtSMFhwrV384fZfZazdjgT2keXe8Q+29YB2qwbLjL+b16Nf7anVU+6I # sjhMMoz7qbQmwFsbkVKjVX+R5GRZ/jOriKzkGbjQd9d5M2XbBNrDHL13Bxoj7CwP # bNQvdOQrNQCyBZlhGH4DVPKqDdWj5JhpyOothyl0/6kxggQNMIIECQIBATCBkzB8 # MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVk # bW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1N # aWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAfAqfB1ZO+YfrQABAAAB # 8DANBglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEE # MC8GCSqGSIb3DQEJBDEiBCDoX3sULGpUf54UmtIfAcSZU5OXuTbL8i5NHFKZpVJC # CzCB+gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EIFwBmqOlcv3kU7mAB5sWR74Q # FAiS6mb+CM6asnFAZUuLMIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgT # Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29m # dCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENB # IDIwMTACEzMAAAHwKnwdWTvmH60AAQAAAfAwIgQgUh/AaUHSlA4ukw+aWAieA9LT # uI9k7DYdlXrRzFnz1kkwDQYJKoZIhvcNAQELBQAEggIAaze4t7gUkz/fUamuKe2R # qjJAvnlPt7ad5fGa3ubfRL2K/4GGw9Cj7XsAEHPcd/Ez11s4tdX9kqkyygHNNujQ # aa7GuNac2q70otZz9MTHuDZDkRp0BGr6RPpYF7WuO/bwOr9rCHpHbnkt917bobmj # XLFd0VwiBfakz8zkU0DvAZOFFjtKKBlLOjbfyytQurZPJRov7QupUSvure74at2u # qTIg4YtSGh7JS4M2ivssyJCrZpIVy8khWA2t6cTrOvuaWuKvRVrH+KpOUnw8b8Oh # 4M0fxZkW0srtdsZ90o3iHhBvdRil/A7rYE9+XkMQA9Kfiksil0E66OHVbSCp5hJY # zq4eOtGLp8oXLGZdTxbTMTLpdoUY/gscyt470NzmZQVAqfYe0pcap/3GBsLL9Lau # Czlsu9lBsaYOlNd8fem1ngDwzwG/GfDwnH+TjTMg08LlOu/Yqd954X9boSjYhCEz # OTsFUO4tjrM7GGnbeC2dfC41g0uaXm2x8L1Vxwh8BT7tgoKKVqEQFjz9RTKRJ+gg # RP43mMchMZ9ta8CYOfFJUtMhMMUrzMtmTo2R1fIGBFy8CwLOtvktYPxQcKPjPmxD # jal9xIKmDgOjt19f+C7qgiFx7HEWz+qgGDedQDib5pfcfOcJm0R7VbcZq5d6cXlE # BCSrSLXfEy5f+dqr1XYsnoo= # SIG # End signature block |